Git Product home page Git Product logo

heckarnews's Introduction

HeckarNews

Hacker News Clone. Demo: https://forum.krehwell.com/

image of frontpage heckarnews

Dependecies

Run & Installation

  • cd to website/ and rest-api/
  • npm run dev on both dir to run on localhost

Website served at :3000 and Server served at :5000

Project Structure

HeckarNews
  |- website 
    |- ... # front-end code
  |- rest-api 
    |- ... # back-end code

Website

Next.Js is the main actor for building the UI. The front-end project structure:

website 
  |-  api             # all code request from back-end
  |-  components      # Header, Footer, etc.
  |-  pages           # routes
  |-  utils           # helper functions. apiBaseUrl.js, etc.
  |-  styles          # .css files
  |-  .env.local      # api key for Algolia
  |-  next.config.js  # .env for next app

This directory has .env.local which must be set up for Algolia API key:

# .env.local
ALGOLIA_APP_ID=...
ALGOLIA_PUBLIC_API_KEY=...

Front-end Flow

Each page which needs for authentication or request is done by simply calling any related function from ./api/.
Ex. getting item on front page:

// page/index.js
import getRankedItemsByPage from "../api/items/getRankedItemsByPage.js"; /* GET ITEM API */ 

export default function Index({ items, authUserData, ... }) {
    ...
}

export async function getServerSideProps({ req, query }) {
    const page = 1;
    const apiResult = await getRankedItemsByPage(page, req);
    
    return {
        props: {
            items: (apiResult && apiResult.items) || [],
            authUserData: apiResult && apiResult.authUser ? apiResult.authUser : {},
            ...
        },
    };
}

Each function on ./api/ is already map with ./utils/apiBaseUrl.js. So, no need to bother about API URL code when running on development or production. Just change the url on ./next.config.js for each development environment.

Page Styles

All styles is using pure CSS which can be found at ./styles/. The directory structure for styles is more or less map to be similar like ./pages/ for ease of use while the importing of all styles is done in ./pages/_app.js.

Rest-API

Express is the main actor for server. The back-end project structure:

rest-api 
  |-  middlewares  # user authentication  
  |-  models       # MongoDb models
  |-  routes       # endpoint for each request
  |-  config.js    # configuration for particular response
  |-  index.js     # server initialization

This directory has .env for MongoDb auth (URI is defined in ./index.js), Mailgun, and Algolia API key:

# .env
# MONGODB ID/PASS
DB_USERNAME=...
DB_PASSWORD=...

# MAILGUN API KEY
MAILGUN_API_KEY=...

# ALGOLIA SEARCH API KEY
ALGOLIA_APP_ID=...
ALGOLIA_PRIVATE_API_KEY=...

Back-end Flow

Each routes in ./routes/ is defined per-functionality use case per-route. Folder Structure for ./routes/:

rest-api 
  |-  routes
    |-  comments
    |-  emails
    |-  items
    |-  moderation
    |-  search
    |-  users
    |-  utils.js  # helper functions which mostly used by api.js. generateUniqueId, isValidDate, etc.
  |- ...

In each of it, there is api.js and index.js which work by each request type in index.js (GET, POST, etc.) will call API functions from api.js for it to process between Db and return result back to index.js to be a response later. (./routes/search/ only has api.js since it does not have any route definition)

Defined Endpoint (routes/[slug]/index.js)

The purpose of index.js is for routes definition, accept request, call api.js to process, and return response to client.
Take an example of ./routes/user/index.js for login workflow:

// routes/user/index.js
app.put("/users/login", async (req, res) => {
    try {
        if (!req.body.username || !req.body.password) {
            throw { submitError: true };
        }
        
        // Ask for validation either user exist or not from api.js
        const response = await api.loginUser(
            req.body.username,
            req.body.password
        );
        
        ...
        res.cookie(...);
        
        // if everything goes fine then just response 
        res.json({ success: true });
    } catch (error) {
        if (!(error instanceof Error)) {
            // catch any known error thrown, such: credentialError, bannedError.
            res.json(error);
        } else {
            // any unknown error will be responsed as submitError.
            res.json({ submitError: true });
        }
    }
});

