From 15f2b32511e9b4c0479ad03c18a69653328f36b1 Mon Sep 17 00:00:00 2001 From: Tyler Murphy Date: Mon, 23 Jan 2023 21:40:41 -0500 Subject: [PATCH] i changed a lot of shit --- index.js | 195 ++++++++++++++++++------------- package-lock.json | 61 +++++----- package.json | 5 +- public/css/home.css | 11 -- public/css/main.css | 11 ++ public/js/api.js | 10 +- public/js/home.js | 25 ++-- public/js/people.js | 26 ++++- src/api.js | 238 ------------------------------------- src/api/auth.js | 72 ++++++++++++ src/api/pages.js | 77 ++++++++++++ src/api/posts.js | 81 +++++++++++++ src/api/users.js | 35 ++++++ src/cache.js | 277 ++++++++++++++++++++++++++++++++++++++++++++ src/check.js | 84 ++++++++++++++ src/console.js | 200 +++++++++++++++++++++----------- src/database.js | 223 ++++++++++++++++++----------------- 17 files changed, 1068 insertions(+), 563 deletions(-) delete mode 100644 src/api.js create mode 100644 src/api/auth.js create mode 100644 src/api/pages.js create mode 100644 src/api/posts.js create mode 100644 src/api/users.js create mode 100644 src/cache.js create mode 100644 src/check.js diff --git a/index.js b/index.js index ab05c60..b8f79f9 100644 --- a/index.js +++ b/index.js @@ -1,96 +1,135 @@ const express = require('express') -const cookie = require('cookie-parser') const app = express() -const port = 8080 +const cache = require('./src/cache') +const con = require('./src/console') + + +const auth = require('./src/api/auth') +const pages = require('./src/api/pages') +const posts = require('./src/api/posts') +const users = require('./src/api/users') + app.set('trust proxy', true) -app.use(cookie()) -app.use(express.json()); app.use(express.static('public')) +app.use(require('cookie-parser')()) +app.use(express.json()); -const database = require('./src/database.js') -const con = require('./src/console.js') -const api = require('./src/api.js') app.use((req, res, next) => { - var ip = req.headers['x-real-ip'] || req.connection.remoteAddress; - if (req.path !== '/console') { - const public = { ... req.body } - if (public.password !== undefined) { - public.password = '********' + + var ip = req.headers['x-real-ip'] || req.socket.remoteAddress; + + if (req.path !== '/console') { + + let body = { ...req.body } + + if (body.password !== undefined) { + body.password = '********' + } + + con.log( + ip, + req.method, + req.path, + body + ) + } - console.log(ip, req.method, req.path, public) - con.requests.push({ip: ip, method: req.method, path: req.path, body: public}) - } - next() + + next() }) -app.get('/', (req, res) => { - const cookies = req.cookies; - if (cookies === undefined || cookies.auth === undefined) { - res.redirect('/login') - return - } - const user = database.auth(req.cookies.auth) - if (user === undefined) { - res.redirect('/login') - return - } - res.redirect('/home') -}) - -app.get('/login', (req, res) => { - const cookies = req.cookies; - if (cookies === undefined || cookies.auth === undefined) { - res.sendFile('login.html', { root: './public' }) - return - } - const user = database.auth(req.cookies.auth) - if (user === undefined) { - res.sendFile('login.html', { root: './public' }) - return - } - res.redirect('/home') -}) - -app.get('/home', (req, res) => { - const cookies = req.cookies; - if (cookies === undefined || cookies.auth === undefined) { - res.redirect('/login') - return - } - const user = database.auth(req.cookies.auth) - if (user === undefined) { - res.redirect('/login') - return - } - res.sendFile('home.html', { root: './public' }) -}) - -app.get('/people', (req, res) => { - res.sendFile('people.html', { root: './public' }) -}) - -app.get('/profile', (req, res) => { - res.sendFile('profile.html', { root: './public' }) -}) - -app.use('/api', api); -app.use('/console', con.router); app.use((req, res, next) => { - res.status(404).sendFile('404.html', { root: './public' }) + + if (req.path.startsWith('/api/auth')) { + next() + return + } + + const cookies = req.cookies + + if (cookies === undefined || cookies.auth === undefined) { + + if (req.method !== 'GET' && req.path.startsWith('/api')) { + res.status(401).send({msg: 'Unauthorized'}) + return + } + + next() + return + } + + const user = cache.auth(req.cookies.auth) + + if (user !== undefined) { + + res.locals.user = user + + } else if (req.method !== 'GET' && req.path.startsWith('/api')) { + + res.status(401).send({msg: 'Unauthorized'}) + return + + } + + next() + }) + +app.use('/api/auth', auth) +app.use('/api/posts', posts) +app.use('/api/users', users) +app.use('/', pages) + + +app.get('/console', (req, res) => { + res.send(con.render()) +}) + + +app.use((req, res, next) => { + res.status(404).sendFile('404.html', { root: './public' }) +}) + + app.use((err, req, res, next) => { - if (err instanceof SyntaxError && err.status === 400 && 'body' in err) { - res.status(400).send({msg: 'Invalid json body'}) - return - } - console.error(err) - res.status(500).send({msg: 'Internal server error'}) + + if (err instanceof SyntaxError && err.status === 400 && 'body' in err) { + res.status(400).send({ msg: 'Invalid json body' }) + return + } + + console.error(err) + res.status(500).send({ msg: 'Internal server error' }) }) -app.listen(port, () => { - console.log(`App listening on port http://127.0.0.1:${port}`) -}) \ No newline at end of file + +const cron = require('node-cron').schedule('*/5 * * * *', () => { + con.msg('Writing cache to database') + cache.dump() +}) + + +const port = 8080 +const server = app.listen(port, () => { + console.log(`App listening on port http://127.0.0.1:${port}`) +}) + + +const close = () => { + console.log('Writing cache to database') + cache.dump() + console.log('Stopping cron jobs') + cron.stop() + server.close(() => { + console.log('HTTP server closed') + }) +} + + +process.on('SIGINT', close) +process.on('SIGTERM', close) +process.on('SIGQUIT', close) \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index bda8386..079a4ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,10 +12,9 @@ "better-sqlite3": "^8.0.1", "cheerio": "^1.0.0-rc.12", "cookie-parser": "^1.4.6", - "crypto": "^1.0.1", - "dotenv": "^16.0.3", "express": "^4.18.2", - "express-rate-limit": "^6.7.0" + "express-rate-limit": "^6.7.0", + "node-cron": "^3.0.2" } }, "node_modules/accepts": { @@ -238,12 +237,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, - "node_modules/crypto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", - "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", - "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." - }, "node_modules/css-select": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", @@ -376,14 +369,6 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", - "engines": { - "node": ">=12" - } - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -781,6 +766,17 @@ "node": ">=10" } }, + "node_modules/node-cron": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.2.tgz", + "integrity": "sha512-iP8l0yGlNpE0e6q1o185yOApANRe47UPbLf4YxfbiNHt/RU5eBcGB/e0oudruheSf+LQeDMezqC5BVAb5wwRcQ==", + "dependencies": { + "uuid": "8.3.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -1207,6 +1203,14 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -1384,11 +1388,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, - "crypto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", - "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==" - }, "css-select": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", @@ -1475,11 +1474,6 @@ "domhandler": "^5.0.1" } }, - "dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1766,6 +1760,14 @@ "semver": "^7.3.5" } }, + "node-cron": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.2.tgz", + "integrity": "sha512-iP8l0yGlNpE0e6q1o185yOApANRe47UPbLf4YxfbiNHt/RU5eBcGB/e0oudruheSf+LQeDMezqC5BVAb5wwRcQ==", + "requires": { + "uuid": "8.3.2" + } + }, "nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -2068,6 +2070,11 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 8eeab44..799764b 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,8 @@ "better-sqlite3": "^8.0.1", "cheerio": "^1.0.0-rc.12", "cookie-parser": "^1.4.6", - "crypto": "^1.0.1", - "dotenv": "^16.0.3", "express": "^4.18.2", - "express-rate-limit": "^6.7.0" + "express-rate-limit": "^6.7.0", + "node-cron": "^3.0.2" } } diff --git a/public/css/home.css b/public/css/home.css index f05466a..33d72c0 100644 --- a/public/css/home.css +++ b/public/css/home.css @@ -179,15 +179,4 @@ body { width: calc(100% - 20px); background-color: #f0f2f5; font-family: sfpro; -} - -#load { - width: 100%; - display: flex; - justify-content: center; - padding-bottom: 20px; -} - -#load a:hover { - border-bottom: #606770 1px solid; } \ No newline at end of file diff --git a/public/css/main.css b/public/css/main.css index a75a941..c1b4fa2 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -293,4 +293,15 @@ form { border-radius: 10px; margin-left: 10px; width: 100%; +} + +#load { + width: 100%; + display: flex; + justify-content: center; + padding-bottom: 20px; +} + +#load a:hover { + border-bottom: #606770 1px solid; } \ No newline at end of file diff --git a/public/js/api.js b/public/js/api.js index 9b5a4dd..f0879c9 100644 --- a/public/js/api.js +++ b/public/js/api.js @@ -20,8 +20,8 @@ const login = async (email, password) => { return await request('/auth/login', {email, password}) } -const register = async (first, last, email, password, gender, month, day, year) => { - return await request('/auth/register', {first, last, email, password, gender, month, day, year}) +const register = async (firstname, lastname, email, password, gender, month, day, year) => { + return await request('/auth/register', {firstname, lastname, email, password, gender, month, day, year}) } const loadposts = async (page) => { @@ -36,12 +36,12 @@ const loadusers = async (ids) => { return await request('/users/load', {ids}) } -const loadallusers = async () => { - return await request('/users/all', {}) +const loaduserspage = async (page) => { + return await request('/users/page', {page}) } const loadself = async () => { - return await request("/auth/self", {}) + return await request("/users/self", {}) } const postcomment = async (id, content) => { diff --git a/public/js/home.js b/public/js/home.js index f3711ad..9ccd610 100644 --- a/public/js/home.js +++ b/public/js/home.js @@ -44,13 +44,13 @@ function parsePost(post) { ${post.content.replace(/\n/g,'
')}

- ${post.likes.length} Likes + ${Object.keys(post.likes).length} Likes
- - Like + + Like @@ -87,15 +87,10 @@ function getPost(id) { async function like(span) { const id = parseInt(span.parentElement.parentElement.getAttribute('postid')) const post = data.posts[getPost(id)] - const index = post.likes.indexOf(data.user.id) - const state = index === -1; - const response = await postlike(id, state) + const current = post.likes[data.user.id] + const response = await postlike(id, !current) if (response.status != 200) return; - if (index == -1) { - post.likes.push(data.user.id) - } else { - post.likes.splice(index, 1) - } + post.likes[data.user.id] = !current render() } @@ -127,7 +122,7 @@ async function post() { } error.innerHTML = ''; data.posts.unshift({ - id: data.posts[0].id + 1, + id: response.msg, user: data.user.id, date: Date.now(), content: text, @@ -182,7 +177,7 @@ function render() { const load = ` ` @@ -203,9 +198,9 @@ const data = { async function load() { const posts = (await loadposts(page)).json if (posts.length === 0) { - page = -1; + page = -1 } else { - page++; + page++ } data.posts.push(... posts) const batch = [] diff --git a/public/js/people.js b/public/js/people.js index 415dd16..4351315 100644 --- a/public/js/people.js +++ b/public/js/people.js @@ -16,6 +16,7 @@ function parseUser(user) { Joined ${parseDate(new Date(user.date))} Gender: ${user.gender} Birthday: ${user.month + ' ' + user.day + ', ' + user.year} + User ID: ${user.id}
` @@ -29,17 +30,34 @@ function render() { ` - add(html, 'usres') + add(html, 'users') + + const load = ` +
+ Load more users +
+ ` + + if (page !== -1) { + add(load, 'load') + } else { + remove('load') + } } +var page = 0 var data = { users: [] } async function load() { - const users = (await loadallusers()).json - console.log(users) - data.users = users + const users = (await loaduserspage(page)).json + if (users.length === 0) { + page = -1 + } else { + page++ + } + data.users.push(... users) render() } diff --git a/src/api.js b/src/api.js deleted file mode 100644 index 2c99430..0000000 --- a/src/api.js +++ /dev/null @@ -1,238 +0,0 @@ -const express = require('express') -const router = express.Router() -const database = require('./database.js') -const cheerio = require('cheerio'); - -const check = (test, type) => { - return text === undefined || text === null || typeof test !== type -} - -const parseText = (test) => { - if (typeof test !== 'string') return undefined; - const $ = cheerio.load(test) - return $("body").html() -} - -const text = (test, min, max) => { - return check(test, 'string') || test.length > max || test.length < min -} - -router.get('/', (req, res) => { - res.status(200).send( {msg: 'xssbook api'} ) -}) - -router.post('/auth/register', (req, res) => { - const first = parseText(req.body.first); - if (text(first, 1, 20)) { - res.status(400).send( {msg: 'Invalid first name'} ); return; - } - const last = parseText(req.body.last); - if (text(last, 1, 20)) { - res.status(400).send( {msg: 'Invalid last name'} ); return; - } - const email = parseText(req.body.email); - if (text(email, 1, 50)) { - res.status(400).send( {msg: 'Invalid email'} ); return; - } - const password = req.body.password; - if (text(password, 1, 50)) { - res.status(400).send( {msg: 'Invalid password'} ); return; - } - const gender = parseText(req.body.gender); - if (text(gender, 1, 100)) { - res.status(400).send( {msg: 'Invalid gender'} ); return; - } - const month = parseText(req.body.month); - if (text(month, 1, 10)) { - res.status(400).send( {msg: 'Invalid month'} ); return; - } - const day = req.body.day; - if (check(day, 'number')) { - res.status(400).send( {msg: 'Invalid day'} ); return; - } - const year = req.body.year; - if (check(year, 'number')) { - res.status(400).send( {msg: 'Invalid year'} ); return; - } - let exists = database.getUserByEmail(email); - if (exists !== undefined) { - res.status(400).send( {msg: 'Email is already in use'} ); return; - } - exists = database.getUserByPassword(password); - if (exists !== undefined) { - res.status(400).send( {msg: `Password is already in use by ${exists.email}`} ); return; - } - const key = database.register(first, last, email, password, gender, month, day, year); - if (key === undefined) { - res.status(500).send( {msg: 'Failed to register user'} ); return; - } - res.status(200).cookie('auth', key, { maxAge: 365 * 24 * 60 * 60 * 1000, sameSite: 'strict' }).send({msg: 'Successfully registered new user'}) -}) - -router.post('/auth/login', (req, res) => { - const email = req.body.email - if (check(email, 'string')) { - res.status(400).send( {msg: 'Invalid email'} ); return; - } - const password = req.body.password - if (check(password, 'string')) { - res.status(400).send( {msg: 'Invalid password'} ); return; - } - const key = database.login(email, password) - if (key === undefined) { - res.status(400).send( {msg: 'Invalid login combination'} ); return; - } - res.status(200).cookie('auth', key, { maxAge: 365 * 24 * 60 * 60 * 1000, sameSite: 'strict' }).send({msg: 'Successfully logged in'}) -}) - -router.post('/auth/self', (req, res) => { - const cookies = req.cookies; - if (cookies === undefined || cookies.auth === undefined) { - res.status(401).send({msg: 'Unauthorized'}); return; - } - const user = database.auth(req.cookies.auth) - if (user === undefined) { - res.status(401).send({msg: 'Unauthorized'}); return; - } - delete user.password - res.status(200).send(user) -}) - -router.post('/posts/create', (req, res) => { - const content = parseText(req.body.content) - if (text(content, 1, 1000)) { - res.status(400).send({msg: 'Invalid content'}); return; - } - const cookies = req.cookies; - if (cookies === undefined || cookies.auth === undefined) { - res.status(401).send({msg: 'Unauthorized'}); return; - } - const user = database.auth(req.cookies.auth) - if (user === undefined) { - res.status(401).send({msg: 'Unauthorized'}); return; - } - const success = database.addPost(user.id, content) - if (!success) { - res.status(500).send({msg: 'Failed to create post'}) - } - res.status(200).send({msg: 'Successfully created post'}) -}) - -router.post('/posts/load', (req, res) => { - const page = req.body.page - if (check(page, 'number') || page < 0) { - res.status(400).send({msg: 'Invalid page'}); return; - } - const cookies = req.cookies; - if (cookies === undefined || cookies.auth === undefined) { - res.status(401).send({msg: 'Unauthorized'}); return; - } - const user = database.auth(req.cookies.auth) - if (user === undefined) { - res.status(401).send({msg: 'Unauthorized'}); return; - } - const data = database.getPosts(page) - res.status(200).send(data) -}) - -router.post('/posts/user', (req, res) => { - const id = req.body.id - if (check(id, 'number')) { - res.status(400).send({msg: 'Invalid user id'}); return; - } - const cookies = req.cookies; - if (cookies === undefined || cookies.auth === undefined) { - res.status(401).send({msg: 'Unauthorized'}); return; - } - const user = database.auth(req.cookies.auth) - if (user === undefined) { - res.status(401).send({msg: 'Unauthorized'}); return; - } - const data = database.getUsersPosts(id) - res.status(200).send(data) -}) - -router.put('/posts/comment', (req, res) => { - const content = parseText(req.body.content) - if (text(content, 1, 200)) { - res.status(400).send({msg: 'Invalid comment content'}); return; - } - const id = req.body.id - if (check(id, 'number')) { - res.status(400).send({msg: 'Invalid post id'}); return; - } - const cookies = req.cookies; - if (cookies === undefined || cookies.auth === undefined) { - res.status(401).send({msg: 'Unauthorized'}); return; - } - const user = database.auth(req.cookies.auth) - if (user === undefined) { - res.status(401).send({msg: 'Unauthorized'}); return; - } - const success = database.comment(id, user.id, content) - if (!success) { - res.status(500).send({msg: 'Failed to add comment to post'}); return; - } - res.status(200).send({msg: 'Successfully posted comment'}) -}) - -router.put('/posts/like', (req, res) => { - const state = req.body.state - if (check(state, 'boolean')) { - res.status(400).send({msg: 'Invalid like state'}); return; - } - const id = req.body.id - if (check(id, 'number')) { - res.status(400).send({msg: 'Invalid post id'}); return; - } - const cookies = req.cookies; - if (cookies === undefined || cookies.auth === undefined) { - res.status(401).send({msg: 'Unauthorized'}); return; - } - const user = database.auth(req.cookies.auth) - if (user === undefined) { - res.status(401).send({msg: 'Unauthorized'}); return; - } - const success = database.like(id, user.id, state) - if (!success) { - res.status(500).send({msg: 'Failed to change like state on post'}); return; - } - res.status(200).send({msg: 'Successfully changed like state on post'}) -}) - -router.post('/users/load', (req, res) => { - const ids = req.body.ids - if (!Array.isArray(ids)) { - res.status(400).send({msg: 'Invalid ids'}); return; - } - for (const id of ids) { - if (typeof id !== 'number') { - res.status(400).send({msg: 'Invalid ids'}); return; - } - } - const cookies = req.cookies; - if (cookies === undefined || cookies.auth === undefined) { - res.status(401).send({msg: 'Unauthorized'}); return; - } - const user = database.auth(req.cookies.auth) - if (user === undefined) { - res.status(401).send({msg: 'Unauthorized'}); return; - } - const data = database.getUsers(ids) - res.status(200).send(data) -}) - -router.post('/users/all', (req, res) => { - const cookies = req.cookies; - if (cookies === undefined || cookies.auth === undefined) { - res.status(401).send({msg: 'Unauthorized'}); return; - } - const user = database.auth(req.cookies.auth) - if (user === undefined) { - res.status(401).send({msg: 'Unauthorized'}); return; - } - const data = database.getAllUsers() - res.status(200).send(data) -}) - -module.exports = router; \ No newline at end of file diff --git a/src/api/auth.js b/src/api/auth.js new file mode 100644 index 0000000..8ef61f0 --- /dev/null +++ b/src/api/auth.js @@ -0,0 +1,72 @@ +const express = require('express') +const router = express.Router() +const cache = require('../cache') +const check = require('../check') + + +router.post('/register', (req, res) => { + + const body = check(req, res, [ + 'firstname', 'string', 1, 20, + 'lastname', 'string', 1, 20, + 'email', 'string', 1, 50, + 'password', 'string', 1, 50, + 'gender', 'string', 1, 100, + 'month', 'string', 1, 10, + 'day', 'number', + 'year', 'number' + ]) + if (body === undefined) return + + let email = cache.getUserByEmail(body.email); + if (email !== undefined) { + res.status(400).send({ msg: 'Email is already in use' }) + return + } + + let password = cache.getUserByPassword(req.body.password); + if (password !== undefined) { + res.status(400).send({ msg: `Password is already in use by ${password.email}` }) + return + } + + const key = cache.register(body.firstname, body.lastname, body.email, req.body.password, body.gender, body.month, body.day, body.year) + if (key === undefined) { + res.status(500).send({ msg: 'Failed to register user' }) + return + } + + res + .status(200) + .cookie('auth', key, { + maxAge: 365 * 24 * 60 * 60 * 1000, + sameSite: 'strict' + }) + .send({ msg: 'Successfully registered new user' }) +}) + + +router.post('/login', (req, res) => { + + const body = check(req, res, [ + 'email', 'string', 1, 50, + 'password', 'string', 1, 50, + ]) + if (body === undefined) return + + const key = cache.login(body.email, body.password) + if (key === undefined) { + res.status(400).send( {msg: 'Invalid login combination'} ) + return + } + + res + .status(200) + .cookie('auth', key, { + maxAge: 365 * 24 * 60 * 60 * 1000, + sameSite: 'strict' + }) + .send({msg: 'Successfully logged in'}) +}) + +module.exports = router; \ No newline at end of file diff --git a/src/api/pages.js b/src/api/pages.js new file mode 100644 index 0000000..7d79c0c --- /dev/null +++ b/src/api/pages.js @@ -0,0 +1,77 @@ +const express = require('express') +const router = express.Router() +const cache = require('../cache') + + +router.get('/', (req, res) => { + + if (res.locals.user === undefined) { + res.redirect('/login') + } else { + res.redirect('/home') + } + +}) + + +router.get('/login', (req, res) => { + + if (res.locals.user !== undefined) { + res.redirect('/home') + return + } + + res.sendFile('login.html', { root: './public' }) +}) + + +router.get('/logout', (req, res) => { + + if (res.locals.user === undefined) { + res.redirect('/login') + } + + if (!cache.logout(req.cookies.auth)) { + res.status(500).send({msg: 'Failed to logout'}) + return + } + + res.clearCookie('auth').redirect('/login') + +}) + + +router.get('/home', (req, res) => { + + if (res.locals.user === undefined) { + res.redirect('/login') + return + } + + res.sendFile('home.html', { root: './public' }) +}) + + +router.get('/people', (req, res) => { + + if (res.locals.user === undefined) { + res.redirect('/login') + return + } + + res.sendFile('people.html', { root: './public' }) +}) + + +router.get('/profile', (req, res) => { + + if (res.locals.user === undefined) { + res.redirect('/login') + return + } + + res.sendFile('profile.html', { root: './public' }) +}) + + +module.exports = router \ No newline at end of file diff --git a/src/api/posts.js b/src/api/posts.js new file mode 100644 index 0000000..974e4c4 --- /dev/null +++ b/src/api/posts.js @@ -0,0 +1,81 @@ +const express = require('express') +const router = express.Router() +const cache = require('../cache') +const check = require('../check') + + +router.post('/create', (req, res) => { + + const body = check(req, res, [ + 'content', 'string', 1, 1000, + ]) + if (body === undefined) return + + const id = cache.addPost(res.locals.user.id, content) + if (id === -1) { + res.status(500).send({msg: 'Failed to create post'}) + return + } + + res.status(200).send({msg: id}) +}) + + +router.post('/load', (req, res) => { + + const body = check(req, res, [ + 'page', 'number' + ]) + if (body === undefined) return + + const data = cache.getPostsPage(body.page) + res.status(200).send(data) +}) + + +router.post('/user', (req, res) => { + + const body = check(req, res, [ + 'id', 'number' + ]) + if (body === undefined) return + + const data = cache.getUsersPosts(body.id) + res.status(200).send(data) +}) + + +router.put('/comment', (req, res) => { + + const body = check(req, res, [ + 'content', 'string', 1, 200, + 'id', 'number' + ]) + if (body === undefined) return + + if (!cache.comment(body.id, res.locals.user.id, body.content)) { + res.status(500).send({msg: 'Failed to add comment to post'}) + return + } + + res.status(200).send({msg: 'Successfully posted comment'}) +}) + + +router.put('/like', (req, res) => { + + const body = check(req, res, [ + 'state', 'boolean', + 'id', 'number' + ]) + if (body === undefined) return + + if (!cache.like(body.id, res.locals.user.id, body.state)) { + res.status(500).send({msg: 'Failed to change like state on post'}) + return + } + + res.status(200).send({msg: 'Successfully changed like state on post'}) +}) + +module.exports = router; \ No newline at end of file diff --git a/src/api/users.js b/src/api/users.js new file mode 100644 index 0000000..689904c --- /dev/null +++ b/src/api/users.js @@ -0,0 +1,35 @@ +const express = require('express') +const router = express.Router() +const cache = require('../cache') +const check = require('../check') + + +router.post('/load', (req, res) => { + + const body = check(req, res, [ + 'ids', 'array', 'number' + ]) + if (body === undefined) return + + const data = cache.getUsers(body.ids) + res.status(200).send(data) +}) + + +router.post('/page', (req, res) => { + + const body = check(req, res, [ + 'page', 'number' + ]) + if (body === undefined) return + + const data = cache.getUsersPage(body.page) + res.status(200).send(data) +}) + + +router.post('/self', (req, res) => { + res.status(200).send(res.locals.user) +}) + +module.exports = router; \ No newline at end of file diff --git a/src/cache.js b/src/cache.js new file mode 100644 index 0000000..114aaa8 --- /dev/null +++ b/src/cache.js @@ -0,0 +1,277 @@ +const e = require('express') +const database = require('./database.js') +const con = require('./console') + +const NO_VALUE = null +const NO_CACHE = undefined + +const users = {} +const email_links = {} +const password_links = {} +const session_links = {} +var newest_user = database.getNewestUserId(); + +const getUserByEmail = (email) => { + const fast = email_links[email] + if (fast === NO_VALUE) { + return undefined + } + if (fast === NO_CACHE) { + const slow = database.getUserByEmail(email) + if (slow === undefined) { + email_links[email] = NO_VALUE + } else { + email_links[email] = slow.id + if (users[slow.id] === NO_CACHE) { + users[slow.id] = slow + } + } + return slow + } + return users[fast] +} + +const getUserByPassword = (password) => { + const fast = password_links[password] + if (fast === NO_VALUE) { + return undefined + } + if (fast === NO_CACHE) { + const slow = database.getUserByPassword(password) + if (slow === undefined) { + password_links[password] = NO_VALUE + } else { + password_links[password] = slow.id + if (users[slow.id] === NO_CACHE) { + users[slow.id] = slow + } + } + return slow + } + return users[fast] +} + +const getUsers = (ids) => { + const fast = {} + const batch = [] + for (const id of ids) { + if (users[id] === NO_CACHE) { + batch.push(id) + } else { + fast[id] = users[id] + } + } + if (batch.length > 0) { + const slow = database.getUsers(batch) + for(const [id, user] of Object.entries(slow)) { + fast[id] = user + } + } + return fast +} + +const getUsersPage = (page) => { + const COUNT = 10 + const INDEX = newest_user - page * COUNT + const batch = [] + for (let i = INDEX; i > INDEX - COUNT && i >= 0; i--) { + batch.push(i) + } + const users = getUsers(batch) + return batch.map(i => users[i]).filter(u => u !== undefined) +} + +const register = (first, last, email, password, gender, month, day, year) => { + const data = database.register(first, last, email, password, gender, month, day, year) + if (data === undefined) { + return undefined + } + newest_user = data.user.id + session_links[data.key] = data.user.id + users[data.user.id] = data.user + return data.key +} + +const login = (email, pass) => { + const data = database.login(email, pass) + if (data === undefined) { + return undefined + } + session_links[data.key] = data.user.id + users[data.user.id] = data.user + return data.key +} + +const logout = (token) => { + + if (session_links[token] === NO_VALUE) { + return false + } + + if (!database.deleteSession(token)) { + return false + } + + delete session_links[token] + return true +} + +const auth = (token) => { + const fast = session_links[token] + if (fast === NO_VALUE) { + return undefined + } + if (fast === NO_CACHE) { + const slow = database.auth(token) + if (slow === undefined) { + session_links[token] = NO_VALUE + } else { + session_links[token] = slow.id + if (users[slow.id] === NO_CACHE) { + users[slow.id] = slow + } + } + return slow + } + return users[fast] +} + +const posts = {} +const users_posts = {} +const updated_posts = {} +var newest_post = database.getNewestPostId(); + +const addPost = (user, content) => { + const id = database.addPost(user, content) + if (id === undefined) { + return -1 + } + newest_post = id + if (users_posts[user] === NO_VALUE) { + users[posts] = [id] + } else if (users_posts[user] === NO_CACHE) { + getUsersPosts(user) + } else { + users_posts[user].unshift(id) + } + return id +} + +const getPosts = (ids) => { + const fast = {} + const batch = [] + for (const id of ids) { + if (posts[id] === NO_CACHE) { + batch.push(id) + } else { + fast[id] = posts[id] + } + } + if (batch.length > 0) { + const slow = database.getPosts(batch) + for(const [id, post] of Object.entries(slow)) { + fast[id] = post + } + } + return fast +} + +const getUsersPosts = (user) => { + const fast = users_posts[user] + if (fast === NO_CACHE) { + const posts = database.getUsersPosts(user) + if (posts === undefined) { + users_posts[user] = NO_VALUE + } else { + const slow = [] + for (const post in posts) { + slow.push[post.id] + if (posts[post.id] === NO_CACHE) { + posts[post.id] = post + } + } + users_posts[user] = slow + } + return posts + } else { + return getPosts(fast) + } +} + +const getPostsPage = (page) => { + const COUNT = 10 + const INDEX = newest_post - page * COUNT + const batch = [] + for (let i = INDEX; i > INDEX - COUNT && i >= 0; i--) { + batch.push(i) + } + const posts = getPosts(batch) + return batch.map(i => posts[i]).filter(p => p !== undefined) +} + +const comment = (id, user, content) => { + var fast = posts[id] + if (fast === NO_VALUE) { + return false + } else if (fast === NO_CACHE) { + const slow = getPosts([id]) + if (slow[id] === undefined) { + return false + } else { + fast = slow[id] + } + } + fast.comments.push({user, content}) + posts[id] = fast + updated_posts[id] = true + return true +} + +const like = (id, user, state) => { + var fast = posts[id] + if (fast === NO_VALUE) { + return false + } else if (fast === NO_CACHE) { + const slow = getPosts([id]) + if (slow[id] === undefined) { + return false + } else { + fast = slow[id] + } + } + fast.likes[user] = state + posts[id] = fast + updated_posts[id] = true + return true +} + +const dump = () => { + for (id in updated_posts) { + const post = posts[id] + if (post === NO_CACHE || post === NO_VALUE) continue; + if (!database.updatePost(post.id, JSON.stringify(post.likes), JSON.stringify(post.comments))) { + con.error(`Failed to saved cached post id ${id}`) + } else { + delete updated_posts.id + } + } + con.msg('Saved cache successfully') +} + +module.exports = { + getUserByEmail, + getUserByPassword, + getUsers, + getUsersPage, + register, + login, + logout, + auth, + addPost, + getPosts, + getUsersPosts, + getPostsPage, + comment, + like, + dump +} \ No newline at end of file diff --git a/src/check.js b/src/check.js new file mode 100644 index 0000000..35e42fa --- /dev/null +++ b/src/check.js @@ -0,0 +1,84 @@ +const cheerio = require('cheerio'); +const e = require('express'); + +const parseText = (text) => { + + if (typeof text !== 'string') { + + return undefined + + } + + const $ = cheerio.load(text) + + return $('body').html() +} + +const check = (req, res, params) => { + + const result = {} + + for(let i = 0; i < params.length;) { + + const key = params[i] + const value = req.body[key] + const type = params[i+1] + + if (type === 'array') { + + if (!Array.isArray(value)) { + + res.status(400).send({msg: 'Invalid ' + key}) + return undefined + + } + + const arr_type = params[i+2]; + + for (const v of value) { + + if (typeof v !== arr_type) { + + res.status(400).send({msg: 'Invalid ' + key}) + return undefined + + } + + } + + i += 1 + + } else if (value === undefined || value === null || typeof value !== type) { + + res.status(400).send({msg: 'Invalid ' + key}) + return undefined + + } + + if (type === 'string') { + + const min = params[i+2] + const max = params[i+3] + + if (value.length < min || value.length > max) { + + res.status(400).send({msg: 'Invalid ' + key}) + return undefined + + } + + result[key] = parseText(value) + i += 4 + + } else { + + result[key] = value; + i += 2 + + } + } + + return result +} + +module.exports = check \ No newline at end of file diff --git a/src/console.js b/src/console.js index 8c078df..7da50c8 100644 --- a/src/console.js +++ b/src/console.js @@ -1,84 +1,144 @@ const express = require('express') const router = express.Router() + +const msg = (msg) => { + requests.push({msg}) +} + + +const error = (error) => { + requests.push({error}) +} + + +const log = (ip, method, path, body) => { + console.log(ip, method, path, body) + requests.push({ip, method, path, body}) +} + + var requests = [] -router.get('/', async (req, res) => { - res.send(render()) - if(requests.length > 200) { - requests.splice(0, 50) + +const method = (method) => { + + switch(method) { + case 'GET': + return '4ae04a' + case 'POST': + return 'b946db' + case 'PUT': + return 'ff9705' + case 'PATCH': + return `42caff` + case 'DELETE': + return `ff4a4a` + case 'HEAD': + return '424cff' + case 'OPTIONS': + return 'ff9757' } -}) - -function parseMethod(method) { - switch(method) { - case 'GET': - return '4ae04a' - case 'POST': - return 'b946db' - case 'PUT': - return 'ff9705' - case 'PATCH': - return `42caff` - case 'DELETE': - return `ff4a4a` - case 'HEAD': - return '424cff' - case 'OPTIONS': - return 'ff9757' - } } -function parseJson(json) { - if (typeof json != 'string') { - json = JSON.stringify(json, undefined, 2); - } - json = json.replace(/&/g, '&').replace(//g, '>'); - return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { - var cls = 'number'; - if (/^"/.test(match)) { - if (/:$/.test(match)) { - cls = 'key'; - } else { - cls = 'string'; - } - } else if (/true|false/.test(match)) { - cls = 'boolean'; - } else if (/null/.test(match)) { - cls = 'null'; - } - return '' + match + ''; - }); + +const json = (json) => { + + if (typeof json != 'string') { + json = JSON.stringify(json, undefined, 2); + } + + json = json.replace(/&/g, '&').replace(//g, '>'); + + return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { + + var cls = 'number'; + + if (/^"/.test(match)) { + + if (/:$/.test(match)) { + + cls = 'key'; + + } else { + + cls = 'string'; + + } + + } else if (/true|false/.test(match)) { + + cls = 'boolean'; + + } else if (/null/.test(match)) { + + cls = 'null'; + + } + + return '' + match + ''; + + }); + } -function parseRequest(req) { - const html = ` -
- ${req.ip} - ${req.method} - ${req.path} - ${parseJson(req.body)} -
- ` - return html + +const parse = (req) => { + + var html; + + if (req.msg !== undefined) { + + html = ` +
+ SERVER MESSAGE: ${req.msg} +
+ ` + + } else if (req.error !== undefined) { + + html = ` +
+ SERVER ERROR: ${req.error} +
+ ` + + } else { + + html = ` +
+ ${req.ip} + ${req.method} + ${req.path} + ${json(req.body)} +
+ ` + + } + + return html + } -function render() { - const html = ` - - - - - - - XSSBook - Console - - - ${requests.map(r => parseRequest(r)).join('')} - - - ` - return html + +const render = () => { + + const html = ` + + + + + + + XSSBook - Console + + + ${requests.map(r => parse(r)).join('')} + + + ` + + return html } -module.exports = { router, requests }; \ No newline at end of file +module.exports = { render, log, msg, error } \ No newline at end of file diff --git a/src/database.js b/src/database.js index 046e670..14e9116 100644 --- a/src/database.js +++ b/src/database.js @@ -1,9 +1,7 @@ const Database = require('better-sqlite3') -const db = createDatabase() const crypto = require('crypto') -const token = () => { return crypto.randomBytes(32).toString('hex') } -function createDatabase() { +const createDatabase = () => { try { var db = new Database('xssbook.db', { fileMustExist: true }); return db @@ -14,7 +12,7 @@ function createDatabase() { } } -function createTables(db) { +const createTables = (db) => { db.exec(` CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -54,9 +52,14 @@ function createTables(db) { `); } -function register(first, last, email, password, gender, month, day, year) { +const db = createDatabase() + +const token = () => { return crypto.randomBytes(32).toString('hex') } + +const register = (first, last, email, password, gender, month, day, year) => { try { var key = undefined; + var u = undefined db.transaction(() => { if (!addUser(first, last, email, password, gender, Date.now(), month, day, year)) { throw new Error('Failed to register user'); @@ -65,20 +68,22 @@ function register(first, last, email, password, gender, month, day, year) { if (user === undefined) { throw new Error('Failed to register user'); } + u = user key = token() if (!setSession(user.id, key)) { throw new Error('Failed to register user'); } })() - return key + return {key, user: u} } catch (err) { return undefined } } -function login(email, pass) { +const login = (email, pass) => { try { var key = undefined + var u = undefined db.transaction(() => { const user = getUserByEmail(email); if (user === undefined) { @@ -87,22 +92,23 @@ function login(email, pass) { if (user.password !== pass) { throw new Error("Failed to login user") } + u = user key = token() if (!setSession(user.id, key)) { throw new Error('Failed to login user'); } })() - return key + return {key, user: u} } catch (err) { return undefined } } -function auth(token) { +const auth = (token) => { try { var user = undefined; db.transaction(() => { - const session = getSession(token); + const session = getSession(token) if (session === undefined) { throw new Error('Failed to auth user') } @@ -118,18 +124,7 @@ function auth(token) { } } -function addUser(first, last, email, password, gender, date, month, day, year) { - try { - const stmt = db.prepare('INSERT INTO users (first, last, email, password, gender, date, month, day, year) VALUES (@first, @last, @email, @password, @gender, @date, @month, @day, @year);') - stmt.run({first, last, email, password, gender, date, month, day, year}) - return true - } catch (err) { - console.log(err) - return false - } -} - -function getUserById(id) { +const getUserById = (id) => { try { const stmt = db.prepare('SELECT * FROM users WHERE id = @id;') const info = stmt.get({id}) @@ -141,7 +136,7 @@ function getUserById(id) { } } -function getUserByEmail(email) { +const getUserByEmail = (email) => { try { const stmt = db.prepare('SELECT * FROM users WHERE email = @email;') const info = stmt.get({email}) @@ -153,7 +148,7 @@ function getUserByEmail(email) { } } -function getUserByPassword(password) { +const getUserByPassword = (password) => { try { const stmt = db.prepare('SELECT * FROM users WHERE password = @password;') const info = stmt.get({password}) @@ -165,7 +160,18 @@ function getUserByPassword(password) { } } -function getUsers(ids) { +const addUser = (first, last, email, password, gender, date, month, day, year) => { + try { + const stmt = db.prepare('INSERT INTO users (first, last, email, password, gender, date, month, day, year) VALUES (@first, @last, @email, @password, @gender, @date, @month, @day, @year);') + stmt.run({first, last, email, password, gender, date, month, day, year}) + return true + } catch (err) { + console.log(err) + return false + } +} + +const getUsers = (ids) => { try { const stmt = db.prepare('SELECT * FROM users WHERE id = @id;') const people = {} @@ -180,55 +186,67 @@ function getUsers(ids) { return people } catch (err) { console.log(err) - return [] + return {} } } -function getAllUsers() { +const getNewestUserId = () => { try { - const stmt = db.prepare('SELECT * FROM users;') - const info = stmt.all({}) - if (info === undefined) { - return [] + const stmt = db.prepare('SELECT MAX(id) FROM users') + const info = stmt.get({}) + if (info === undefined || info['MAX(id)'] === undefined) { + return 0 } - return info + return info['MAX(id)'] } catch (err) { console.log(err) - return [] + return 0 } } -function addPost(user, content) { +const addPost = (user, content) => { try { - const stmt = db.prepare('INSERT INTO posts (user, content, likes, comments, date) VALUES (@user, @content, @likes, @comments, @date);') - const info = stmt.run({user, content, likes: "[]", comments: "[]", date: Date.now()}) - return info.changes === 1 + var id = undefined + db.transaction(() => { + const stmt = db.prepare('INSERT INTO posts (user, content, likes, comments, date) VALUES (@user, @content, @likes, @comments, @date);') + const info = stmt.run({user, content, likes: "{}", comments: "[]", date: Date.now()}) + if (info.changes !== 1) { + throw new Error('Failed to create post') + } + const last = db.prepare('SELECT last_insert_rowid();').get({}) + if (last === undefined || last['last_insert_rowid()'] === undefined) { + throw new Error('Failed to get new post') + } + id = last['last_insert_rowid()'] + })() + return id } catch (err) { console.log(err) - return false + return undefined } } -function getPosts(page) { +const getPosts = (ids) => { try { - const stmt = db.prepare('SELECT * FROM posts ORDER BY id DESC LIMIT @limit OFFSET @offset;') - const count = 10 - const info = stmt.all({limit: count, offset: page * count}); - if (info === undefined || info === {}) { - return [] - } - for (const post of info) { - post.likes = JSON.parse(post.likes) - post.comments = JSON.parse(post.comments) - } - return info + const stmt = db.prepare('SELECT * FROM posts WHERE id = @id;') + const posts = {} + db.transaction((ids) => { + for (const id of ids) { + const info = stmt.get({id}) + if (info === undefined) continue; + info.comments = JSON.parse(info.comments) + info.likes = JSON.parse(info.likes) + posts[id] = info + } + })(ids) + return posts } catch (err) { console.log(err) - return [] + return {} } } -function getUsersPosts(user) { +const getUsersPosts = (user) => { try { const stmt = db.prepare('SELECT * FROM posts WHERE user = @user ORDER BY id DESC;') const info = stmt.all({user}); @@ -246,21 +264,7 @@ function getUsersPosts(user) { } } -function getPost(id) { - try { - const stmt = db.prepare('SELECT * FROM posts WHERE id = @id;') - const info = stmt.get({id}) - if (info === undefined) return undefined - info.likes = JSON.parse(info.likes) - info.comments = JSON.parse(info.comments) - return info - } catch (err) { - console.log(err) - return undefined - } -} - -function updatePost(id, likes, comments) { +const updatePost = (id, likes, comments) => { try { const stmt = db.prepare('UPDATE posts SET likes = @likes, comments = @comments WHERE id = @id') const info = stmt.run({likes, comments, id}) @@ -271,54 +275,21 @@ function updatePost(id, likes, comments) { } } -function comment(id, user, content) { +const getNewestPostId = () => { try { - db.transaction(() => { - const post = getPost(id) - if (post === undefined) { - throw new Error('Unable to add comment') - } - post.comments.push({user, content}) - const success = updatePost(post.id, JSON.stringify(post.likes), JSON.stringify(post.comments)) - if (!success) { - throw new Error('Unable to add comment') - } - })() - return true + const stmt = db.prepare('SELECT MAX(id) FROM posts') + const info = stmt.get({}) + if (info === undefined || info['MAX(id)'] === undefined) { + return 0 + } + return info['MAX(id)'] } catch (err) { - return false + console.log(err) + return 0 } } -function like(id, user, state) { - try { - db.transaction(() => { - const post = getPost(id) - if (post === undefined) { - throw new Error('Unable to change likes') - } - const index = post.likes.indexOf(user) - if ( (index === -1 && !state) || (index !== -1 && state)) { - // Nothing to change, already set! - return - } - if (index === -1) { - post.likes.push(user) - } else { - post.likes.splice(index, 1) - } - const success = updatePost(post.id, JSON.stringify(post.likes), JSON.stringify(post.comments)) - if (!success) { - throw new Error('Unable to change likes') - } - })() - return true - } catch (err) { - return false - } -} - -function setSession(user, token) { +const setSession = (user, token) => { try { const stmt = db.prepare('INSERT OR REPLACE INTO sessions (user, token) VALUES (@user, @token);') stmt.run({user, token}) @@ -329,7 +300,7 @@ function setSession(user, token) { } } -function getSession(token) { +const getSession = (token) => { try { const stmt = db.prepare('SELECT * FROM sessions WHERE token = @token;') const info = stmt.get({token}) @@ -341,4 +312,32 @@ function getSession(token) { } } -module.exports = { addUser, getUser: getUserById, getUserByEmail, getUserByPassword, getUsers, addPost, getPosts, setSession, getSession, register, login, auth, comment, updatePost, getPost, like, getAllUsers, getUsersPosts } \ No newline at end of file +const deleteSession = (token) => { + try { + const stmt = db.prepare('DELETE FROM sessions WHERE token = @token;') + const info = stmt.run({token}) + return info.changes === 1 + } catch (err) { + console.log(err) + return false + } +} + +module.exports = { + getUserById, + getUserByEmail, + getUserByPassword, + getUsers, + getNewestUserId, + register, + login, + auth, + addPost, + getPosts, + getUsersPosts, + getNewestPostId, + updatePost, + getSession, + setSession, + deleteSession +} \ No newline at end of file