sanifae/src/lib/db/db.js
2023-02-10 19:12:57 -05:00

271 lines
No EOL
7.1 KiB
JavaScript

const rowCount = 5;
const AUTH_ACTIONS = [
'postCreate',
'fileCreate',
'vote'
];
const fileSizeLimit = 1024*1024*5;
import sqlite3 from 'sqlite3'
import { open } from 'sqlite'
import { hash, compare } from 'bcrypt'
import { randomBytes, createHash } from 'node:crypto';
import { readFile, writeFile } from 'node:fs/promises';
import { calcVote, calcVoteUser, checkLength, checkRegex } from '../util.js';
var db;
async function initDb() {
db = await open({
filename: `${process.cwd()}/db/main.db`,
driver: sqlite3.Database
});
await db.run('CREATE TABLE IF NOT EXISTS auth (username CHAR(64), password CHAR(1024))');
await db.run('CREATE TABLE IF NOT EXISTS token (username CHAR(64), token CHAR(1024))');
await db.run('CREATE TABLE IF NOT EXISTS post (username CHAR(64), id CHAR(64), content CHAR(10240), upvotes INTEGER, downvotes INTEGER, rating REAL)');
await db.run('CREATE TABLE IF NOT EXISTS vote (id CHAR(64), username CHAR(64), type INTEGER)');
await db.run('CREATE TABLE IF NOT EXISTS user (username CHAR(64), followers INTEGER, following INTEGER, upvotes INTEGER, downvotes INTEGER, reputation REAL)');
}
let backendProxy = async ({route, backendParams}) => {
if (!db) await initDb();
if (AUTH_ACTIONS.indexOf(route) != -1) {
var user = (await backend.token({cookies: backendParams.cookies})).data;
if (!user || user == '') return {'success': 'Not authorized.' };
backendParams['user'] = user;
}
return backend[route](backendParams);
}
var backend = {};
let updateUser = async ({user}) => {
let allPosts = await db.all('SELECT * from post WHERE username = ?', [
user
]);
let upvotes = 0;
let downvotes = 0;
let reputation = 0;
allPosts.forEach(post => {
upvotes += post.upvotes || 0;
downvotes += post.downvotes || 0;
});
await db.run('DELETE FROM user WHERE username = ?', [
user
]);
await db.run('INSERT INTO user (username,followers,following,upvotes,downvotes,reputation) VALUES (?,?,?,?,?,?)', [
user,
0,
0,
upvotes,
downvotes,
calcVoteUser(upvotes,downvotes)
]);
}
backend.register = async ({user, pass, pass2}) => {
var lengthCheck = false;
lengthCheck =
checkLength(pass,'Password',4,1024) ||
checkLength(user,'Username',1,64) ||
checkRegex(user,'Username',/[^A-Za-z0-9\-\_]/g);
if (lengthCheck) return lengthCheck;
if (pass != pass2) return {'success': 'Passwords don\'t match.'};
var existingAccounts = await db.all('SELECT username FROM auth WHERE username = ?',[
user
]);
if (existingAccounts && existingAccounts.length > 0)
return { success: 'Account already exists.' };
var passHash = await hash(pass,10);
await db.run('INSERT INTO auth (username, password) VALUES (?, ?)', [
user,
passHash
])
return { success: 'Successfully created account.', location: '/'};
}
backend.login = async ({user, pass, cookies}) => {
var existingAccounts = await db.all('SELECT username, password FROM auth WHERE username = ?',[
user
]);
if (!existingAccounts || existingAccounts.length < 1)
return { success: 'Account does not exist.' };
var passHash = await compare(pass,existingAccounts[0].password);
if (!passHash)
return { success: 'Incorrect password.' };
var token = randomBytes(256).toString('hex');
await db.run('INSERT INTO token (username, token) VALUES (?, ?)', [
user,
token
])
if (token) {
cookies.set('token',token, {
maxAge: 60 * 60 * 24 * 7,
path: '/'
});
};
return { success: 'Successfully logged into account.', data: token, location: '/'};
}
backend.postCreate = async ({content, user}) => {
var lengthCheck = checkLength(content,'Post content',1,10240);
if (lengthCheck)
return lengthCheck;
if (!content) return {'success': 'There is no post!' };
var id = randomBytes(10).toString('hex');
await db.run('INSERT INTO post (username, id, content, rating) VALUES (?, ?, ?, ?)', [
user,
id,
content,
calcVote(0,0)
])
return {'success': 'Your post has been broadcasted!', 'href': `/post/${id}` };
}
backend.postGet = async ({id}) => {
var posts = await db.all('SELECT * from post WHERE id = ?', [
id
])
if (!posts || posts.length < 1) {
return {'success': 'Post does not exist.'}
}
return {data: posts[0]};
}
backend.userGet = async ({user}) => {
var posts = await db.all('SELECT * from user WHERE username = ?', [
user
])
if (!posts || posts.length < 1) {
return {'success': 'Post does not exist.'}
}
return {data: posts[0]};
}
backend.postBulk = async ({page,user}) => {
var posts;
if (!user) {
posts = await db.all('SELECT * from post ORDER BY rating DESC LIMIT ?, ?', [
page*rowCount,
rowCount
])
} else {
posts = await db.all('SELECT * from post WHERE username = ? ORDER BY rating DESC LIMIT ?, ?', [
user,
page*rowCount,
rowCount
])
}
return {data: posts};
}
backend.vote = async ({cookies, id, vote, user}) => {
if (!id || (vote != 'down' && vote != 'up')) return {success: 'fail' };
await db.run('DELETE FROM vote WHERE username = ? AND id = ?', [
user,
id
]);
await db.run('INSERT INTO vote (id, username, type) VALUES (?,?,?)', [
id,
user,
vote == 'up' ? 1 : 2
]);
var votes = await db.all('SELECT type from vote WHERE id = ?', [
id
]) || [];
var up = votes.filter(x => x.type == 1).length;
var down = votes.filter(x => x.type == 2).length;
await db.run('UPDATE post SET upvotes = ?, downvotes = ?, rating = ? WHERE id = ?', [
up,
down,
calcVote(up,down),
id
]);
var user = await db.all('SELECT * from post WHERE id = ?', [
id
]) || [];
if (!user[0])
return {success: 'fail' };
await updateUser({user: user[0].username});
return {data: {up,down}};
}
backend.token = async ({cookies}) => {
var tokenIn = cookies.get('token');
var existingAccounts = await db.all('SELECT username from token WHERE token = ?',[
tokenIn
]);
if (!existingAccounts || existingAccounts.length < 1)
return false;
return {data: existingAccounts[0].username};
}
backend.fileCreate = async({img, extension}) => {
const imgHash = createHash('md5').update(img).digest('hex');
let lengthCheck = checkLength(img,'Image',fileSizeLimit);
if (lengthCheck)
return lengthCheck;
const extensionSafe = extension.replace(/(\s+)/g, '\\$1');
if (extensionSafe != 'png' && extensionSafe != 'jpg' && extensionSafe != 'svg' || extensionSafe != 'gif')
return { success: 'Illegal file extension.' };
writeFile(`${process.cwd()}/db/post-${imgHash}.${extensionSafe}`,img,{encoding: 'base64'});
return { success: 'Successfully uploaded file.', 'href': `/img/${imgHash}.${extensionSafe}`};
}
export {
backendProxy,
backend
}