A lot of stuff
This commit is contained in:
parent
98b2c285f4
commit
a1c9b30cf6
9 changed files with 439 additions and 421 deletions
|
@ -20,7 +20,8 @@
|
||||||
#header {
|
#header {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
font-size: 2rem;
|
font-size: 1rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#logo {
|
#logo {
|
||||||
|
|
||||||
width: calc(100vw + 50px);
|
width: calc(100vw - 10px);
|
||||||
|
|
||||||
background: var(--dark-1);
|
background: var(--dark-1);
|
||||||
|
|
||||||
|
|
|
@ -55,8 +55,11 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
.vote-area {
|
|
||||||
margin-right: 30px;
|
|
||||||
|
#header-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
|
@ -70,7 +73,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.date {
|
.date {
|
||||||
margin-left: 1rem;
|
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
|
@ -93,12 +95,14 @@
|
||||||
<Area>
|
<Area>
|
||||||
<span slot="header" id='header'>
|
<span slot="header" id='header'>
|
||||||
<img class='pfp' src='/pfp/{username}.png'/>
|
<img class='pfp' src='/pfp/{username}.png'/>
|
||||||
<a href='/users/{username}'>
|
<div class='header-area'>
|
||||||
|
<div><a href='/users/{username}'>
|
||||||
{username}
|
{username}
|
||||||
</a>
|
</a></div>
|
||||||
<span class='date'>
|
<div class='date'>
|
||||||
{date}
|
{date}
|
||||||
</span>
|
</div>
|
||||||
|
</div>
|
||||||
</span>
|
</span>
|
||||||
<span slot="main">
|
<span slot="main">
|
||||||
<PostBody content={content} />
|
<PostBody content={content} />
|
||||||
|
|
420
src/lib/db/db.js
420
src/lib/db/db.js
|
@ -1,4 +1,4 @@
|
||||||
const ROW_COUNT = 5;
|
import { backend } from './handlers.js';
|
||||||
|
|
||||||
const AUTH_ACTIONS = [
|
const AUTH_ACTIONS = [
|
||||||
'postCreate',
|
'postCreate',
|
||||||
|
@ -9,32 +9,11 @@ const AUTH_ACTIONS = [
|
||||||
'follow'
|
'follow'
|
||||||
];
|
];
|
||||||
|
|
||||||
const LEGAL_SORTS = {
|
|
||||||
'time': 'time',
|
|
||||||
'rating': 'rating',
|
|
||||||
'hot': `rating / (%d - time + 24000)`
|
|
||||||
}
|
|
||||||
|
|
||||||
const roles = [
|
|
||||||
'Owner',
|
|
||||||
'Admin',
|
|
||||||
'Veteran'
|
|
||||||
]
|
|
||||||
|
|
||||||
const FILE_SIZE_LIMIT = 1024*1024*16;
|
|
||||||
|
|
||||||
const VALID_EXTENSIONS = ['png','jpg','jpeg','gif','svg', 'mp4'];
|
|
||||||
|
|
||||||
var ridArray = {};
|
|
||||||
|
|
||||||
import sqlite3 from 'sqlite3'
|
import sqlite3 from 'sqlite3'
|
||||||
import { open } from 'sqlite'
|
import { open } from 'sqlite'
|
||||||
import { hash, compare } from 'bcrypt'
|
|
||||||
import { randomBytes, createHash } from 'node:crypto';
|
|
||||||
import { writeFile } from 'node:fs/promises';
|
|
||||||
import { calcVote, checkLength, checkRegex, safePath, formatPost } from '../util.js';
|
|
||||||
|
|
||||||
var db;
|
var db;
|
||||||
|
|
||||||
async function initDb() {
|
async function initDb() {
|
||||||
db = await open({
|
db = await open({
|
||||||
filename: `${process.cwd()}/db/main.db`,
|
filename: `${process.cwd()}/db/main.db`,
|
||||||
|
@ -53,392 +32,25 @@ async function initDb() {
|
||||||
let backendProxy = async ({route, backendParams}) => {
|
let backendProxy = async ({route, backendParams}) => {
|
||||||
if (!db) await initDb();
|
if (!db) await initDb();
|
||||||
|
|
||||||
var user = ((await backend.token({cookies: backendParams.cookies})) || {}).data;
|
let extraParams = {};
|
||||||
|
|
||||||
|
extraParams['db'] = db;
|
||||||
|
|
||||||
|
let user = (await backend.token({cookies: backendParams.cookies},extraParams)) || {};
|
||||||
|
|
||||||
|
user = user.data;
|
||||||
|
|
||||||
if ((!user || user == '') && AUTH_ACTIONS.indexOf(route) != -1) return {'success': 'Not authorized.' };
|
if ((!user || user == '') && AUTH_ACTIONS.indexOf(route) != -1) return {'success': 'Not authorized.' };
|
||||||
3
|
3
|
||||||
var isAdmin = false;
|
let isAdmin = false;
|
||||||
if (user && user != '') isAdmin = ((await userRoles({user})) || []).indexOf('Admin') != -1;
|
if (user && user != '') isAdmin = ((await backend.userRoles({}, {user, db})) || []).indexOf('Admin') != -1;
|
||||||
|
|
||||||
backendParams['admin'] = isAdmin;
|
extraParams['admin'] = isAdmin;
|
||||||
|
extraParams['user'] = user;
|
||||||
|
|
||||||
if (AUTH_ACTIONS.indexOf(route) != -1) backendParams['user'] = user;
|
return backend[route](backendParams, extraParams) || {};
|
||||||
|
|
||||||
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;
|
|
||||||
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,
|
|
||||||
calcVote(upvotes,downvotes,'user')
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
])
|
|
||||||
|
|
||||||
await updateUser({user: user});
|
|
||||||
|
|
||||||
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}) => {
|
|
||||||
if (!content) return {'success': 'No post provided.'}
|
|
||||||
|
|
||||||
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');
|
|
||||||
|
|
||||||
var postFlatten = formatPost(content).flat();
|
|
||||||
var reply = postFlatten[postFlatten.findIndex(x => x.subtype == 'post')];
|
|
||||||
|
|
||||||
if (reply)
|
|
||||||
reply = reply.url.split('/').pop();
|
|
||||||
|
|
||||||
await db.run('INSERT INTO post (username, id, content, rating, reply, time) VALUES (?, ?, ?, ?, ?, ?)', [
|
|
||||||
user,
|
|
||||||
id,
|
|
||||||
content,
|
|
||||||
calcVote(0,0),
|
|
||||||
reply || '',
|
|
||||||
Math.floor(new Date() * 1000)
|
|
||||||
])
|
|
||||||
|
|
||||||
return {'success': 'Your post has been broadcasted!', 'href': `/post/${id}` };
|
|
||||||
}
|
|
||||||
|
|
||||||
backend.postDelete = async ({id, user, admin}) => {
|
|
||||||
if (admin) {
|
|
||||||
await db.run('DELETE FROM post WHERE id = ?', [
|
|
||||||
id
|
|
||||||
])
|
|
||||||
} else {
|
|
||||||
await db.run('DELETE FROM post WHERE username = ? AND id = ?', [
|
|
||||||
user,
|
|
||||||
id
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
return {'success': 'Your post has been deleted!', 'href': `/post/${id}` };
|
|
||||||
}
|
|
||||||
|
|
||||||
let userRoles = async ({user}) => {
|
|
||||||
var rolesLocal = await db.all('SELECT roles from bio WHERE username = ?', [
|
|
||||||
user
|
|
||||||
] );
|
|
||||||
|
|
||||||
if (rolesLocal.length == 0) rolesLocal = [{}];
|
|
||||||
|
|
||||||
let rolesLocalList = rolesLocal[0].roles;
|
|
||||||
|
|
||||||
return roles.filter((elem,i) => ((rolesLocalList % (1<<(i+1))) > ((1<<i) - 1)) );
|
|
||||||
};
|
|
||||||
|
|
||||||
backend.userGet = async ({user}) => {
|
|
||||||
var posts = await db.all('SELECT * from user WHERE username = ?', [
|
|
||||||
user
|
|
||||||
])
|
|
||||||
|
|
||||||
if (!posts || posts.length < 1) {
|
|
||||||
return {'success': 'User does not exist.'}
|
|
||||||
}
|
|
||||||
|
|
||||||
var following = await db.all('SELECT * from follow WHERE username = ?', [
|
|
||||||
user
|
|
||||||
]);
|
|
||||||
|
|
||||||
var followers = await db.all('SELECT * from follow WHERE following = ?', [
|
|
||||||
user
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!following) following = [];
|
|
||||||
|
|
||||||
if (!followers) followers = [];
|
|
||||||
|
|
||||||
return {data: posts[0], following, followers};
|
|
||||||
}
|
|
||||||
|
|
||||||
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.'}
|
|
||||||
}
|
|
||||||
|
|
||||||
posts[0].rolesArr = (await userRoles({user})) || [];
|
|
||||||
|
|
||||||
return {data: posts[0]};
|
|
||||||
}
|
|
||||||
|
|
||||||
backend.postBulk = async ({page, id, user, cookies, sort, type, admin}) => {
|
|
||||||
var posts;
|
|
||||||
|
|
||||||
var userAuth = (await backend.token({cookies})).data || '';
|
|
||||||
|
|
||||||
sort = (LEGAL_SORTS[sort]) || 'rating';
|
|
||||||
|
|
||||||
if (sort + '' != sort) sort = 'rating';
|
|
||||||
|
|
||||||
sort = sort.replaceAll('%d',Math.floor(new Date() * 1000));
|
|
||||||
|
|
||||||
|
|
||||||
if (type == 'all') {
|
|
||||||
posts = await db.all('SELECT * from post ORDER BY '+sort+' DESC LIMIT ?, ?', [
|
|
||||||
page*ROW_COUNT,
|
|
||||||
ROW_COUNT
|
|
||||||
])
|
|
||||||
} else if (type == 'post') {
|
|
||||||
posts = await db.all('SELECT * from post WHERE id = ?', [
|
|
||||||
id
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (posts.length == 0) posts.push({});
|
|
||||||
|
|
||||||
posts.push(...(await db.all('SELECT * from post WHERE reply = ? ORDER BY '+sort+' DESC LIMIT ?, ?', [
|
|
||||||
id,
|
|
||||||
page*ROW_COUNT,
|
|
||||||
ROW_COUNT
|
|
||||||
])))
|
|
||||||
|
|
||||||
} else if (type == 'user') {
|
|
||||||
posts = await db.all('SELECT * from post WHERE username = ? ORDER BY '+sort+' DESC LIMIT ?, ?', [
|
|
||||||
user,
|
|
||||||
page*ROW_COUNT,
|
|
||||||
ROW_COUNT
|
|
||||||
])
|
|
||||||
} else if (type == 'follow') {
|
|
||||||
posts = await db.all('SELECT * from post WHERE username IN (SELECT following from follow WHERE username = ?) ORDER BY '+sort+' DESC LIMIT ?, ?', [
|
|
||||||
userAuth,
|
|
||||||
page*ROW_COUNT,
|
|
||||||
ROW_COUNT
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
posts = posts.map(post => {
|
|
||||||
return {...post, isAuthor: userAuth == post.username || admin};
|
|
||||||
})
|
|
||||||
|
|
||||||
return {data: posts};
|
|
||||||
}
|
|
||||||
|
|
||||||
backend.vote = async ({id, vote, user}) => {
|
|
||||||
if (!id || (vote != 'down' && vote != 'up')) return {success: 'fail' };
|
|
||||||
|
|
||||||
var isCreator = (await db.all('SELECT * from post WHERE id = ?', [
|
|
||||||
id
|
|
||||||
]))[0].username == user;
|
|
||||||
|
|
||||||
if (isCreator)
|
|
||||||
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.follow = async ({target, user}) => {
|
|
||||||
var isFollowing = await db.all('SELECT * FROM follow WHERE username = ? AND following = ?',[
|
|
||||||
user,
|
|
||||||
target
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (isFollowing && isFollowing.length > 0) {
|
|
||||||
await db.run('DELETE FROM follow WHERE username = ? AND following = ?',[
|
|
||||||
user,
|
|
||||||
target
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
await db.run('INSERT INTO follow (username, following) VALUES (?, ?)',[
|
|
||||||
user,
|
|
||||||
target
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var following = await db.all('SELECT * from follow WHERE username = ?', [
|
|
||||||
target
|
|
||||||
]);
|
|
||||||
|
|
||||||
var followers = await db.all('SELECT * from follow WHERE following = ?', [
|
|
||||||
target
|
|
||||||
]);
|
|
||||||
|
|
||||||
return {'success': 'User followed/unfollowed.', 'data': {following, followers}};
|
|
||||||
};
|
|
||||||
|
|
||||||
let fileCreate = (type) => {
|
|
||||||
return async ({img, extension,id, last, user }) => {
|
|
||||||
let validExtensions = VALID_EXTENSIONS;
|
|
||||||
|
|
||||||
if (type == 'pfp') validExtensions = ['png'];
|
|
||||||
|
|
||||||
if (ridArray[id] !== '' && !(ridArray[id])) {
|
|
||||||
ridArray[id] = img;
|
|
||||||
} else {
|
|
||||||
ridArray[id] += img;
|
|
||||||
}
|
|
||||||
|
|
||||||
const imgData = ridArray[id];
|
|
||||||
|
|
||||||
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 > FILE_SIZE_LIMIT)
|
|
||||||
return {'success': 'Image too big.'}
|
|
||||||
|
|
||||||
const extensionSafe = safePath(extension);
|
|
||||||
|
|
||||||
if (validExtensions.indexOf(extensionSafe) == -1)
|
|
||||||
return { success: 'Illegal file extension. Permitted file extensions are: ' + validExtensions.join(', ') };
|
|
||||||
|
|
||||||
if (type == 'post') {
|
|
||||||
writeFile(`${process.cwd()}/db/post-${imgHash}.${extensionSafe}`,imgData,{encoding: 'base64'});
|
|
||||||
} else {
|
|
||||||
writeFile(`${process.cwd()}/db/pfp-${user}.png`,imgData,{encoding: 'base64'});
|
|
||||||
}
|
|
||||||
|
|
||||||
return { success: 'Successfully uploaded file.', 'href': `/img/${imgHash}.${extensionSafe}`};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
backend.fileCreate = fileCreate('post');
|
|
||||||
backend.pfp = fileCreate('pfp');
|
|
||||||
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
backendProxy,
|
backendProxy
|
||||||
backend,
|
};
|
||||||
VALID_EXTENSIONS
|
|
||||||
}
|
|
401
src/lib/db/handlers.js
Normal file
401
src/lib/db/handlers.js
Normal file
|
@ -0,0 +1,401 @@
|
||||||
|
const ROW_COUNT = 5;
|
||||||
|
|
||||||
|
const LEGAL_SORTS = {
|
||||||
|
'time': 'time',
|
||||||
|
'rating': 'rating',
|
||||||
|
'hot': `rating / (%d - time + 24000)`
|
||||||
|
}
|
||||||
|
|
||||||
|
const roles = [
|
||||||
|
'Owner',
|
||||||
|
'Admin',
|
||||||
|
'Veteran'
|
||||||
|
]
|
||||||
|
|
||||||
|
const FILE_SIZE_LIMIT = 1024*1024*16;
|
||||||
|
|
||||||
|
const VALID_EXTENSIONS = ['png','jpg','jpeg','gif','svg', 'mp4'];
|
||||||
|
|
||||||
|
|
||||||
|
import { hash, compare } from 'bcrypt'
|
||||||
|
import { randomBytes, createHash } from 'node:crypto';
|
||||||
|
import { writeFile } from 'node:fs/promises';
|
||||||
|
import { calcVote, checkLength, checkRegex, safePath, formatPost } from '../util.js';
|
||||||
|
|
||||||
|
var ridArray = {};
|
||||||
|
|
||||||
|
let updateUser = async ({user},{db}) => {
|
||||||
|
let allPosts = await db.all('SELECT * from post WHERE username = ?', [
|
||||||
|
user
|
||||||
|
]);
|
||||||
|
|
||||||
|
let upvotes = 0;
|
||||||
|
let downvotes = 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,
|
||||||
|
calcVote(upvotes,downvotes,'user')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileCreate = (type) => {
|
||||||
|
return async ({img, extension,id, last}, {user}) => {
|
||||||
|
let validExtensions = VALID_EXTENSIONS;
|
||||||
|
|
||||||
|
if (type == 'pfp') validExtensions = ['png'];
|
||||||
|
|
||||||
|
if (ridArray[id] !== '' && !(ridArray[id])) {
|
||||||
|
ridArray[id] = img;
|
||||||
|
} else {
|
||||||
|
ridArray[id] += img;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imgData = ridArray[id];
|
||||||
|
|
||||||
|
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 > FILE_SIZE_LIMIT)
|
||||||
|
return {'success': 'Image too big.'}
|
||||||
|
|
||||||
|
const extensionSafe = safePath(extension);
|
||||||
|
|
||||||
|
if (validExtensions.indexOf(extensionSafe) == -1)
|
||||||
|
return { success: 'Illegal file extension. Permitted file extensions are: ' + validExtensions.join(', ') };
|
||||||
|
|
||||||
|
let fileName = (type == 'post') ? `post-${imgHash}.${extensionSafe}` : `pfp-${user}.png`
|
||||||
|
|
||||||
|
writeFile(`${process.cwd()}/db/${fileName}`,imgData,{encoding: 'base64'});
|
||||||
|
|
||||||
|
return { success: 'Successfully uploaded file.', 'href': `/img/${imgHash}.${extensionSafe}`};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var backend = {};
|
||||||
|
|
||||||
|
backend.fileCreate = fileCreate('post');
|
||||||
|
backend.pfp = fileCreate('pfp');
|
||||||
|
|
||||||
|
backend.userRoles = async ({user},{db}) => {
|
||||||
|
var rolesLocal = await db.all('SELECT roles from bio WHERE username = ?', [
|
||||||
|
user
|
||||||
|
] );
|
||||||
|
|
||||||
|
if (rolesLocal.length == 0) rolesLocal = [{}];
|
||||||
|
|
||||||
|
let rolesLocalList = rolesLocal[0].roles;
|
||||||
|
|
||||||
|
return roles.filter((elem,i) => ((rolesLocalList % (1<<(i+1))) > ((1<<i) - 1)) );
|
||||||
|
};
|
||||||
|
|
||||||
|
backend.register = async ({user, pass, pass2},{db}) => {
|
||||||
|
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
|
||||||
|
])
|
||||||
|
|
||||||
|
await updateUser({user: user});
|
||||||
|
|
||||||
|
return { success: 'Successfully created account.', location: '/'};
|
||||||
|
}
|
||||||
|
|
||||||
|
backend.login = async ({user, pass, cookies},{db}) => {
|
||||||
|
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,db}) => {
|
||||||
|
if (!content) return {'success': 'No post provided.'}
|
||||||
|
|
||||||
|
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');
|
||||||
|
|
||||||
|
var postFlatten = formatPost(content).flat();
|
||||||
|
var reply = postFlatten[postFlatten.findIndex(x => x.subtype == 'post')];
|
||||||
|
|
||||||
|
if (reply)
|
||||||
|
reply = reply.url.split('/').pop();
|
||||||
|
|
||||||
|
await db.run('INSERT INTO post (username, id, content, rating, reply, time) VALUES (?, ?, ?, ?, ?, ?)', [
|
||||||
|
user,
|
||||||
|
id,
|
||||||
|
content,
|
||||||
|
calcVote(0,0),
|
||||||
|
reply || '',
|
||||||
|
Math.floor(new Date() * 1000)
|
||||||
|
])
|
||||||
|
|
||||||
|
return {'success': 'Your post has been broadcasted!', 'href': `/post/${id}` };
|
||||||
|
}
|
||||||
|
|
||||||
|
backend.postDelete = async ({id}, {user, admin, db}) => {
|
||||||
|
if (admin) {
|
||||||
|
await db.run('DELETE FROM post WHERE id = ?', [
|
||||||
|
id
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
await db.run('DELETE FROM post WHERE username = ? AND id = ?', [
|
||||||
|
user,
|
||||||
|
id
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
return {'success': 'Your post has been deleted!', 'href': `/post/${id}` };
|
||||||
|
}
|
||||||
|
|
||||||
|
backend.userGet = async ({user},{db}) => {
|
||||||
|
var posts = await db.all('SELECT * from user WHERE username = ?', [
|
||||||
|
user
|
||||||
|
])
|
||||||
|
|
||||||
|
if (!posts || posts.length < 1) {
|
||||||
|
return {'success': 'User does not exist.'}
|
||||||
|
}
|
||||||
|
|
||||||
|
var following = await db.all('SELECT * from follow WHERE username = ?', [
|
||||||
|
user
|
||||||
|
]);
|
||||||
|
|
||||||
|
var followers = await db.all('SELECT * from follow WHERE following = ?', [
|
||||||
|
user
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!following) following = [];
|
||||||
|
|
||||||
|
if (!followers) followers = [];
|
||||||
|
|
||||||
|
return {data: posts[0], following, followers};
|
||||||
|
}
|
||||||
|
|
||||||
|
backend.userBio = async ({user},{db}) => {
|
||||||
|
var posts = await db.all('SELECT * from bio WHERE username = ?', [
|
||||||
|
user
|
||||||
|
])
|
||||||
|
|
||||||
|
if (!posts || posts.length < 1) {
|
||||||
|
return {'success': 'Bio does not exist.'}
|
||||||
|
}
|
||||||
|
|
||||||
|
posts[0].rolesArr = (await userRoles({user})) || [];
|
||||||
|
|
||||||
|
return {data: posts[0]};
|
||||||
|
}
|
||||||
|
|
||||||
|
backend.postBulk = async ({page, id, user, cookies, sort, type}, {admin, db}) => {
|
||||||
|
var posts;
|
||||||
|
|
||||||
|
var userAuth = (await backend.token({cookies}, {db})).data || '';
|
||||||
|
|
||||||
|
sort = (LEGAL_SORTS[sort]) || 'rating';
|
||||||
|
|
||||||
|
if (sort + '' != sort) sort = 'rating';
|
||||||
|
|
||||||
|
sort = sort.replaceAll('%d',Math.floor(new Date() * 1000));
|
||||||
|
|
||||||
|
|
||||||
|
if (type == 'all') {
|
||||||
|
posts = await db.all('SELECT * from post ORDER BY '+sort+' DESC LIMIT ?, ?', [
|
||||||
|
page*ROW_COUNT,
|
||||||
|
ROW_COUNT
|
||||||
|
])
|
||||||
|
} else if (type == 'post') {
|
||||||
|
posts = await db.all('SELECT * from post WHERE id = ?', [
|
||||||
|
id
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (posts.length == 0) posts.push({});
|
||||||
|
|
||||||
|
posts.push(...(await db.all('SELECT * from post WHERE reply = ? ORDER BY '+sort+' DESC LIMIT ?, ?', [
|
||||||
|
id,
|
||||||
|
page*ROW_COUNT,
|
||||||
|
ROW_COUNT
|
||||||
|
])))
|
||||||
|
|
||||||
|
} else if (type == 'user') {
|
||||||
|
posts = await db.all('SELECT * from post WHERE username = ? ORDER BY '+sort+' DESC LIMIT ?, ?', [
|
||||||
|
user,
|
||||||
|
page*ROW_COUNT,
|
||||||
|
ROW_COUNT
|
||||||
|
])
|
||||||
|
} else if (type == 'follow') {
|
||||||
|
posts = await db.all('SELECT * from post WHERE username IN (SELECT following from follow WHERE username = ?) ORDER BY '+sort+' DESC LIMIT ?, ?', [
|
||||||
|
userAuth,
|
||||||
|
page*ROW_COUNT,
|
||||||
|
ROW_COUNT
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
posts = posts.map(post => {
|
||||||
|
return {...post, isAuthor: userAuth == post.username || admin};
|
||||||
|
})
|
||||||
|
|
||||||
|
return {data: posts};
|
||||||
|
}
|
||||||
|
|
||||||
|
backend.vote = async ({id, vote}, {user, db}) => {
|
||||||
|
if (!id || (vote != 'down' && vote != 'up')) return {success: 'fail' };
|
||||||
|
|
||||||
|
var isCreator = (await db.all('SELECT * from post WHERE id = ?', [
|
||||||
|
id
|
||||||
|
]))[0].username == user;
|
||||||
|
|
||||||
|
if (isCreator)
|
||||||
|
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}, {db}) => {
|
||||||
|
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.follow = async ({target}, {user, db}) => {
|
||||||
|
var isFollowing = await db.all('SELECT * FROM follow WHERE username = ? AND following = ?',[
|
||||||
|
user,
|
||||||
|
target
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (isFollowing && isFollowing.length > 0) {
|
||||||
|
await db.run('DELETE FROM follow WHERE username = ? AND following = ?',[
|
||||||
|
user,
|
||||||
|
target
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
await db.run('INSERT INTO follow (username, following) VALUES (?, ?)',[
|
||||||
|
user,
|
||||||
|
target
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var following = await db.all('SELECT * from follow WHERE username = ?', [
|
||||||
|
target
|
||||||
|
]);
|
||||||
|
|
||||||
|
var followers = await db.all('SELECT * from follow WHERE following = ?', [
|
||||||
|
target
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {'success': 'User followed/unfollowed.', 'data': {following, followers}};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export {
|
||||||
|
backend,
|
||||||
|
VALID_EXTENSIONS
|
||||||
|
}
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
padding: 25px;
|
|
||||||
padding-top: 0px;
|
padding-top: 0px;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -8,9 +8,9 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Area handleSubmit=''>
|
<Area handleSubmit=''>
|
||||||
<p slot="header">
|
<span slot="header">
|
||||||
Log in
|
Log in
|
||||||
</p>
|
</span>
|
||||||
|
|
||||||
<span slot='main'>
|
<span slot='main'>
|
||||||
<h2>Login</h2>
|
<h2>Login</h2>
|
||||||
|
|
|
@ -8,9 +8,9 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Area handleSubmit=''>
|
<Area handleSubmit=''>
|
||||||
<p slot="header">
|
<span slot="header">
|
||||||
Register
|
Register
|
||||||
</p>
|
</span>
|
||||||
|
|
||||||
<span slot='main'>
|
<span slot='main'>
|
||||||
<h2>Register</h2>
|
<h2>Register</h2>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { backend, backendProxy } from '../../../lib/db/db.js';
|
import { backend } from '../../../lib/db/handlers.js';
|
||||||
|
import { backendProxy } from '../../../lib/db/db.js';
|
||||||
|
|
||||||
|
|
||||||
/** @type {import('./$types').RequestHandler} */
|
/** @type {import('./$types').RequestHandler} */
|
||||||
|
|
Loading…
Reference in a new issue