summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/api.js221
-rw-r--r--src/database.js244
2 files changed, 451 insertions, 14 deletions
diff --git a/src/api.js b/src/api.js
index 6b5e594..8091a3c 100644
--- a/src/api.js
+++ b/src/api.js
@@ -2,13 +2,230 @@ const express = require('express')
const router = express.Router()
const database = require('./database.js')
+const check = (test, type) => {
+ return text === undefined || text === null || typeof test !== type
+}
+
+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('/', (req, res) => {
- res.status(200).send( {msg: 'xssbook api'} )
+router.post('/auth/register', (req, res) => {
+ const first = req.body.first;
+ if (text(first, 1, 20)) {
+ res.status(400).send( {msg: 'Invalid first name'} ); return;
+ }
+ const last = req.body.last;
+ if (text(last, 1, 20)) {
+ res.status(400).send( {msg: 'Invalid last name'} ); return;
+ }
+ const email = 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 = req.body.gender;
+ if (text(gender, 1, 100)) {
+ res.status(400).send( {msg: 'Invalid gender'} ); return;
+ }
+ const month = 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).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).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 = req.body.content
+ if (text(content, 1, 420)) {
+ 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 = 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/database.js b/src/database.js
index 6168f4c..12964b7 100644
--- a/src/database.js
+++ b/src/database.js
@@ -1,5 +1,7 @@
const Database = require('better-sqlite3')
const db = createDatabase()
+const crypto = require('crypto')
+const token = () => { return crypto.randomBytes(32).toString('hex') }
function createDatabase() {
try {
@@ -20,7 +22,7 @@ function createTables(db) {
last VARCHAR(20) NOT NULL,
email VARCHAR(50) NOT NULL,
password VARCHAR(50) NOT NULL,
- gender VARCHAR(20) NOT NULL,
+ gender VARCHAR(100) NOT NULL,
date INTEGER NOT NULL,
month VARCHAR(10) NOT NULL,
day INTEGER NOT NULL,
@@ -52,9 +54,73 @@ function createTables(db) {
`);
}
+function register(first, last, email, password, gender, month, day, year) {
+ try {
+ var key = undefined;
+ db.transaction(() => {
+ if (!addUser(first, last, email, password, gender, Date.now(), month, day, year)) {
+ throw new Error('Failed to register user');
+ }
+ const user = getUserByEmail(email);
+ if (user === undefined) {
+ throw new Error('Failed to register user');
+ }
+ key = token()
+ if (!setSession(user.id, key)) {
+ throw new Error('Failed to register user');
+ }
+ })()
+ return key
+ } catch (err) {
+ return undefined
+ }
+}
+
+function login(email, pass) {
+ try {
+ var key = undefined
+ db.transaction(() => {
+ const user = getUserByEmail(email);
+ if (user === undefined) {
+ throw new Error('Failed to login user')
+ }
+ if (user.password !== pass) {
+ throw new Error("Failed to login user")
+ }
+ key = token()
+ if (!setSession(user.id, key)) {
+ throw new Error('Failed to login user');
+ }
+ })()
+ return key
+ } catch (err) {
+ return undefined
+ }
+}
+
+function auth(token) {
+ try {
+ var user = undefined;
+ db.transaction(() => {
+ const session = getSession(token);
+ if (session === undefined) {
+ throw new Error('Failed to auth user')
+ }
+ const u = getUserById(session.user);
+ if (u === undefined) {
+ throw new Error('Failed to auth user')
+ }
+ user = u;
+ })()
+ return user
+ } catch (err) {
+ return undefined
+ }
+}
+
function addUser(first, last, email, password, gender, date, month, day, year) {
try {
- const stmt = db.prepare('INSERT OR REPLACE INTO users (first, last, email, password, gender, date, month, day, year) VALUES (@first, @last, @email, @password, @gender, @date, @month, @day, @year);')
+ 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) {
@@ -63,7 +129,7 @@ function addUser(first, last, email, password, gender, date, month, day, year) {
}
}
-function getUser(id) {
+function getUserById(id) {
try {
const stmt = db.prepare('SELECT * FROM users WHERE id = @id;')
const info = stmt.get({id})
@@ -75,6 +141,30 @@ function getUser(id) {
}
}
+function getUserByEmail(email) {
+ try {
+ const stmt = db.prepare('SELECT * FROM users WHERE email = @email;')
+ const info = stmt.get({email})
+ if (info === undefined) return undefined
+ return info
+ } catch (err) {
+ console.log(err)
+ return undefined
+ }
+}
+
+function getUserByPassword(password) {
+ try {
+ const stmt = db.prepare('SELECT * FROM users WHERE password = @password;')
+ const info = stmt.get({password})
+ if (info === undefined) return undefined
+ return info
+ } catch (err) {
+ console.log(err)
+ return undefined
+ }
+}
+
function getUsers(ids) {
try {
const stmt = db.prepare('SELECT * FROM users WHERE id = @id;')
@@ -90,14 +180,28 @@ function getUsers(ids) {
return people
} catch (err) {
console.log(err)
- return undefined
+ return []
+ }
+}
+
+function getAllUsers() {
+ try {
+ const stmt = db.prepare('SELECT * FROM users;')
+ const info = stmt.all({})
+ if (info === undefined) {
+ return []
+ }
+ return info
+ } catch (err) {
+ console.log(err)
+ return []
}
}
-function addPost(user, content, likes, comments, date) {
+function addPost(user, content) {
try {
- const stmt = db.prepare('INSERT OR REPLACE INTO posts (user, content, likes, comments, date) VALUES (@user, @content, @likes, @comments, @date);')
- const info = stmt.run({user, content, likes, comments, date})
+ 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
} catch (err) {
console.log(err)
@@ -106,10 +210,112 @@ function addPost(user, content, likes, comments, date) {
}
function getPosts(page) {
- const stmt = db.prepare('SELECT * FROM posts ORDER BY id DESC LIMIT @limit OFFSET @offset;')
- const count = 20
- const info = stmt.all({limit: count, offset: page * count});
- console.log(info)
+ try {
+ const stmt = db.prepare('SELECT * FROM posts ORDER BY id DESC LIMIT @limit OFFSET @offset;')
+ const count = 20
+ 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
+ } catch (err) {
+ console.log(err)
+ return []
+ }
+}
+
+function getUsersPosts(user) {
+ try {
+ const stmt = db.prepare('SELECT * FROM posts WHERE user = @user ORDER BY id DESC;')
+ const info = stmt.all({user});
+ if (info === undefined || info === {}) {
+ return []
+ }
+ for (const post of info) {
+ post.likes = JSON.parse(post.likes)
+ post.comments = JSON.parse(post.comments)
+ }
+ return info
+ } catch (err) {
+ console.log(err)
+ return []
+ }
+}
+
+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) {
+ try {
+ const stmt = db.prepare('UPDATE posts SET likes = @likes, comments = @comments WHERE id = @id')
+ const info = stmt.run({likes, comments, id})
+ return info.changes === 1
+ } catch (err) {
+ console.log(err)
+ return false
+ }
+}
+
+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) {
+ 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) {
@@ -121,4 +327,18 @@ function setSession(user, token) {
console.log(err)
return false
}
-} \ No newline at end of file
+}
+
+function getSession(token) {
+ try {
+ const stmt = db.prepare('SELECT * FROM sessions WHERE token = @token;')
+ const info = stmt.get({token})
+ if (info === undefined) return undefined
+ return info
+ } catch (err) {
+ console.log(err)
+ return undefined
+ }
+}
+
+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