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 = `
+
+ `
+
+ 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