Added file uploads
This commit is contained in:
parent
911d17637f
commit
b2ce13eea1
10 changed files with 169 additions and 40 deletions
|
@ -1,11 +1,13 @@
|
||||||
<script>
|
<script>
|
||||||
import Area from '$lib/components/Area.svelte';
|
import Area from '$lib/components/Area.svelte';
|
||||||
|
|
||||||
|
import {formatPost} from '$lib/util.js';
|
||||||
|
|
||||||
export let success, username, content, upvotes, downvotes, id;
|
export let success, username, content, upvotes, downvotes, id;
|
||||||
|
|
||||||
let query = (id) ? `/post/${id}` : '';
|
let query = (id) ? `/post/${id}` : '';
|
||||||
|
|
||||||
let contentSplit = content.split('\n');
|
let contentSplit = formatPost(content || '');
|
||||||
|
|
||||||
let fData;
|
let fData;
|
||||||
|
|
||||||
|
@ -38,6 +40,10 @@
|
||||||
.vote-area {
|
.vote-area {
|
||||||
margin-right: 30px;
|
margin-right: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 250px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
{#if success}
|
{#if success}
|
||||||
|
@ -61,7 +67,11 @@
|
||||||
</span>
|
</span>
|
||||||
<span slot="main">
|
<span slot="main">
|
||||||
{#each contentSplit as line}
|
{#each contentSplit as line}
|
||||||
|
{#if line && line.type == 'img'}
|
||||||
|
<img src={line.url} alt='Image preview'>
|
||||||
|
{:else}
|
||||||
<p>{line}</p>
|
<p>{line}</p>
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</span>
|
</span>
|
||||||
<span slot="footer">
|
<span slot="footer">
|
||||||
|
|
|
@ -2,14 +2,15 @@ const rowCount = 5;
|
||||||
|
|
||||||
const AUTH_ACTIONS = [
|
const AUTH_ACTIONS = [
|
||||||
'postCreate',
|
'postCreate',
|
||||||
|
'fileCreate',
|
||||||
'vote'
|
'vote'
|
||||||
];
|
];
|
||||||
|
|
||||||
import sqlite3 from 'sqlite3'
|
import sqlite3 from 'sqlite3'
|
||||||
import { open } from 'sqlite'
|
import { open } from 'sqlite'
|
||||||
import { hash, compare } from 'bcrypt'
|
import { hash, compare } from 'bcrypt'
|
||||||
import { randomBytes } from 'node:crypto';
|
import { randomBytes, createHash } from 'node:crypto';
|
||||||
|
import { readFile, writeFile } from 'node:fs/promises';
|
||||||
import { calcVote, calcVoteUser, checkLength, checkRegex } from '../util.js';
|
import { calcVote, calcVoteUser, checkLength, checkRegex } from '../util.js';
|
||||||
|
|
||||||
var db;
|
var db;
|
||||||
|
@ -128,7 +129,7 @@ backend.login = async ({user, pass, cookies}) => {
|
||||||
return { success: 'Successfully logged into account.', data: token, location: '/'};
|
return { success: 'Successfully logged into account.', data: token, location: '/'};
|
||||||
}
|
}
|
||||||
|
|
||||||
backend.postCreate = async ({content}) => {
|
backend.postCreate = async ({content, user}) => {
|
||||||
var lengthCheck = checkLength(content,'Post content',1,10240);
|
var lengthCheck = checkLength(content,'Post content',1,10240);
|
||||||
|
|
||||||
if (lengthCheck)
|
if (lengthCheck)
|
||||||
|
@ -145,7 +146,7 @@ backend.postCreate = async ({content}) => {
|
||||||
calcVote(0,0)
|
calcVote(0,0)
|
||||||
])
|
])
|
||||||
|
|
||||||
return {'success': 'Your post has been broadcasted!' };
|
return {'success': 'Your post has been broadcasted!', 'href': `/post/${id}` };
|
||||||
}
|
}
|
||||||
|
|
||||||
backend.postGet = async ({id}) => {
|
backend.postGet = async ({id}) => {
|
||||||
|
@ -244,6 +245,24 @@ backend.token = async ({cookies}) => {
|
||||||
return {data: existingAccounts[0].username};
|
return {data: existingAccounts[0].username};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backend.fileCreate = async({img, extension}) => {
|
||||||
|
const imgHash = createHash('md5').update(img).digest('hex');
|
||||||
|
|
||||||
|
let lengthCheck = checkLength(img,'Image',0,1024*1024*5);
|
||||||
|
|
||||||
|
if (lengthCheck)
|
||||||
|
return lengthCheck;
|
||||||
|
|
||||||
|
const extensionSafe = extension.replace(/(\s+)/g, '\\$1');
|
||||||
|
|
||||||
|
if (extensionSafe != 'png' && extensionSafe != 'jpg' && extensionSafe != 'svg' )
|
||||||
|
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 {
|
export {
|
||||||
backendProxy,
|
backendProxy,
|
||||||
backend
|
backend
|
||||||
|
|
|
@ -59,10 +59,33 @@ let handleSubmit = async e => {
|
||||||
}).then(x => x.text());
|
}).then(x => x.text());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let formatPost = function(post) {
|
||||||
|
post = post.split('\n');
|
||||||
|
|
||||||
|
post = post.map(subPost => {
|
||||||
|
var splitPost = subPost.split('||');
|
||||||
|
|
||||||
|
if (splitPost.length > 1) {
|
||||||
|
var cap1 = splitPost[0];
|
||||||
|
|
||||||
|
if (cap1 == 'img') {
|
||||||
|
var matchCleaned = splitPost[1].replace(/(\s+)/g, '\\$1');
|
||||||
|
splitPost = {'type': 'img', 'url': `/img/${matchCleaned}`};
|
||||||
|
|
||||||
|
return splitPost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subPost;
|
||||||
|
});
|
||||||
|
|
||||||
|
return post;
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
checkLength,
|
checkLength,
|
||||||
checkRegex,
|
checkRegex,
|
||||||
calcVote,
|
calcVote,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
calcVoteUser
|
calcVoteUser,
|
||||||
|
formatPost
|
||||||
};
|
};
|
|
@ -83,6 +83,9 @@
|
||||||
<a href='/new_post'>
|
<a href='/new_post'>
|
||||||
Create
|
Create
|
||||||
</a>
|
</a>
|
||||||
|
<a href='/new_file'>
|
||||||
|
Create file
|
||||||
|
</a>
|
||||||
{:else}
|
{:else}
|
||||||
<a href='/account'>
|
<a href='/account'>
|
||||||
Log in / Register
|
Log in / Register
|
||||||
|
|
|
@ -27,7 +27,7 @@ async function handleReq({ cookies, params, route }) {
|
||||||
var backendParams = {cookies};
|
var backendParams = {cookies};
|
||||||
|
|
||||||
for (const [key, value] of params) {
|
for (const [key, value] of params) {
|
||||||
backendParams[key] = value;
|
backendParams[key] = value + '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return await mainApi({backendParams, route: route});
|
return await mainApi({backendParams, route: route});
|
||||||
|
|
14
src/routes/img/[img]/+server.js
Normal file
14
src/routes/img/[img]/+server.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { backend, backendProxy } from '../../../lib/db/db.js';
|
||||||
|
|
||||||
|
import { readFile, writeFile } 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 = await readFile(`${process.cwd()}/db/post-${imgName}`);
|
||||||
|
|
||||||
|
return new Response(res);
|
||||||
|
}
|
75
src/routes/new_file/+page.svelte
Normal file
75
src/routes/new_file/+page.svelte
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
<script>
|
||||||
|
import Area from '$lib/components/Area.svelte';
|
||||||
|
|
||||||
|
/** @type {import('./$types').ActionData} */
|
||||||
|
export let form;
|
||||||
|
|
||||||
|
let fileInput;
|
||||||
|
let files;
|
||||||
|
let preview;
|
||||||
|
|
||||||
|
function getBase64(image) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(image);
|
||||||
|
reader.onload = e => {
|
||||||
|
preview = e.target.result;
|
||||||
|
uploadFunction(e.target.result);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
async function uploadFunction(imgBase64) {
|
||||||
|
const imgData = imgBase64.split(',').pop();
|
||||||
|
|
||||||
|
var fData = (new FormData());
|
||||||
|
|
||||||
|
fData.append('img',imgData);
|
||||||
|
fData.append('extension',fileInput.value.split('.').pop());
|
||||||
|
|
||||||
|
form = await fetch(`/api/fileCreate`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: fData
|
||||||
|
}).then(x => x.json());
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
textarea {
|
||||||
|
width: 10rem;
|
||||||
|
height: 10rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 250px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<Area>
|
||||||
|
<p slot="header">
|
||||||
|
Upload File
|
||||||
|
</p>
|
||||||
|
<form slot="main" action='/api/postCreate' method='POST' >
|
||||||
|
{#if preview}
|
||||||
|
<img src={preview} alt="Image preview"/>
|
||||||
|
{:else}
|
||||||
|
<img src='/icon_sanifae.svg' alt="Image preview"/>
|
||||||
|
{/if}
|
||||||
|
<input class="hidden" id="file-to-upload" type="file" bind:files bind:this={fileInput} on:change={() => getBase64(files[0])}/>
|
||||||
|
<p>
|
||||||
|
<a href='#' class="upload-btn" on:click={ () => fileInput.click() }>Upload</a>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
<p slot="footer">
|
||||||
|
{#if form?.success}
|
||||||
|
{#if form?.href}
|
||||||
|
<a href='{form?.href}'>{form?.success}</a>
|
||||||
|
{:else}
|
||||||
|
{form?.success}
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
Create an image for the world to see.
|
||||||
|
</p>
|
||||||
|
</Area>
|
|
@ -1,19 +0,0 @@
|
||||||
import { backend } from '../../lib/db/db.js';
|
|
||||||
import { checkLength, checkRegex } from '../../lib/util.js';
|
|
||||||
|
|
||||||
/** @type {import('./$types').Actions} */
|
|
||||||
export const actions = {
|
|
||||||
create: async ({ request, cookies }) => {
|
|
||||||
const data = await request.formData();
|
|
||||||
const content = data.get('content') + '';
|
|
||||||
|
|
||||||
var lengthCheck = checkLength(content,'Post content',1,10240);
|
|
||||||
|
|
||||||
if (lengthCheck)
|
|
||||||
return lengthCheck;
|
|
||||||
|
|
||||||
await backend.postCreate({user, content});
|
|
||||||
|
|
||||||
return {'success': 'Successfully posted.'};
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -26,10 +26,20 @@
|
||||||
<input formaction="?/create" type='submit' value='Post'>
|
<input formaction="?/create" type='submit' value='Post'>
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
<p slot="footer">
|
<span slot="footer">
|
||||||
|
<p>
|
||||||
{#if form?.success}
|
{#if form?.success}
|
||||||
<p>{form?.success}</p>
|
{#if form?.href}
|
||||||
|
<a href='{form?.href}'>{form?.success}</a>
|
||||||
|
{:else}
|
||||||
|
{form?.success}
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
Create a post for the world to see.
|
|
||||||
</p>
|
</p>
|
||||||
|
<p>Create a post for the world to see.</p>
|
||||||
|
<h2>Post syntax</h2>
|
||||||
|
<p>
|
||||||
|
<b>img||filename.blah</b> embeds a user-uploaded file in this site
|
||||||
|
</p>
|
||||||
|
</span>
|
||||||
</Area>
|
</Area>
|
|
@ -7,13 +7,7 @@ export async function load({ fetch, params, url }) {
|
||||||
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 100));
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
|
||||||
var f = (new FormData());
|
const res = await fetch(`/api/postGet?id=${id}`);
|
||||||
|
|
||||||
f.append('id',id);
|
|
||||||
const res = await fetch(`/api/postGet`, {
|
|
||||||
method: 'POST',
|
|
||||||
body: f
|
|
||||||
});
|
|
||||||
const postJson = (await res.json()).data;
|
const postJson = (await res.json()).data;
|
||||||
|
|
||||||
console.log(postJson);
|
console.log(postJson);
|
||||||
|
|
Loading…
Reference in a new issue