diff --git a/src/lib/functions/generateReadableStream.ts b/src/lib/functions/generateReadableStream.ts index a375c69..fac2242 100644 --- a/src/lib/functions/generateReadableStream.ts +++ b/src/lib/functions/generateReadableStream.ts @@ -3,7 +3,7 @@ export async function generateStream(file: File): Promise { const formData = new FormData(); formData.append('file', file); - await fetch(`/api/upload/`, { + await fetch(`/api/set-profile-photo/`, { method: 'POST', body: formData, }); diff --git a/src/lib/server/db/sqlite.ts b/src/lib/server/db/sqlite.ts index d98bdeb..8b9d076 100644 --- a/src/lib/server/db/sqlite.ts +++ b/src/lib/server/db/sqlite.ts @@ -13,6 +13,14 @@ class AuthDb { this.client.pragma('journal_mode = WAL'); } + setUserImage(userId: string, image: string) { + try { + this.client.prepare('UPDATE user SET image = ? WHERE id = ?').run(image, userId); + } catch (e) { + console.error(`Error setting user image: ${(e as Error).message}`); + } + } + getUser(userId: string): Profile { const row = this.client.prepare('SELECT username, image FROM user WHERE id = ?').get(userId); return { diff --git a/src/lib/server/storage/minio-client.ts b/src/lib/server/storage/minio-client.ts index da80a9f..ebfca23 100644 --- a/src/lib/server/storage/minio-client.ts +++ b/src/lib/server/storage/minio-client.ts @@ -34,6 +34,17 @@ class MinioClient { } } + async fetchProfilePhoto(objectId: string) { + try { + const bucket = 'profile-photos'; + const object = await this.client.getObject(bucket, objectId); + return object; + } catch (e) { + console.error(`Error fetching file: ${(e as Error).message}`); + throw e; + } + } + async uploadProfile(stream: Readable, mime: string) { try { const bucket = 'profile-photos'; diff --git a/src/routes/(server)/api/images/[filename]/+server.ts b/src/routes/(server)/api/images/[filename]/+server.ts new file mode 100644 index 0000000..b3da31d --- /dev/null +++ b/src/routes/(server)/api/images/[filename]/+server.ts @@ -0,0 +1,30 @@ +import { fsClient } from '$lib/server/storage/minio-client'; +import { error } from '@sveltejs/kit'; + +export const GET = async ({ params }) => { + const { filename } = params; + + try { + const stream = await fsClient?.fetchProfilePhoto(filename); + if (!stream) { + return error(404, { message: 'File not found' }); + } + + const readableStream = new ReadableStream({ + start(controller) { + stream.on('data', (chunk) => controller.enqueue(chunk)); + stream.on('end', () => controller.close()); + stream.on('error', (err) => controller.error(err)); + }, + }); + + return new Response(readableStream, { + headers: { + 'Content-Type': `image/${filename.split('.').pop()}`, + 'Cache-Control': 'public, max-age=31536000', + }, + }); + } catch { + return error(404, { message: 'File not found' }); + } +}; diff --git a/src/routes/(server)/api/upload/+server.ts b/src/routes/(server)/api/set-profile-photo/+server.ts similarity index 79% rename from src/routes/(server)/api/upload/+server.ts rename to src/routes/(server)/api/set-profile-photo/+server.ts index 0638f6e..68a734a 100644 --- a/src/routes/(server)/api/upload/+server.ts +++ b/src/routes/(server)/api/set-profile-photo/+server.ts @@ -1,5 +1,6 @@ import { error, json } from '@sveltejs/kit'; import { auth } from '$lib/server/db/auth'; +import { authdb } from '$lib/server/db/sqlite'; import { fsClient } from '$lib/server/storage/minio-client'; import { Readable } from 'stream'; @@ -23,11 +24,13 @@ export const POST = async ({ request }) => { const buffer = await file.arrayBuffer(); const stream = Readable.from(Buffer.from(buffer)); + console.log('Uploading profile photo'); const uploadResponse = await fsClient?.uploadProfile(stream, file.type); + authdb.setUserImage(session.user.id, `/api/images/${uploadResponse?.objectId}`); return json(uploadResponse); } catch (e) { console.error(e); - return error(500, 'Error uploading file'); + return error(500, (e as Error).message); } };