[+] Get photo, edit
This commit is contained in:
+75
-13
@@ -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<string, string | undefined>, 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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user