2023-02-05 09:50:49 -05:00
const rowCount = 5 ;
2023-02-07 18:06:18 -05:00
const AUTH _ACTIONS = [
'postCreate' ,
2023-02-07 19:36:32 -05:00
'fileCreate' ,
2023-02-11 02:11:45 -05:00
'vote' ,
'postDelete'
2023-02-07 18:06:18 -05:00
] ;
2023-02-10 23:44:25 -05:00
const fileSizeLimit = 1024 * 1024 * 16 ;
var ridArray = { } ;
2023-02-10 19:12:57 -05:00
2023-02-05 09:50:49 -05:00
import sqlite3 from 'sqlite3'
import { open } from 'sqlite'
import { hash , compare } from 'bcrypt'
2023-02-07 19:36:32 -05:00
import { randomBytes , createHash } from 'node:crypto' ;
import { readFile , writeFile } from 'node:fs/promises' ;
2023-02-07 18:06:18 -05:00
import { calcVote , calcVoteUser , checkLength , checkRegex } from '../util.js' ;
2023-02-05 09:50:49 -05:00
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)' ) ;
2023-02-11 19:42:10 -05:00
await db . run ( 'CREATE TABLE IF NOT EXISTS user (username CHAR(64), followers INTEGER, following INTEGER, upvotes INTEGER, downvotes INTEGER, reputation REAL)' ) ;
await db . run ( 'CREATE TABLE IF NOT EXISTS bio (username CHAR(64), content CHAR(10240), roles INTEGER)' ) ;
2023-02-05 09:50:49 -05:00
}
2023-02-07 18:06:18 -05:00
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 ) ;
}
2023-02-05 09:50:49 -05:00
var backend = { } ;
2023-02-07 18:06:18 -05:00
let updateUser = async ( { user } ) => {
2023-02-05 09:50:49 -05:00
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
] )
2023-02-11 02:11:45 -05:00
await updateUser ( { user : user [ 0 ] . username } ) ;
2023-02-05 09:50:49 -05:00
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 : '/' } ;
}
2023-02-07 19:36:32 -05:00
backend . postCreate = async ( { content , user } ) => {
2023-02-10 23:44:25 -05:00
if ( ! content ) return { 'success' : 'No post provided.' }
2023-02-05 09:50:49 -05:00
var lengthCheck = checkLength ( content , 'Post content' , 1 , 10240 ) ;
if ( lengthCheck )
return lengthCheck ;
2023-02-07 18:06:18 -05:00
if ( ! content ) return { 'success' : 'There is no post!' } ;
2023-02-05 09:50:49 -05:00
var id = randomBytes ( 10 ) . toString ( 'hex' ) ;
await db . run ( 'INSERT INTO post (username, id, content, rating) VALUES (?, ?, ?, ?)' , [
user ,
id ,
content ,
calcVote ( 0 , 0 )
] )
2023-02-07 19:36:32 -05:00
return { 'success' : 'Your post has been broadcasted!' , 'href' : ` /post/ ${ id } ` } ;
2023-02-05 09:50:49 -05:00
}
2023-02-11 02:11:45 -05:00
backend . postDelete = async ( { id , user } ) => {
await db . run ( 'DELETE FROM post WHERE username = ? AND id = ?' , [
user ,
id
] )
return { 'success' : 'Your post has been deleted!' , 'href' : ` /post/ ${ id } ` } ;
}
backend . postGet = async ( { id , cookies } ) => {
2023-02-05 09:50:49 -05:00
var posts = await db . all ( 'SELECT * from post WHERE id = ?' , [
id
] )
if ( ! posts || posts . length < 1 ) {
return { 'success' : 'Post does not exist.' }
}
2023-02-11 02:11:45 -05:00
var user = ( await backend . token ( { cookies } ) ) . data ;
return { data : posts [ 0 ] , isAuthor : posts [ 0 ] . username == user } ;
2023-02-05 09:50:49 -05:00
}
backend . userGet = async ( { user } ) => {
var posts = await db . all ( 'SELECT * from user WHERE username = ?' , [
user
] )
if ( ! posts || posts . length < 1 ) {
2023-02-11 19:42:10 -05:00
return { 'success' : 'User does not exist.' }
}
return { data : posts [ 0 ] } ;
}
backend . userBio = async ( { user } ) => {
var posts = await db . all ( 'SELECT * from bio WHERE username = ?' , [
user
] )
if ( ! posts || posts . length < 1 ) {
return { 'success' : 'Bio does not exist.' }
2023-02-05 09:50:49 -05:00
}
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 } ;
}
2023-02-07 18:06:18 -05:00
backend . vote = async ( { cookies , id , vote , user } ) => {
if ( ! id || ( vote != 'down' && vote != 'up' ) ) return { success : 'fail' } ;
2023-02-05 09:50:49 -05:00
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 } ;
}
2023-02-10 23:44:25 -05:00
backend . fileCreate = async ( { img , extension , id , last } ) => {
if ( ridArray [ id ] !== '' && ! ( ridArray [ id ] ) ) {
ridArray [ id ] = img ;
} else {
ridArray [ id ] += img ;
}
2023-02-07 19:36:32 -05:00
2023-02-10 23:44:25 -05:00
const imgData = ridArray [ id ] ;
2023-02-07 19:36:32 -05:00
2023-02-10 23:44:25 -05:00
if ( last != 'true' ) {
return { 'success' : 'Image still proccessing...' }
} else {
ridArray [ id ] = false ;
}
const imgHash = createHash ( 'md5' ) . update ( imgData ) . digest ( 'hex' ) ;
if ( ! imgHash )
return { 'success' : 'Image not provided.' }
if ( imgHash . length > fileSizeLimit )
return { 'success' : 'Image too big.' }
2023-02-07 19:36:32 -05:00
2023-02-10 23:44:25 -05:00
const extensionSafe = extension . replace ( /[^a-zA-Z]+/g , '\\$1' ) ;
2023-02-07 19:36:32 -05:00
2023-02-10 23:44:25 -05:00
if ( extensionSafe != 'png' && extensionSafe != 'jpg' && extensionSafe != 'svg' && extensionSafe != 'gif' )
2023-02-07 19:36:32 -05:00
return { success : 'Illegal file extension.' } ;
2023-02-10 23:44:25 -05:00
writeFile ( ` ${ process . cwd ( ) } /db/post- ${ imgHash } . ${ extensionSafe } ` , imgData , { encoding : 'base64' } ) ;
2023-02-07 19:36:32 -05:00
return { success : 'Successfully uploaded file.' , 'href' : ` /img/ ${ imgHash } . ${ extensionSafe } ` } ;
}
2023-02-05 09:50:49 -05:00
export {
2023-02-07 18:06:18 -05:00
backendProxy ,
2023-02-05 09:50:49 -05:00
backend
}