diff options
author | Tyler Murphy <tylermurphy534@gmail.com> | 2023-01-23 21:40:41 -0500 |
---|---|---|
committer | Tyler Murphy <tylermurphy534@gmail.com> | 2023-01-23 21:40:41 -0500 |
commit | 15f2b32511e9b4c0479ad03c18a69653328f36b1 (patch) | |
tree | c9b4782bfc0b790ab1e19c7b3e2ac43d22b005e7 /src | |
parent | fix endpoint, html parse, load more posts (diff) | |
download | xssbook-15f2b32511e9b4c0479ad03c18a69653328f36b1.tar.gz xssbook-15f2b32511e9b4c0479ad03c18a69653328f36b1.tar.bz2 xssbook-15f2b32511e9b4c0479ad03c18a69653328f36b1.zip |
i changed a lot of shit
Diffstat (limited to 'src')
-rw-r--r-- | src/api.js | 238 | ||||
-rw-r--r-- | src/api/auth.js | 72 | ||||
-rw-r--r-- | src/api/pages.js | 77 | ||||
-rw-r--r-- | src/api/posts.js | 81 | ||||
-rw-r--r-- | src/api/users.js | 35 | ||||
-rw-r--r-- | src/cache.js | 277 | ||||
-rw-r--r-- | src/check.js | 84 | ||||
-rw-r--r-- | src/console.js | 200 | ||||
-rw-r--r-- | src/database.js | 223 |
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, '&').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 '<span class="' + cls + '">' + match + '</span>'; - }); + +const json = (json) => { + + if (typeof json != 'string') { + json = JSON.stringify(json, undefined, 2); + } + + json = json.replace(/&/g, '&').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 '<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 |