A lot of stuff

This commit is contained in:
tdgmdev 2023-03-09 19:12:17 -05:00
parent a1c9b30cf6
commit ad969e5807
13 changed files with 227 additions and 167 deletions

1
.gitignore vendored
View file

@ -9,3 +9,4 @@ node_modules
vite.config.js.timestamp-* vite.config.js.timestamp-*
vite.config.ts.timestamp-* vite.config.ts.timestamp-*
/db /db
/db-old

View file

@ -29,7 +29,6 @@
} }
#header { #header {
border-bottom: var(--dark-2) solid 2px;
width: calc(100% - 30px); width: calc(100% - 30px);
} }

View file

@ -0,0 +1,28 @@
<script>
import Area from '$lib/components/Area.svelte';
import { handleSubmit } from '$lib/util.js';
export let form = {};
export let action = '/';
export let name = 'Empty form';
let submitFunc = async e => form = JSON.parse(await handleSubmit(e))
</script>
<Area handleSubmit=''>
<span slot="header">
{name}
</span>
<span slot='main'>
<form action={action} on:submit|preventDefault={submitFunc} method='POST'>
<slot></slot>
</form>
</span>
<p slot="footer">
{#if form?.success}
<p>{form?.success}</p>
{/if}
By using the Sanifae service, you agree to the <a href='/tos'>Terms of Service</a>.
</p>
</Area>

View file

@ -70,6 +70,7 @@
width: 50px; width: 50px;
height: 50px; height: 50px;
margin-right: 10px; margin-right: 10px;
border-radius: 100%;
} }
.date { .date {
@ -94,7 +95,7 @@
{:else} {:else}
<Area> <Area>
<span slot="header" id='header'> <span slot="header" id='header'>
<img class='pfp' src='/pfp/{username}.png'/> <img class='pfp' src='/img/pfp/{username}.png'/>
<div class='header-area'> <div class='header-area'>
<div><a href='/users/{username}'> <div><a href='/users/{username}'>
{username} {username}

View file

@ -1,4 +1,5 @@
import { backend } from './handlers.js'; import { backend } from './handlers.js';
import { mkdir, access } from 'node:fs/promises';
const AUTH_ACTIONS = [ const AUTH_ACTIONS = [
'postCreate', 'postCreate',
@ -9,24 +10,79 @@ const AUTH_ACTIONS = [
'follow' 'follow'
]; ];
const FILE_DIRS = [
'/db',
'/db/files/upload',
'/db/files/pfp'
]
import sqlite3 from 'sqlite3' import sqlite3 from 'sqlite3'
import { open } from 'sqlite' import { open } from 'sqlite'
var db; var db;
async function newDir(dir) {
await access(newDir)
.then(() => {})
.catch(async () => await mkdir(dir, { recursive: true }));
}
async function initFolders() {
for (var i = 0; i < FILE_DIRS.length; i++) {
await newDir( `${process.cwd()}/${FILE_DIRS[i]}`);
}
}
async function initDb() { async function initDb() {
await initFolders();
db = await open({ db = await open({
filename: `${process.cwd()}/db/main.db`, filename: `${process.cwd()}/db/main.db`,
driver: sqlite3.Database 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 auth ( \
await db.run('CREATE TABLE IF NOT EXISTS token (username CHAR(64), token CHAR(1024))'); username CHAR(64), \
await db.run('CREATE TABLE IF NOT EXISTS post (username CHAR(64), id CHAR(64), content CHAR(10240), upvotes INTEGER, downvotes INTEGER, rating REAL, reply CHAR(64), time INTEGER)'); password CHAR(1024) \
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)');
await db.run('CREATE TABLE IF NOT EXISTS bio (username CHAR(64), content CHAR(10240), roles INTEGER)'); await db.run('CREATE TABLE IF NOT EXISTS token ( \
await db.run('CREATE TABLE IF NOT EXISTS follow (username CHAR(64), following CHAR(64))'); 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, \
reply CHAR(64), \
time INTEGER \
)');
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, \
roles INTEGER, \
pinned CHAR(64) \
)');
await db.run('CREATE TABLE IF NOT EXISTS follow (\
username CHAR(64), \
following CHAR(64) \
)');
} }
let backendProxy = async ({route, backendParams}) => { let backendProxy = async ({route, backendParams}) => {

View file

@ -84,9 +84,9 @@ let fileCreate = (type) => {
if (validExtensions.indexOf(extensionSafe) == -1) if (validExtensions.indexOf(extensionSafe) == -1)
return { success: 'Illegal file extension. Permitted file extensions are: ' + validExtensions.join(', ') }; return { success: 'Illegal file extension. Permitted file extensions are: ' + validExtensions.join(', ') };
let fileName = (type == 'post') ? `post-${imgHash}.${extensionSafe}` : `pfp-${user}.png` let fileName = (type == 'post') ? `upload/${imgHash}.${extensionSafe}` : `pfp/${user}.png`
writeFile(`${process.cwd()}/db/${fileName}`,imgData,{encoding: 'base64'}); writeFile(`${process.cwd()}/db/files/${fileName}`,imgData,{encoding: 'base64'});
return { success: 'Successfully uploaded file.', 'href': `/img/${imgHash}.${extensionSafe}`}; return { success: 'Successfully uploaded file.', 'href': `/img/${imgHash}.${extensionSafe}`};
} }
@ -98,7 +98,7 @@ backend.fileCreate = fileCreate('post');
backend.pfp = fileCreate('pfp'); backend.pfp = fileCreate('pfp');
backend.userRoles = async ({user},{db}) => { backend.userRoles = async ({user},{db}) => {
var rolesLocal = await db.all('SELECT roles from bio WHERE username = ?', [ var rolesLocal = await db.all('SELECT roles from user WHERE username = ?', [
user user
] ); ] );
@ -135,7 +135,7 @@ backend.register = async ({user, pass, pass2},{db}) => {
passHash passHash
]) ])
await updateUser({user: user}); await updateUser({user: user}, {db});
return { success: 'Successfully created account.', location: '/'}; return { success: 'Successfully created account.', location: '/'};
} }
@ -236,21 +236,9 @@ backend.userGet = async ({user},{db}) => {
if (!followers) followers = []; if (!followers) followers = [];
return {data: posts[0], following, followers}; posts[0].rolesArr = await backend.userRoles({user},{db});
}
backend.userBio = async ({user},{db}) => { return {data: posts[0], following, followers };
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}) => { backend.postBulk = async ({page, id, user, cookies, sort, type}, {admin, db}) => {
@ -264,11 +252,14 @@ backend.postBulk = async ({page, id, user, cookies, sort, type}, {admin, db}) =>
sort = sort.replaceAll('%d',Math.floor(new Date() * 1000)); sort = sort.replaceAll('%d',Math.floor(new Date() * 1000));
let pageParams = [
page*ROW_COUNT,
ROW_COUNT
]
if (type == 'all') { if (type == 'all') {
posts = await db.all('SELECT * from post ORDER BY '+sort+' DESC LIMIT ?, ?', [ posts = await db.all('SELECT * from post ORDER BY '+sort+' DESC LIMIT ?, ?', [
page*ROW_COUNT, ...pageParams
ROW_COUNT
]) ])
} else if (type == 'post') { } else if (type == 'post') {
posts = await db.all('SELECT * from post WHERE id = ?', [ posts = await db.all('SELECT * from post WHERE id = ?', [
@ -279,21 +270,18 @@ backend.postBulk = async ({page, id, user, cookies, sort, type}, {admin, db}) =>
posts.push(...(await db.all('SELECT * from post WHERE reply = ? ORDER BY '+sort+' DESC LIMIT ?, ?', [ posts.push(...(await db.all('SELECT * from post WHERE reply = ? ORDER BY '+sort+' DESC LIMIT ?, ?', [
id, id,
page*ROW_COUNT, ...pageParams
ROW_COUNT
]))) ])))
} else if (type == 'user') { } else if (type == 'user') {
posts = await db.all('SELECT * from post WHERE username = ? ORDER BY '+sort+' DESC LIMIT ?, ?', [ posts = await db.all('SELECT * from post WHERE username = ? ORDER BY '+sort+' DESC LIMIT ?, ?', [
user, user,
page*ROW_COUNT, ...pageParams
ROW_COUNT
]) ])
} else if (type == 'follow') { } 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 ?, ?', [ posts = await db.all('SELECT * from post WHERE username IN (SELECT following from follow WHERE username = ?) ORDER BY '+sort+' DESC LIMIT ?, ?', [
userAuth, userAuth,
page*ROW_COUNT, ...pageParams
ROW_COUNT
]) ])
} }
@ -346,7 +334,7 @@ backend.vote = async ({id, vote}, {user, db}) => {
if (!user[0]) if (!user[0])
return {success: 'fail' }; return {success: 'fail' };
await updateUser({user: user[0].username}); await updateUser({user: user[0].username}, {db});
return {data: {up,down}}; return {data: {up,down}};
} }

View file

@ -87,7 +87,7 @@ let formatPost = function(post) {
extension = safeName(extension); extension = safeName(extension);
splitPost = {'type': EXTENSION_MAP[extension] || 'none', 'url': `/img/${matchCleaned}`}; splitPost = {'type': EXTENSION_MAP[extension] || 'none', 'url': `/img/file/${matchCleaned}`};
return splitPost; return splitPost;
} }

View file

@ -34,7 +34,10 @@
:global(input, textarea) { :global(input, textarea) {
border: 0; border: 0;
border-radius: 0.2rem; border-radius: 0.2rem;
box-shadow: 0px 2px 2.5px 0px var(--dark-2); box-shadow: 0px 0px 2px 2px var(--dark-2);
font-size: 1rem;
padding: 0.5rem;
} }
</style> </style>

View file

@ -1,34 +1,15 @@
<script> <script>
import Area from '$lib/components/Area.svelte'; import Form from '$lib/components/Form.svelte';
import { handleSubmit } from '$lib/util.js';
export let form = {};
let submitFunc = async e => form = JSON.parse(await handleSubmit(e))
</script> </script>
<Area handleSubmit=''> <Form action='/api/login' name='Log in'>
<span slot="header"> <p>
Log in <input name='user' placeholder='Username'>
</span>
<span slot='main'>
<h2>Login</h2>
<form action='/api/login' on:submit|preventDefault={submitFunc} method='POST'>
<p>
Username: <input name='user'>
</p>
<p>
Password: <input type='password' name='pass'>
</p>
<p>
<input type='submit' value='Log in'>
</p>
</form>
</span>
<p slot="footer">
{#if form?.success}
<p>{form?.success}</p>
{/if}
</p> </p>
</Area> <p>
<input type='password' name='pass' placeholder='Password'>
</p>
<p>
<input type='submit' value='Log in'>
</p>
</Form>

View file

@ -1,38 +1,18 @@
<script> <script>
import Area from '$lib/components/Area.svelte'; import Form from '$lib/components/Form.svelte';
import { handleSubmit } from '$lib/util.js';
export let form = {};
let submitFunc = async e => form = JSON.parse(await handleSubmit(e))
</script> </script>
<Area handleSubmit=''> <Form action='/api/register' name='Register'>
<span slot="header"> <p>
Register <input name='user' placeholder='Username'>
</span>
<span slot='main'>
<h2>Register</h2>
<form action='/api/register' on:submit|preventDefault={submitFunc} method='POST'>
<p>
Username: <input name='user'>
</p>
<p>
Password: <input type='password' name='pass'>
</p>
<p>
Confirm Password: <input type='password' name='pass2'>
</p>
<p>
<input type='submit' value='Register'>
</p>
</form>
</span>
<p slot="footer">
{#if form?.success}
<p>{form?.success}</p>
{/if}
By using the Sanifae service, you agree to the <a href='/tos'>Terms of Service</a>.
</p> </p>
</Area> <p>
<input type='password' name='pass' placeholder='Password'>
</p>
<p>
<input type='password' name='pass2' placeholder='Password (confirm)'>
</p>
<p>
<input type='submit' value='Register'>
</p>
</Form>

View file

@ -1,14 +1,23 @@
import { backend, backendProxy } from '../../../lib/db/db.js'; import { backend, backendProxy } from '../../../../lib/db/db.js';
import { readFile, writeFile } from 'node:fs/promises'; import { readFile, writeFile } from 'node:fs/promises';
const FILE_DIRS = [
'upload',
'pfp'
]
/** @type {import('./$types').RequestHandler} */ /** @type {import('./$types').RequestHandler} */
export async function GET({ url, cookies, params }) { export async function GET({ url, cookies, params }) {
var imgName = params['img']; var imgName = params['img'];
imgName = imgName.replace(/(\s+)/g, '\\$1'); imgName = imgName.replace(/(\s+)/g, '\\$1');
var res = await readFile(`${process.cwd()}/db/post-${imgName}`); var dir = params['dir'];
if (FILE_DIRS.indexOf(dir) == -1) dir = FILE_DIRS[0];
var res = await readFile(`${process.cwd()}/db/files/${dir}/${imgName}`);
var response = new Response(res); var response = new Response(res);
var extension = imgName.split('.').pop(); var extension = imgName.split('.').pop();

View file

@ -1,24 +0,0 @@
import { VALID_EXTENSIONS } from '../../../lib/db/db.js';
import { readFile } from 'node:fs/promises';
/** @type {import('./$types').RequestHandler} */
export async function GET({ url, cookies, params }) {
var imgName = params['img'];
imgName = imgName.replace(/(\s+)/g, '\\$1');
var res;
var res = await readFile(`${process.cwd()}/db/pfp-${imgName}`);
var response = new Response(res);
var extension = imgName.split('.').pop();
if (extension == 'svg') {
response = new Response(res, {'headers': {
'Content-Type': 'image/png'
}});
}
return response;
}

View file

@ -10,7 +10,6 @@
let uploadForm = {}; let uploadForm = {};
let userData = data.postJsonUser.data; let userData = data.postJsonUser.data;
let userBio = data.postJsonUserBio.data;
let following = data.postJsonUser.following; let following = data.postJsonUser.following;
let followers = data.postJsonUser.followers; let followers = data.postJsonUser.followers;
@ -37,67 +36,106 @@
} }
.pfp { .pfp {
width: 50px; width: 100px;
height: 50px; height: 100px;
margin-right: 10px; border-radius: 100%;
}
.pfp-small {
width: 45px;
height: 45px;
border-radius: 100%;
} }
#header { #header {
display: flex; display: flex;
justify-content: space-between;
width: 100%;
}
.sections {
display: grid;
grid-template-columns: 1fr 1fr;
}
.sections div {
text-align: right;
}
.sections div:nth-child(1) {
text-align: left;
}
.profile {
align-items: center; align-items: center;
flex-direction: column;
display: flex;
} }
</style> </style>
{#if userData} {#if userData}
<Area> <Area>
<span slot="header" id='header'> <span slot="header" id='header'>
<img class='pfp' src='/pfp/{userData.username}.png'/> <div class='profile'>
<a href='/users/{userData.username}'> <img class='pfp' src='/img/pfp/{userData.username}.png'/>
{userData.username} <a href='/users/{userData.username}'>
</a> {userData.username}
</a>
</div>
<div>
<Button clickFunc={follow}>
Follow
</Button>
</div>
</span> </span>
<span slot="main"> <span slot="main">
<div class='left'></div> <p class='data'>
<p> <span class='follower'>
<b>Reputation:</b> {userData.reputation} <b>{userData.reputation}</b> Reputation
</span>
<span class='follower'>
<b>{userData.upvotes}</b> Upvotes
</span>
<span class='follower'>
<b>{userData.downvotes}</b> Downvotes
</span>
</p> </p>
<p>
<b>Upvotes:</b> {userData.upvotes} {#if userData.rolesArr}
</p> <p>
<p> {#each userData.rolesArr as role}
<b>Downvotes:</b> {userData.downvotes} <b class='follower'>{role}</b>
</p>
<h2>Roles</h2>
<p>
{#if userBio && userBio.rolesArr}
{#each userBio.rolesArr as role}
<i class='follower'>{role}</i>
{/each} {/each}
{/if} </p>
</p> {/if}
<h2>Following</h2> <div class='sections'>
{#each following as user} <div>
<a class='follower' href='/users/{user.following}'> <h2>{following.length} followers</h2>
<img class='pfp' src='/pfp/{user.following}.png'/> {#each following as user}
</a> <a class='follower' href='/users/{user.following}'>
{/each} <img class='pfp-small' src='/img/pfp/{user.following}.png'/>
</a>
{/each}
</div>
<div>
<h2>{followers.length} following</h2>
{#each followers as user}
<a class='follower' href='/users/{user.username}'>
<img class='pfp-small' src='/img/pfp/{user.username}.png'/>
</a>
{/each}
</div>
</div>
<h2>Followers</h2>
{#each followers as user}
<a class='follower' href='/users/{user.username}'>
<img class='pfp' src='/pfp/{user.username}.png'/>
</a>
{/each}
{#if data.resAcc.data == userData.username} {#if data.resAcc.data == userData.username}
<h2>Set PFP</h2> <h2>Set PFP</h2>
<FileUpload bind:form={uploadForm} type='small' apiUrl={'/api/pfp'}/> <FileUpload bind:form={uploadForm} type='small' apiUrl={'/api/pfp'}/>
{/if} {/if}
</span> </span>
<span slot="footer"> <span slot="footer">
<Button clickFunc={follow}>
Follow
</Button>
</span> </span>
</Area> </Area>
{:else} {:else}