There is no callback in server code. All of them are using async/await style. By using so, it is easier for server to catch any error and simply response to front-end with a reasonable message instead of returning the error itself. In the client side, it just has to check for this message for validation, ex:

  • Res: { success: true } ➞ user is logged in,
  • Res: { credentialError: true } ➞ username is not registered, or.
  • Res: { bannedError: true } ➞ user has been banned, etc.
API (routes/[slug]/api.js)

API is solely for the bridge between Db and Server. All actions containing relation between them will be processed here. API will call DB and do checkers/validation within it. If everything is fine it will return object while if something is bad it will throw object. The error thrown will be catch in index.js to be responsed later for client.
Take an example of ./routes/user/api.js for login API:

// routes/user/api.js
module.exports = {
    loginUser: async (username, password) => {
        const user = await UserModel.findOne({ username }).exec();

        // if user not exist, throw credential error
        if (!user) {
            throw { credentialError: true };
        }

        // if pass is not correct, throw credential error
        const passwordIsMatch = await user.comparePassword(password);
        if (!passwordIsMatch) {
            throw { credentialError: true };
        }
        
        // if user is banned, throw banned error
        if (user.banned) {
            throw { bannedError: true };
        }

        // if everything is going fine, return 
        return {
            success: true,
            username: user.username,
            ...
        };
    },
}

Models

All models are basic MongoDb model. Except for user, it has a special middleware UserSchema.pre and extension method UserSchema.methods.comparePassword. Middleware used for user is .pre("save", ...) action, it will hash the password using bcrypt before actually saving it to cloud.

Credit

Thanks to internet. This project can be freely used without needing to be wised.
This repository is under GPL (Giant Penis License).

Initially about to make a porn forum but turned out to be this instead.

heckarnews's People

Contributors

krehwell avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

heckarnews's Issues

`undefined` cannot be serialized as JSON.

my website:
http://207.246.109.130:3000
ubuntu 22.04
[email protected]
[email protected]

http://207.246.109.130:3000/search?q=test
(Algolia is configured, and the data can also be seen in the background)

http://207.246.109.130:3000/user?idaaa=test

Server Error:
Error: Error serializing .totalNumOfHits returned from getServerSideProps in "/search".
Reason: undefined cannot be serialized as JSON. Please use null or omit this value.

Call Stack
isSerializable
file:///root/HeckarNews/website/node_modules/next/dist/lib/is-serializable-props.js (7:95)

file:///root/HeckarNews/website/node_modules/next/dist/lib/is-serializable-props.js (7:490)
Array.every

isSerializable
file:///root/HeckarNews/website/node_modules/next/dist/lib/is-serializable-props.js (7:291)
isSerializableProps
file:///root/HeckarNews/website/node_modules/next/dist/lib/is-serializable-props.js (9:219)
renderToHTML
file:///root/HeckarNews/website/node_modules/next/dist/next-server/server/render.js (42:1318)
processTicksAndRejections
node:internal/process/task_queues (96:5)
async
file:///root/HeckarNews/website/node_modules/next/dist/next-server/server/next-server.js (112:97)
async
file:///root/HeckarNews/website/node_modules/next/dist/next-server/server/next-server.js (105:142)
async DevServer.renderToHTMLWithComponents
file:///root/HeckarNews/website/node_modules/next/dist/next-server/server/next-server.js (137:387)
async DevServer.renderToHTML
file:///root/HeckarNews/website/node_modules/next/dist/next-server/server/next-server.js (138:610)
async DevServer.renderToHTML
file:///root/HeckarNews/website/node_modules/next/dist/server/next-dev-server.js (36:578)
async DevServer.render
file:///root/HeckarNews/website/node_modules/next/dist/next-server/server/next-server.js (75:160)
async Object.fn
file:///root/HeckarNews/website/node_modules/next/dist/next-server/server/next-server.js (58:672)
async Router.execute
file:///root/HeckarNews/website/node_modules/next/dist/next-server/server/router.js (25:67)
async DevServer.run
file:///root/HeckarNews/website/node_modules/next/dist/next-server/server/next-server.js (68:1042)

your website will be fine:
https://forum.krehwell.com/search?q=test
https://forum.krehwell.com/user?idaaa=yakuza

How to solve it?
Also, how can I prevent error messages from appearing on the browser side?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.