diff --git a/index.js b/index.js
index 1564775..c35ccbb 100644
--- a/index.js
+++ b/index.js
@@ -4,6 +4,10 @@ const express = require('express')
const app = express()
const port = 8080
+app.set('trust proxy', true)
+
+const database = require('./src/database.js')
+
const rateLimiter = require('express-rate-limit')
const limiter = (min, count) => {
return rateLimiter({
@@ -21,11 +25,55 @@ app.use(cookieParser())
app.use(express.json());
app.use(express.static('public'))
+app.use((req, res, next) => {
+ const public = { ... req.body }
+ if (public.password !== undefined) {
+ public.password = '********'
+ }
+ console.log(req.ip, req.method, req.path, public)
+ update(req.ip, req.method, req.path, public)
+ 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) => {
- res.sendFile('login.html', { root: './public' })
+ 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' })
})
@@ -37,17 +85,95 @@ app.get('/profile', (req, res) => {
res.sendFile('profile.html', { root: './public' })
})
-app.use('/api', limiter(1,60))
-app.use('/api/register', limiter(60, 5))
-app.use('/api/login', limiter(10, 5))
-
const api = require('./src/api.js')
app.use('/api', api);
+const connections = []
+app.get('/console', (req, res) => {
+ res.write(`
+
+
+
+
+
+ XSSBook - Console
+
+
+ `)
+ connections.push(res)
+})
+
+function color(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 highlight(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 + '';
+ });
+}
+
+async function update(ip, method, path, json) {
+ connections.forEach(con => {
+ con.write(`
+
+ ${ip}
+ ${method}
+ ${path}
+ ${highlight(json)}
+
+ `)
+ })
+}
+
+// app.use('/api', limiter(1,60))
+// app.use('/api/register', limiter(60, 5))
+// app.use('/api/login', limiter(10, 5))
+
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'})
+})
+
app.listen(port, () => {
console.log(`App listening on port http://127.0.0.1:${port}`)
})
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 1044105..3eea334 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
"dependencies": {
"better-sqlite3": "^8.0.1",
"cookie-parser": "^1.4.6",
+ "crypto": "^1.0.1",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"express-rate-limit": "^6.7.0"
@@ -171,9 +172,9 @@
}
},
"node_modules/cookie": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
- "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
+ "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
"engines": {
"node": ">= 0.6"
}
@@ -190,19 +191,17 @@
"node": ">= 0.8.0"
}
},
- "node_modules/cookie-parser/node_modules/cookie": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
- "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
- "engines": {
- "node": ">= 0.6"
- }
- },
"node_modules/cookie-signature": {
"version": "1.0.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/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -360,6 +359,14 @@
"express": "^4 || ^5"
}
},
+ "node_modules/express/node_modules/cookie": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
+ "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
@@ -1145,9 +1152,9 @@
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
- "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
+ "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
},
"cookie-parser": {
"version": "1.4.6",
@@ -1156,13 +1163,6 @@
"requires": {
"cookie": "0.4.1",
"cookie-signature": "1.0.6"
- },
- "dependencies": {
- "cookie": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
- "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
- }
}
},
"cookie-signature": {
@@ -1170,6 +1170,11 @@
"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=="
+ },
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -1280,6 +1285,13 @@
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
+ },
+ "dependencies": {
+ "cookie": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
+ "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
+ }
}
},
"express-rate-limit": {
diff --git a/package.json b/package.json
index c185b82..ef46f50 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
"dependencies": {
"better-sqlite3": "^8.0.1",
"cookie-parser": "^1.4.6",
+ "crypto": "^1.0.1",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"express-rate-limit": "^6.7.0"
diff --git a/public/404.html b/public/404.html
index 7aedb82..49f0d5d 100644
--- a/public/404.html
+++ b/public/404.html
@@ -5,11 +5,11 @@
- XSSBook - Home
+ XSSBook - Not Found
404
diff --git a/public/css/console.css b/public/css/console.css
new file mode 100644
index 0000000..bc07969
--- /dev/null
+++ b/public/css/console.css
@@ -0,0 +1,60 @@
+body {
+ margin: 0;
+ padding: 0;
+ background-color: #181818;
+ display: flex;
+ flex-direction: column-reverse;
+}
+
+@font-face {
+ font-family: sfpro;
+ src: url("../fonts/sfpro.otf") format("opentype");
+}
+
+div {
+ background-color: #282828;
+ font-family: sfpro;
+ margin: 15px;
+ margin-bottom: 0px;
+ border-radius: 5px;
+ padding: 10px;
+ width: calc(100% - 50px)
+}
+
+span {
+ display: inline-block;
+ padding: 0;
+ margin: 0;
+ color: #ffffff;
+ font-family: sfpro;
+ margin-right: 10px;
+}
+
+.json span {
+ display: inline;
+ margin: 0;
+}
+
+.key {
+ color: white;
+}
+
+.value {
+ color: white;
+}
+
+.boolean {
+ color: aqua;
+}
+
+.null {
+ color: blue;
+}
+
+.number {
+ color: yellow;
+}
+
+.string {
+ color: #4ae04a
+}
\ No newline at end of file
diff --git a/public/css/home.css b/public/css/home.css
index de0c2d0..33d72c0 100644
--- a/public/css/home.css
+++ b/public/css/home.css
@@ -148,6 +148,11 @@ body {
margin-bottom: 10px;
}
+.comment p, .post p {
+ word-break: break-all;
+ white-space: normal;
+}
+
.comment>span {
display: flex;
flex-direction: column;
diff --git a/public/css/main.css b/public/css/main.css
index 9d2fe1d..a75a941 100644
--- a/public/css/main.css
+++ b/public/css/main.css
@@ -42,6 +42,14 @@ body {
color: #1778f2
}
+.error {
+ font-family: sfpro;
+ color: #f02849;
+ padding-top: 10px;
+ margin-bottom: -10px;
+ font-size: 15px;
+}
+
.gtext {
font-family: sfpro;
color: #606770
@@ -77,6 +85,7 @@ body {
}
a {
+ color: inherit;
text-decoration: none;
cursor: pointer;
}
@@ -263,6 +272,7 @@ footer {
.hidden {
visibility: hidden;
pointer-events: none;
+ display: none !important;
}
.pfp, .pfp img {
diff --git a/public/css/people.css b/public/css/people.css
index 70db81f..b8cf025 100644
--- a/public/css/people.css
+++ b/public/css/people.css
@@ -1,3 +1,44 @@
body {
background-color: #f0f2f5;
+}
+
+#users {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.person {
+ width: 30em;
+ height: fit-content;
+ background-color: white;
+ border-radius: 10px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, .05);
+ margin-bottom: 1.5em;
+ display: flex;
+ flex-direction: row;
+}
+
+.profile, .profile img {
+ border-radius: 10px 0px 0px 10px;
+ width: 10em;
+ height: 10em;
+ padding: 0;
+ display: block;
+ background-color: #e4e6e8;
+ flex-shrink: 0;
+}
+
+.info {
+ margin: 20px;
+ display: flex;
+ flex-direction: column;
+}
+
+.info span {
+ width: 280px;
+ margin-bottom: 5px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
}
\ No newline at end of file
diff --git a/public/css/profile.css b/public/css/profile.css
index 70db81f..4c5ae10 100644
--- a/public/css/profile.css
+++ b/public/css/profile.css
@@ -1,3 +1,121 @@
body {
background-color: #f0f2f5;
+}
+
+.spacer {
+ margin-bottom: 3.5em;
+}
+
+#top {
+ background-color: white;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 100%;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, .05);
+}
+
+#banner {
+ background-image: linear-gradient(#949494, white, white);
+ height: 30em;
+ width: 100%;
+ display: flex;
+ justify-content: center;
+}
+
+#banner div, #banner img {
+ width: 80em;
+ height: inherit;
+ background-color: #e4e6e8;
+ border-radius: 0px 0px 20px 20px;
+}
+
+#info {
+ width: 80em;
+ display: flex;
+ flex-direction: row;
+}
+
+.face {
+ background-color: #e4e6e8;
+ height: 12em;
+ width: 12em;
+ border-radius: 7em;
+ border: solid 5px white;
+ margin-top: -2em;
+ margin-left: 2em;
+ margin-right: 2em;
+}
+
+.infodata {
+ margin-top: 2em;
+ display: flex;
+ flex-direction: column;
+}
+
+.infodata span {
+ margin-bottom: .5em;
+}
+
+.profilebuttons {
+ width: 80em;
+ height: 3em;
+ display: flex;
+ align-items: center;
+}
+
+.profilebuttons button {
+ all: unset;
+ font-family: sfprobold;
+ padding: 0px 50px;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #606770;
+ cursor: pointer;
+}
+
+.profilebuttons button:hover {
+ background-color: #dddfe2;
+}
+
+.selected {
+ color: #1778f2 !important;
+ border-bottom: 3px solid #1778f2 !important;
+}
+
+#about {
+ margin-top: 2em;
+ align-self: center;
+ padding: 0;
+ display: flex;
+ flex-direction: row;
+}
+
+#posts {
+ margin-top: 2em;
+}
+
+#about .ltext {
+ border-right: 2px solid #dadde1;
+ padding: 10px;
+ padding-right: 3em;
+}
+
+#about .data {
+ display: flex;
+ flex-direction: column;
+ padding: 10px;
+ padding-left: 20px;
+ padding-top: 15px;
+}
+
+#about .data span {
+ margin-bottom: 10px;
+ width: 28em;
+ margin-bottom: 5px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
}
\ No newline at end of file
diff --git a/public/home.html b/public/home.html
index 6035fc4..cc780ac 100644
--- a/public/home.html
+++ b/public/home.html
@@ -10,6 +10,8 @@
+
+
\ No newline at end of file
diff --git a/public/js/api.js b/public/js/api.js
new file mode 100644
index 0000000..371ecf3
--- /dev/null
+++ b/public/js/api.js
@@ -0,0 +1,57 @@
+const endpoint = 'https://xssbook.com/api'
+
+const request = async (url, body, method) => {
+ if (method === undefined) method = 'POST'
+ const response = await fetch(endpoint + url, {
+ method,
+ body: JSON.stringify(body),
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ });
+ if (response.status == 401) {
+ location.href = 'login'
+ }
+ const json = await response.json()
+ return { status: response.status, msg: json.msg, json }
+}
+
+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 loadposts = async (page) => {
+ return await request('/posts/load', {page})
+}
+
+const loadusersposts = async (id) => {
+ return await request('/posts/user', {id})
+}
+
+const loadusers = async (ids) => {
+ return await request('/users/load', {ids})
+}
+
+const loadallusers = async () => {
+ return await request('/users/all', {})
+}
+
+const loadself = async () => {
+ return await request("/auth/self", {})
+}
+
+const postcomment = async (id, content) => {
+ return await request('/posts/comment', {id, content}, 'PUT')
+}
+
+const postlike = async (id, state) => {
+ return await request('/posts/like', {id, state}, 'PUT')
+}
+
+const createpost = async (content) => {
+ return await request('/posts/create', {content})
+}
\ No newline at end of file
diff --git a/public/js/header.js b/public/js/header.js
index 24643d6..8fe03e5 100644
--- a/public/js/header.js
+++ b/public/js/header.js
@@ -1,7 +1,7 @@
function header(home, people) {
const html = `
diff --git a/public/people.html b/public/people.html
index a46487d..f0aa514 100644
--- a/public/people.html
+++ b/public/people.html
@@ -3,12 +3,13 @@
-
+
XSSBook - People
+
\ No newline at end of file
diff --git a/public/profile.html b/public/profile.html
index 5f7a902..0274327 100644
--- a/public/profile.html
+++ b/public/profile.html
@@ -3,27 +3,15 @@
-
+
+
XSSBook - Profile
-
+
+
+
+
+
\ No newline at end of file
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 addPost(user, content, likes, comments, date) {
+function getAllUsers() {
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('SELECT * FROM users;')
+ const info = stmt.all({})
+ if (info === undefined) {
+ return []
+ }
+ return info
+ } catch (err) {
+ console.log(err)
+ return []
+ }
+}
+
+function 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
} 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