From b0721ddafa3222fb2a802eff107ab271a3a0d25c Mon Sep 17 00:00:00 2001 From: Azalea <22280294+hykilpikonna@users.noreply.github.com> Date: Sat, 25 Oct 2025 22:10:57 +0800 Subject: [PATCH] [+] Get photo, edit --- src/index.ts | 88 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 13 deletions(-) diff --git a/src/index.ts b/src/index.ts index fff2f03..c01478a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,13 +15,17 @@ interface PhotoMetadata { upload_time: string original_photo: { filename: string, path: string, exif: any } edited_photo: { filename: string, path: string, exif: any } + [key: string]: any // Custom properties } class HttpError extends Error { - constructor(public status: number, message: string) { - super(message) + constructor(public status: number, public content: string | object) { + super() } } +function done(status: number, content: string | object) { + throw new HttpError(status, content) +} /** * Loads the metadata.json file. @@ -78,9 +82,10 @@ function checkHeaderKey(headers: Record, expectList: export const app = new Elysia() // Error handling: Return status code and message .onError(({ error, status }) => { - console.error(error) - - if (error instanceof HttpError) return status(error.status, { error: error.message }) + if (error instanceof HttpError) { + if (error.content instanceof String) return status(error.status, { msg: error.message }) + else return status(error.status, error.content) + } else return status(500, { error: "Internal Server Error" }) }) @@ -90,7 +95,7 @@ export const app = new Elysia() // 2. Check for existing ID const metadata = await getMetadata() - if (metadata.find((m) => m.id === id)) status(409, "This ID already exists") + if (metadata.find((m) => m.id === id)) done(409, "This ID already exists") // 3. Process and save files // We use {id}-1 and {id}-2 as requested by "{id}-{number}" @@ -130,7 +135,7 @@ export const app = new Elysia() metadata.push(newEntry) await saveMetadata(metadata) - return status(201, { success: true, id: newEntry.id }) + done(201, { success: true, id: newEntry.id }) }, { // --- Validation Schema --- body: t.Object({ @@ -150,20 +155,77 @@ export const app = new Elysia() // ----- 2. GET /photos ----- // Returns all metadata entries *without* the 'owner_key'. .get("/photos", async () => { - const metadata = await getMetadata() + const metadata = (await getMetadata()).filter((entry) => !entry.hide) // Map over the array and filter out the 'owner_key' from each object - const publicMetadata = metadata.map((entry) => { + const pubMeta = metadata.map((entry) => { const { owner_key, ...publicEntry } = entry return publicEntry }) - return publicMetadata + return pubMeta }) - // ----- 3. GET /photos/:id ----- - // Returns the static file for the *edited* photo. - // Respects the 'hide' flag. + // ----- 3. GET /photos/:id (Static File) ----- + // Returns the static file for the *edited* photo. + // Respects the 'hide' flag. + .get("/photos/:id", async ({ body }) => { + const { id } = body + const metadata = await getMetadata() + const photo = metadata.find((p) => p.id === id) + + // Not found or hidden + if (!photo || photo.hide === true) throw done(404, "Photo not found") + + const file = Bun.file(photo.edited_photo.path) + if (!(await file.exists())) { + console.error(`Missing file for ID ${id} at ${photo.edited_photo.path}`) + done(404, "Photo file not found on disk") + } + return file + }, { + body: t.Object({ id: t.String() }) + }) + + // ----- 4. POST /edit ----- + // Edits a specific field in the metadata. + .post("/edit", async ({ body, status }) => { + const { id, key, field, value } = body + const metadata = await getMetadata() + + const photoIndex = metadata.findIndex((p) => p.id === id) + if (photoIndex === -1) throw + + const photo = metadata[photoIndex] + + // Check authentication (owner key OR site key) + if (key !== photo.owner_key && key !== INSTANT_KEY) { + throw new HttpError(401, "Invalid authentication key") + } + + // Prevent editing core, protected fields + const protectedFields = ["id", "owner_key", "upload_time", "original_photo", "edited_photo"] + if (protectedFields.includes(field)) { + throw new HttpError(400, `Cannot edit protected field: ${field}`) + } + + // Apply the edit + console.log(`Editing photo ${id}: Set ${field} = ${value}`) + photo[field] = value + metadata[photoIndex] = photo // Update the photo in the main array + + await saveMetadata(metadata) + + set.status = 200 // OK + return { success: true, id, updated: { [field]: value } } + }, { + body: t.Object({ + id: t.String(), + key: t.String(), + field: t.String(), + value: t.Any() // Allow any type of value (boolean, string, null, etc.) + }) + }) .listen(3000)