diff options
Diffstat (limited to 'backend/src')
| -rw-r--r-- | backend/src/db.js | 4 | ||||
| -rw-r--r-- | backend/src/index.js | 22 | ||||
| -rw-r--r-- | backend/src/routes.js | 133 |
3 files changed, 159 insertions, 0 deletions
diff --git a/backend/src/db.js b/backend/src/db.js new file mode 100644 index 0000000..2573239 --- /dev/null +++ b/backend/src/db.js @@ -0,0 +1,4 @@ +const config = require('../knexfile')[process.env.NODE_ENV || 'development']; +const knex = require('knex')(config); + +module.exports = knex; diff --git a/backend/src/index.js b/backend/src/index.js new file mode 100644 index 0000000..128ed60 --- /dev/null +++ b/backend/src/index.js @@ -0,0 +1,22 @@ +const express = require('express'); +const bodyParser = require('body-parser'); +const cookieParser = require('cookie-parser'); +const morgan = require('morgan'); +const routes = require('./routes'); + +const app = express(); + +app.use(morgan('dev')); +app.use(bodyParser.json()); +app.use(cookieParser()); +app.use((req, res, next) => { + res.set('Access-Control-Allow-Origin', 'http://localhost:3000'); + res.set('Access-Control-Allow-Headers', 'Content-Type'); + res.set('Access-Control-Allow-Credentials', true); + next(); +}); +app.use(routes.normalRouter); +app.use(routes.authedRouter); + +const port = 4000; +app.listen(port, () => console.log(`App listening on http://localhost:${port}!`)); diff --git a/backend/src/routes.js b/backend/src/routes.js new file mode 100644 index 0000000..a762d3a --- /dev/null +++ b/backend/src/routes.js @@ -0,0 +1,133 @@ +const express = require('express'); +const sha512 = require('js-sha512'); +const shortid = require('shortid'); + +const salt = 'suola'; + +const db = require('./db'); + +const buildSessionCookie = ({ sessionId, isLogout, remember }) => { + if (!isLogout) { + if (remember) { + return `sessionId=${sessionId}; Domain=localhost; HttpOnly; SameSite=strict;`; + } else { + return `sessionId=${sessionId}; Domain=localhost; HttpOnly; SameSite=strict; Max-Age=86400;`; + } + } else { + return `sessionId=deleted; Domain=localhost; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; SameSite=strict;`; + } +}; + +const normalRouter = express.Router(); +const authedRouter = express.Router(); + +authedRouter.use(async (req, res, next) => { + const cookies = req.cookies; + const { sessionId } = cookies; + + if (!sessionId) { + res.status(403); + return res.json({ + message: 'Log in first!', + }); + } + + const token = await db('tokens').where('value', sessionId).first(); + + if (!token) { + res.status(400); + res.set('Set-Cookie', buildSessionCookie({ isLogout: true })); + return res.json({ + message: 'Invalid session token.', + sessionId, + }); + } + + const user = await db('users').where({ id: token.user_id }).first(); + + req.sessionId = sessionId; + req.user = user; + + next(); +}); + +normalRouter.get('/', (req, res) => { + res.json({ + message: 'Backend API', + }); +}); + +normalRouter.post('/register', async (req, res) => { + const { username, password } = req.body; + if (!username || !password) { + res.status(400); + return res.json({ + error: 'Username or password missing.', + }); + } + + const existingUuser = await db('users').where({ username }).first(); + if (existingUuser) { + res.status(400); + return res.json({ + error: 'Username already taken.', + }); + } + + const pwHash = sha512(salt + password); + const sessionId = shortid.generate(); + const user = (await db('users').insert({ username, password: pwHash }).returning('*'))[0]; + await db('tokens').insert({ user_id: user.id, type: 'session', value: sessionId }); + + res.set('Set-Cookie', buildSessionCookie({ sessionId, remember: false, isLogout: false })); + return res.json({ + username, + }); +}); + +normalRouter.post('/login', async (req, res) => { + const { username, password, remember } = req.body; + if (!username || !password) { + res.status(400); + return res.json({ + error: 'Username or password missing.', + }); + } + + const user = await db('users').where({ username }).first(); + + if (user) { + const sessionId = shortid.generate(); + await db('tokens').insert({ user_id: user.id, type: 'session', value: sessionId }); + + res.set('Set-Cookie', buildSessionCookie({ sessionId, remember, isLogout: false })); + + return res.json({ + username, + }); + } + + res.status(403); + res.json({ + error: 'Wrong username or password', + }); +}); + +authedRouter.post('/logout', async (req, res) => { + if (req.user && req.sessionId) { + await db('tokens').where({ value: req.sessionId }).delete(); + } + res.status(204); + res.set('Set-Cookie', buildSessionCookie({ isLogout: true })); + res.send(); +}); + +authedRouter.get('/user', (req, res) => { + const user = req.user; + return res.json({ + message: `Hello user ${user.username}!`, + username: user.username, + }); +}); + +module.exports = { authedRouter, normalRouter }; |
