summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/api.js238
-rw-r--r--src/api/auth.js72
-rw-r--r--src/api/pages.js77
-rw-r--r--src/api/posts.js81
-rw-r--r--src/api/users.js35
-rw-r--r--src/cache.js277
-rw-r--r--src/check.js84
-rw-r--r--src/console.js200
-rw-r--r--src/database.js223
9 files changed, 867 insertions, 420 deletions
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)
- }
-})
-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'
- }
+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 parseJson(json) {
- if (typeof json != 'string') {
- json = JSON.stringify(json, undefined, 2);
- }
- json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
- 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 '<span class="' + cls + '">' + match + '</span>';
- });
+
+const json = (json) => {
+
+ if (typeof json != 'string') {
+ json = JSON.stringify(json, undefined, 2);
+ }
+
+ json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+
+ 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 '<span class="' + cls + '">' + match + '</span>';
+
+ });
+
}
-function parseRequest(req) {
- const html = `
- <div>
- <span class="ip">${req.ip}</span>
- <span class="method" style="color: #${parseMethod(req.method)}">${req.method}</span>
- <span class="path">${req.path}</span>
- <span class="json">${parseJson(req.body)}</span>
- </div>
- `
- return html
+
+const parse = (req) => {
+
+ var html;
+
+ if (req.msg !== undefined) {
+
+ html = `
+ <div>
+ <span style="color: #00aaff">SERVER MESSAGE: ${req.msg}</span>
+ </div>
+ `
+
+ } else if (req.error !== undefined) {
+
+ html = `
+ <div>
+ <span style="color: #ff4050">SERVER ERROR: ${req.error}</span>
+ </div>
+ `
+
+ } else {
+
+ html = `
+ <div>
+ <span class="ip">${req.ip}</span>
+ <span class="method" style="color: #${method(req.method)}">${req.method}</span>
+ <span class="path">${req.path}</span>
+ <span class="json">${json(req.body)}</span>
+ </div>
+ `
+
+ }
+
+ return html
+
}
-function render() {
- const html = `
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="refresh" content="5">
- <link rel="stylesheet" href="css/console.css">
- <title>XSSBook - Console</title>
- </head>
- <body>
- ${requests.map(r => parseRequest(r)).join('')}
- </body>
- </html>
- `
- return html
+
+const render = () => {
+
+ const html = `
+ <!DOCTYPE html>
+ <html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta http-equiv="refresh" content="5">
+ <link rel="stylesheet" href="css/console.css">
+ <title>XSSBook - Console</title>
+ </head>
+ <body>
+ ${requests.map(r => parse(r)).join('')}
+ </body>
+ </html>
+ `
+
+ 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) {
- 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
- } catch (err) {
- return false
- }
-}
-
-function like(id, user, state) {
+const getNewestPostId = () => {
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
+ 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 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