private uploads
This commit is contained in:
parent
6ea5c20cf3
commit
5468158b51
12 changed files with 69 additions and 28 deletions
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `internalName` to the `Upload` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE `Upload` ADD COLUMN `internalName` LONGTEXT NOT NULL;
|
|
@ -51,9 +51,10 @@ model Upload {
|
|||
uploader User @relation(fields: [uploaderId], references: [id])
|
||||
uploaderId Int
|
||||
|
||||
fileName String @db.LongText
|
||||
public Boolean @default(true)
|
||||
uploaded DateTime @default(now())
|
||||
fileName String @db.LongText
|
||||
internalName String @db.LongText
|
||||
public Boolean @default(true)
|
||||
uploaded DateTime @default(now())
|
||||
}
|
||||
|
||||
enum Role {
|
||||
|
|
|
@ -27,16 +27,15 @@
|
|||
<div class="rounded-md transition-all bg-mantle">
|
||||
<div
|
||||
in:fade|global={{ delay: 100 * i }}
|
||||
class="flex place-content-between px-1.5 w-full h-14 rounded-md transition-all {error
|
||||
? 'bg-red-300 dark:bg-red-700/60'
|
||||
: ''}"
|
||||
style={error
|
||||
class="flex place-content-between px-1.5 w-full h-14 rounded-md transition-all
|
||||
{url ? 'bg-blue/30' : ''} {error ? 'bg-red/30' : ''}"
|
||||
style={error || url
|
||||
? ''
|
||||
: `background: linear-gradient(90deg, rgb(var(--ctp-surface0)) ${percent}%, transparent ${percent}%);`}
|
||||
>
|
||||
<div class="flex overflow-x-scroll flex-col my-auto overflow-y-clip">
|
||||
{#if url}
|
||||
<a href={url} class="text-blue">{file.name}</a>
|
||||
<a href={url} class="font-bold text-blue">{file.name}</a>
|
||||
{:else}
|
||||
<p>{file.name}</p>
|
||||
{/if}
|
||||
|
|
|
@ -13,14 +13,17 @@
|
|||
<div class="flex my-auto md:space-x-6">
|
||||
<a
|
||||
href="/dashboard"
|
||||
class="hidden md:block flex-none my-auto w-20 fill-text text-xl transition-all hover:scale-105 focus:scale-105 active:scale-95"
|
||||
class="hidden flex-none my-auto w-20 text-xl transition-all md:block fill-text hover:scale-105 focus:scale-105 active:scale-95"
|
||||
>
|
||||
<Logo></Logo>
|
||||
</a>
|
||||
<div class="flex my-auto space-x-3">
|
||||
<HeaderLink href="/dashboard">Dashboard</HeaderLink>
|
||||
<HeaderLink href="/uploads">Uploads</HeaderLink>
|
||||
<HeaderLink href="/admin">Admin</HeaderLink>
|
||||
<HeaderLink href="/links">Links</HeaderLink>
|
||||
{#if $user?.role === 'ADMINISTRATOR'}
|
||||
<HeaderLink href="/admin">Admin</HeaderLink>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex my-auto space-x-2">
|
||||
|
|
|
@ -7,12 +7,10 @@ export function goBack() {
|
|||
}
|
||||
|
||||
export function bytesToHumanReadable(bytes: number) {
|
||||
if (bytes === 0) {
|
||||
return '0 B';
|
||||
}
|
||||
|
||||
let e = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
return (bytes / Math.pow(1024, e)).toFixed(2) + ' ' + ' KMGTP'.charAt(e) + 'B';
|
||||
let i = bytes == 0 ? 0 : Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
return (
|
||||
+(bytes / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB'][i]
|
||||
);
|
||||
}
|
||||
|
||||
export function request(
|
||||
|
|
|
@ -88,7 +88,12 @@ export async function deleteSession(id: string) {
|
|||
});
|
||||
}
|
||||
|
||||
export async function createUpload(id: string, uploaderId: number, fileName: string) {
|
||||
export async function createUpload(
|
||||
id: string,
|
||||
uploaderId: number,
|
||||
fileName: string,
|
||||
internalName: string
|
||||
) {
|
||||
const settings = await prisma.userSettings.findFirst({
|
||||
where: { id: uploaderId }
|
||||
});
|
||||
|
@ -98,6 +103,7 @@ export async function createUpload(id: string, uploaderId: number, fileName: str
|
|||
id,
|
||||
uploaderId,
|
||||
fileName,
|
||||
internalName,
|
||||
public: settings?.newPostsPublic
|
||||
}
|
||||
});
|
||||
|
@ -113,6 +119,7 @@ export async function getUpload(id: string) {
|
|||
select: {
|
||||
id: true,
|
||||
fileName: true,
|
||||
internalName: true,
|
||||
public: true,
|
||||
uploaded: true,
|
||||
uploader: true
|
||||
|
|
|
@ -25,5 +25,5 @@ function du() {
|
|||
|
||||
if (!building) {
|
||||
du();
|
||||
setTimeout(du, 1000 * 60 * 10);
|
||||
setTimeout(du, 1000 * 60 * 5);
|
||||
}
|
||||
|
|
|
@ -6,8 +6,11 @@
|
|||
import FileComponent from '$lib/components/File.svelte';
|
||||
import { get } from 'svelte/store';
|
||||
import { request } from '$lib';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let data;
|
||||
console.log(data);
|
||||
user.set(data?.user);
|
||||
|
||||
let input: HTMLInputElement,
|
||||
|
@ -16,6 +19,10 @@
|
|||
|
||||
let running = false;
|
||||
|
||||
onMount(() => {
|
||||
fileProgress.set({});
|
||||
});
|
||||
|
||||
// lazy again
|
||||
function progress(name: string, data: any) {
|
||||
let _ = get(fileProgress);
|
||||
|
@ -37,11 +44,22 @@
|
|||
const response = await request(body, (percent: number) => {
|
||||
progress(k, { percent });
|
||||
}).catch(() => {
|
||||
toast.error(k + ' failed to upload.');
|
||||
progress(k, { error: true });
|
||||
});
|
||||
|
||||
if (response && response.success) progress(k, { url: response.body });
|
||||
else progress(k, { error: true });
|
||||
else {
|
||||
if (response && response.body)
|
||||
try {
|
||||
let body = JSON.parse(response.body);
|
||||
toast.error(k + ' failed: ' + body.message);
|
||||
} catch (_) {
|
||||
toast.error(k + ' failed to upload.');
|
||||
}
|
||||
|
||||
progress(k, { error: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -69,7 +87,7 @@
|
|||
<div>
|
||||
<h1 class="text-2xl font-bold">Welcome, {data.user.username}.</h1>
|
||||
<p class="text-overlay1">
|
||||
Your max upload size is <span class="font-bold">{data.user.maxUploadMB} MB</span>.
|
||||
Your max upload size is <span class="font-bold">{data.user.maxUploadMB} MiB</span>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 p-2 mx-auto w-full rounded-lg shadow-lg bg-crust">
|
||||
|
|
0
src/routes/(app)/links/+page.svelte
Normal file
0
src/routes/(app)/links/+page.svelte
Normal file
|
@ -15,15 +15,16 @@ export const POST = async ({ request, cookies }) => {
|
|||
const data = await request.formData();
|
||||
const file = data.get('file') as File;
|
||||
|
||||
if (Math.floor(file.size / (1024 * 1024)) > user.maxUploadMB)
|
||||
if (file.size / 1048576 >= user.maxUploadMB)
|
||||
return error(413, { status: 413, message: 'Content Too Large' });
|
||||
|
||||
let id = generateId(undefined, 10);
|
||||
let internalName = `${Date.now()}-${file.name}`;
|
||||
|
||||
const object = await minio
|
||||
.putObject(
|
||||
BUCKET,
|
||||
`${user.id}/${file.name}`,
|
||||
`${user.id}/${internalName}`,
|
||||
Buffer.from(await file.arrayBuffer()),
|
||||
file.size,
|
||||
{
|
||||
|
@ -34,7 +35,7 @@ export const POST = async ({ request, cookies }) => {
|
|||
if (!object)
|
||||
return error(500, { status: 500, message: 'Internal Server Error - Contact Administrator' });
|
||||
|
||||
const objectRecord = await createUpload(id, user.id, file.name);
|
||||
const objectRecord = await createUpload(id, user.id, file.name, internalName);
|
||||
if (!objectRecord)
|
||||
return error(500, { status: 500, message: 'Internal Server Error - Contact Administrator' });
|
||||
|
||||
|
|
|
@ -2,14 +2,17 @@ import { getUpload } from '$lib/server/database.js';
|
|||
import minio, { BUCKET } from '$lib/server/minio';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
export const GET = async ({ params }) => {
|
||||
export const GET = async ({ params, locals }) => {
|
||||
const id = params.id;
|
||||
|
||||
const file = await getUpload(id);
|
||||
if (!file) throw error(404, { status: 404, message: 'File Not Found' });
|
||||
|
||||
const object = await minio.getObject(BUCKET, `${file.uploader.id}/${file.fileName}`);
|
||||
const metadata = await minio.statObject(BUCKET, `${file.uploader.id}/${file.fileName}`);
|
||||
if (!file.public && locals?.user?.id !== file.uploader.id)
|
||||
throw error(403, { status: 403, message: 'Forbidden' });
|
||||
|
||||
const object = await minio.getObject(BUCKET, `${file.uploader.id}/${file.internalName}`);
|
||||
const metadata = await minio.statObject(BUCKET, `${file.uploader.id}/${file.internalName}`);
|
||||
|
||||
const ac = new AbortController();
|
||||
|
||||
|
|
|
@ -2,14 +2,17 @@ import { getSettings, getUpload } from '$lib/server/database';
|
|||
import minio, { BUCKET } from '$lib/server/minio';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
export async function load({ params }) {
|
||||
export async function load({ params, locals }) {
|
||||
const file = await getUpload(params.id);
|
||||
if (!file) throw error(404, { status: 404, message: 'File Not Found' });
|
||||
|
||||
if (!file.public && locals?.user?.id !== file.uploader.id)
|
||||
throw error(403, { status: 403, message: 'Forbidden' });
|
||||
|
||||
const settings = await getSettings(file.uploader.id);
|
||||
if (!settings) throw error(500, { status: 500, message: 'Internal Server Error' });
|
||||
|
||||
const metadata = await minio.statObject(BUCKET, `${file.uploader.id}/${file.fileName}`);
|
||||
const metadata = await minio.statObject(BUCKET, `${file.uploader.id}/${file.internalName}`);
|
||||
|
||||
function formatString(input: string) {
|
||||
if (file && metadata)
|
||||
|
|
Loading…
Reference in a new issue