From d27971692df2bd7c4374ef9b40028904ff1dac05 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 12 Jun 2024 13:23:59 +0000 Subject: [PATCH 1/3] chore: Update S3 configuration and endpoint handling --- src/routes/api/upload/+server.ts | 20 ++++++++++++++++---- src/routes/settings/+page.server.ts | 3 +++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/routes/api/upload/+server.ts b/src/routes/api/upload/+server.ts index 1e582c8..09d40ca 100644 --- a/src/routes/api/upload/+server.ts +++ b/src/routes/api/upload/+server.ts @@ -1,7 +1,6 @@ // src/routes/api/upload.js -import { ensureBucketExists, s3Client, uploadObject } from "$lib/server/s3"; -import { HeadBucketCommand } from "@aws-sdk/client-s3"; +import { ensureBucketExists, uploadObject } from "$lib/server/s3"; import type { RequestEvent } from "@sveltejs/kit"; import { generateId } from "lucia"; @@ -10,6 +9,7 @@ export async function POST(event: RequestEvent): Promise { const contentType = event.request.headers.get("content-type") ?? ""; const fileExtension = contentType.split("/").pop(); const fileName = `${generateId(25)}.${fileExtension}`; + const bucket = event.request.headers.get("bucket") as string; if (!fileExtension || !fileName) { return new Response(JSON.stringify({ error: "Invalid file type" }), { @@ -20,6 +20,18 @@ export async function POST(event: RequestEvent): Promise { }); } + if (!bucket) { + return new Response( + JSON.stringify({ error: "Bucket name is required" }), + { + status: 400, + headers: { + "Content-Type": "application/json", + }, + } + ); + } + // check if the file is an image if (!contentType.startsWith("image")) { return new Response(JSON.stringify({ error: "Invalid file type" }), { @@ -35,10 +47,10 @@ export async function POST(event: RequestEvent): Promise { "Content-Type": contentType, }; - await ensureBucketExists("profile-pics"); + await ensureBucketExists(bucket); const objectUrl = await uploadObject( - "profile-pics", + bucket, fileName, Buffer.from(fileBuffer) ); diff --git a/src/routes/settings/+page.server.ts b/src/routes/settings/+page.server.ts index edd5857..03d8680 100644 --- a/src/routes/settings/+page.server.ts +++ b/src/routes/settings/+page.server.ts @@ -81,6 +81,9 @@ export const actions: Actions = { const response = await event.fetch("/api/upload", { method: "POST", body: profilePicture, + headers: { + bucket: "profile-pics", + }, }); const data = await response.json(); From 157d9fe99c35537bb1d2f33d55dd688742e802de Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 12 Jun 2024 13:35:23 +0000 Subject: [PATCH 2/3] Object auto delete when upload --- src/lib/server/s3.ts | 18 ++++++++++++++++++ src/routes/api/upload/+server.ts | 9 +++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/lib/server/s3.ts b/src/lib/server/s3.ts index b9ca7b3..5f50a7c 100644 --- a/src/lib/server/s3.ts +++ b/src/lib/server/s3.ts @@ -1,5 +1,6 @@ import { CreateBucketCommand, + DeleteObjectCommand, HeadBucketCommand, PutBucketPolicyCommand, PutObjectCommand, @@ -119,3 +120,20 @@ export const uploadObject = async ( throw error; } }; + +export const deleteObject = async (bucketName: string, fileName: string) => { + const deleteObjectCommand = new DeleteObjectCommand({ + Bucket: bucketName, + Key: fileName, + }); + + try { + await s3Client.send(deleteObjectCommand); + } catch (error) { + console.error( + `Error uploading file ${fileName} to bucket ${bucketName}:`, + error + ); + throw error; + } +}; diff --git a/src/routes/api/upload/+server.ts b/src/routes/api/upload/+server.ts index 09d40ca..4b685a0 100644 --- a/src/routes/api/upload/+server.ts +++ b/src/routes/api/upload/+server.ts @@ -1,6 +1,6 @@ // src/routes/api/upload.js -import { ensureBucketExists, uploadObject } from "$lib/server/s3"; +import { deleteObject, ensureBucketExists, uploadObject } from "$lib/server/s3"; import type { RequestEvent } from "@sveltejs/kit"; import { generateId } from "lucia"; @@ -8,7 +8,7 @@ export async function POST(event: RequestEvent): Promise { try { const contentType = event.request.headers.get("content-type") ?? ""; const fileExtension = contentType.split("/").pop(); - const fileName = `${generateId(25)}.${fileExtension}`; + const fileName = `${generateId(50)}.${fileExtension}`; const bucket = event.request.headers.get("bucket") as string; if (!fileExtension || !fileName) { @@ -49,6 +49,11 @@ export async function POST(event: RequestEvent): Promise { await ensureBucketExists(bucket); + if (event.locals.user?.icon) { + const key: string = event.locals.user.icon.split("/").pop() as string; + await deleteObject(bucket, key); + } + const objectUrl = await uploadObject( bucket, fileName, From bf51dcb4e3021db5308d9272cad91099fc65b7a9 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 12 Jun 2024 13:41:31 +0000 Subject: [PATCH 3/3] chore: Update UserAvatar component to display user's first initial if no icon is provided --- src/lib/components/UserAvatar.svelte | 14 +++++--------- src/routes/api/upload/+server.ts | 2 +- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/lib/components/UserAvatar.svelte b/src/lib/components/UserAvatar.svelte index 6601652..e1d4756 100644 --- a/src/lib/components/UserAvatar.svelte +++ b/src/lib/components/UserAvatar.svelte @@ -3,14 +3,6 @@ import { goto } from "$app/navigation"; export let user: any; - let icon: string = ""; - - if (user.icon != null && user.icon != "") { - icon = user.icon; - } else { - icon = user.username.charAt(0); - } - async function navToSettings() { goto("/settings"); } @@ -22,7 +14,11 @@