added voting
e
This commit is contained in:
parent
d8670ecf81
commit
e211b2cc50
13 changed files with 92 additions and 201 deletions
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import Area from '$lib/Area.svelte';
|
import Area from '$lib/components/Area.svelte';
|
||||||
|
|
||||||
export let success, username, content, upvotes, downvotes, id;
|
export let success, username, content, upvotes, downvotes, id;
|
||||||
|
|
192
src/lib/db.js
192
src/lib/db.js
|
@ -1,192 +0,0 @@
|
||||||
const rowCount = 5;
|
|
||||||
|
|
||||||
import sqlite3 from 'sqlite3'
|
|
||||||
import { open } from 'sqlite'
|
|
||||||
import { hash, compare } from 'bcrypt'
|
|
||||||
import { calcVote } from './util.js';
|
|
||||||
import { checkLength, checkRegex } from './util.js';
|
|
||||||
|
|
||||||
const {
|
|
||||||
randomBytes
|
|
||||||
} = await import('node:crypto');
|
|
||||||
|
|
||||||
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)');
|
|
||||||
}
|
|
||||||
|
|
||||||
var backend = {};
|
|
||||||
|
|
||||||
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.'};
|
|
||||||
|
|
||||||
if (!db) await initDb();
|
|
||||||
|
|
||||||
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}) => {
|
|
||||||
if (!db) await initDb();
|
|
||||||
|
|
||||||
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 ({cookies, content}) => {
|
|
||||||
if (!db) await initDb();
|
|
||||||
|
|
||||||
var lengthCheck = checkLength(content,'Post content',1,10240);
|
|
||||||
|
|
||||||
if (lengthCheck)
|
|
||||||
return lengthCheck;
|
|
||||||
|
|
||||||
var user = (await backend.token({cookies})).data;
|
|
||||||
|
|
||||||
if (!user || !content || user == '') return {'success': 'Not authorized.' };
|
|
||||||
|
|
||||||
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!' };
|
|
||||||
}
|
|
||||||
|
|
||||||
backend.postGet = async ({id}) => {
|
|
||||||
if (!db) await initDb();
|
|
||||||
|
|
||||||
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.postBulk = async ({page}) => {
|
|
||||||
if (!db) await initDb();
|
|
||||||
|
|
||||||
var posts = await db.all('SELECT * from post ORDER BY rating DESC LIMIT ?, ?', [
|
|
||||||
page*rowCount,
|
|
||||||
rowCount
|
|
||||||
])
|
|
||||||
|
|
||||||
return {data: posts};
|
|
||||||
}
|
|
||||||
|
|
||||||
backend.vote = async ({cookies, id, vote}) => {
|
|
||||||
if (!db) await initDb();
|
|
||||||
|
|
||||||
var user = (await backend.token({cookies})).data;
|
|
||||||
|
|
||||||
if (!user || !id || user == '' || (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
|
|
||||||
]);
|
|
||||||
|
|
||||||
return {data: {up,down}};
|
|
||||||
}
|
|
||||||
|
|
||||||
backend.token = async ({cookies}) => {
|
|
||||||
if (!db) await initDb();
|
|
||||||
|
|
||||||
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};
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
backend
|
|
||||||
}
|
|
|
@ -35,6 +35,19 @@ let calcVote = function(up,down) {
|
||||||
return rating * Math.log(totalPadded);
|
return rating * Math.log(totalPadded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let calcVoteUser = function(up,down) {
|
||||||
|
var upPadded = up + 3;
|
||||||
|
var downPadded = down + 3;
|
||||||
|
var totalPadded = Math.max(up + down, 3);
|
||||||
|
|
||||||
|
var rating = -Math.log((1 / ((((upPadded - downPadded) / (upPadded + downPadded)) + 1) / 2)) - 1) / Math.log(Math.E);
|
||||||
|
|
||||||
|
rating = Math.min(rating,10);
|
||||||
|
rating = Math.max(rating,-10);
|
||||||
|
|
||||||
|
return Math.round(rating * Math.log(totalPadded) * 10);
|
||||||
|
}
|
||||||
|
|
||||||
let handleSubmit = async e => {
|
let handleSubmit = async e => {
|
||||||
const ACTION_URL = e.target.action
|
const ACTION_URL = e.target.action
|
||||||
|
|
||||||
|
@ -50,5 +63,6 @@ export {
|
||||||
checkLength,
|
checkLength,
|
||||||
checkRegex,
|
checkRegex,
|
||||||
calcVote,
|
calcVote,
|
||||||
handleSubmit
|
handleSubmit,
|
||||||
|
calcVoteUser
|
||||||
};
|
};
|
|
@ -74,7 +74,7 @@
|
||||||
<img src='/logo_sanifae.svg' alt='Sanifae Logo'>
|
<img src='/logo_sanifae.svg' alt='Sanifae Logo'>
|
||||||
</a>
|
</a>
|
||||||
{#if data.username && data.username != 'false'}
|
{#if data.username && data.username != 'false'}
|
||||||
<a href='/users/{data.username}'>
|
<a href='/user/{data.username}'>
|
||||||
{data.username}
|
{data.username}
|
||||||
</a>
|
</a>
|
||||||
<a href='/logout'>
|
<a href='/logout'>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import Post from '$lib/Post.svelte';
|
import Post from '$lib/components/Post.svelte';
|
||||||
|
|
||||||
/** @type {import('./$types').PageData} */
|
/** @type {import('./$types').PageData} */
|
||||||
export let data;
|
export let data;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import Area from '$lib/Area.svelte';
|
import Area from '$lib/components/Area.svelte';
|
||||||
import { handleSubmit } from '$lib/util.js';
|
import { handleSubmit } from '$lib/util.js';
|
||||||
|
|
||||||
export let form = {};
|
export let form = {};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { backend } from '../../../lib/db.js';
|
import { backend } from '../../../lib/db/db.js';
|
||||||
|
|
||||||
/** @type {import('./$types').RequestHandler} */
|
/** @type {import('./$types').RequestHandler} */
|
||||||
export async function GET({ url, cookies, params }) {
|
export async function GET({ url, cookies, params }) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { backend } from '../../lib/db.js';
|
import { backend } from '../../lib/db/db.js';
|
||||||
import { checkLength, checkRegex } from '../../lib/util.js';
|
import { checkLength, checkRegex } from '../../lib/util.js';
|
||||||
|
|
||||||
/** @type {import('./$types').Actions} */
|
/** @type {import('./$types').Actions} */
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import Area from '$lib/Area.svelte';
|
import Area from '$lib/components/Area.svelte';
|
||||||
|
|
||||||
import { handleSubmit } from '$lib/util.js';
|
import { handleSubmit } from '$lib/util.js';
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import Post from '$lib/Post.svelte';
|
import Post from '$lib/components/Post.svelte';
|
||||||
|
|
||||||
/** @type {import('./$types').PageData} */
|
/** @type {import('./$types').PageData} */
|
||||||
export let data;
|
export let data;
|
||||||
|
|
20
src/routes/user/[user]/+page.js
Normal file
20
src/routes/user/[user]/+page.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/** @type {import('./$types').PageLoad} */
|
||||||
|
export async function load({ fetch, params, url }) {
|
||||||
|
var search = url.searchParams;
|
||||||
|
|
||||||
|
var voteType = search.get('vote');
|
||||||
|
|
||||||
|
var id = search.get('page') * 1;
|
||||||
|
|
||||||
|
var user = params.user + '';
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
|
||||||
|
const res = await fetch(`/api/postBulk?user=${user}&page=${id}`);
|
||||||
|
const postJson = await res.json();
|
||||||
|
|
||||||
|
const resUser = await fetch(`/api/userGet?user=${user}`);
|
||||||
|
const postJsonUser = await resUser.json();
|
||||||
|
|
||||||
|
return { postJson, id, postJsonUser };
|
||||||
|
}
|
49
src/routes/user/[user]/+page.svelte
Normal file
49
src/routes/user/[user]/+page.svelte
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<script>
|
||||||
|
import Post from '$lib/components/Post.svelte';
|
||||||
|
import Area from '$lib/components/Area.svelte';
|
||||||
|
|
||||||
|
/** @type {import('./$types').PageData} */
|
||||||
|
export let data;
|
||||||
|
|
||||||
|
let userData = data.postJsonUser.data;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Area>
|
||||||
|
<span slot="header">
|
||||||
|
<a href='/users/{userData.username}'>
|
||||||
|
{userData.username}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<span slot="main">
|
||||||
|
<p>
|
||||||
|
<b>Reputation:</b> {userData.reputation}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>Upvotes:</b> {userData.upvotes}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>Downvotes:</b> {userData.downvotes}
|
||||||
|
</p>
|
||||||
|
</span>
|
||||||
|
<span slot="footer">
|
||||||
|
|
||||||
|
</span>
|
||||||
|
</Area>
|
||||||
|
|
||||||
|
<h2>Posts</h2>
|
||||||
|
|
||||||
|
{#each data.postJson.data as post}
|
||||||
|
<Post
|
||||||
|
success={post.success}
|
||||||
|
username={post.username}
|
||||||
|
content={post.content}
|
||||||
|
upvotes={post.upvotes}
|
||||||
|
downvotes={post.downvotes}
|
||||||
|
id={post.id}
|
||||||
|
></Post>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a data-sveltekit-reload href='?page={data.id+1}'>Next page</a>
|
||||||
|
</p>
|
||||||
|
<p></p>
|
Loading…
Reference in a new issue