fix: add upload/delete file endpoints and wire avatar update through profile
- Add POST /upload and DELETE /assets/:id routes to backend (session auth via session_token cookie) - Add avatar arg to updateProfile GraphQL mutation and resolver - Fix frontend to pass avatarId correctly on save, preserve existing avatar when unchanged - Ignore 404 on file delete (already gone is fine) - Remove broken folder lookup (getFolders is a stub, backend has no folder concept) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,7 @@ builder.mutationField("updateProfile", (t) =>
|
||||
artistName: t.arg.string(),
|
||||
description: t.arg.string(),
|
||||
tags: t.arg.stringList(),
|
||||
avatar: t.arg.string(),
|
||||
},
|
||||
resolve: async (_root, args, ctx) => {
|
||||
if (!ctx.currentUser) throw new GraphQLError("Unauthorized");
|
||||
@@ -58,6 +59,7 @@ builder.mutationField("updateProfile", (t) =>
|
||||
if (args.description !== undefined && args.description !== null)
|
||||
updates.description = args.description;
|
||||
if (args.tags !== undefined && args.tags !== null) updates.tags = args.tags;
|
||||
if (args.avatar !== undefined) updates.avatar = args.avatar;
|
||||
|
||||
await ctx.db
|
||||
.update(users)
|
||||
|
||||
@@ -7,7 +7,8 @@ import { createYoga } from "graphql-yoga";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { files } from "./db/schema/index";
|
||||
import path from "path";
|
||||
import { existsSync } from "fs";
|
||||
import { existsSync, mkdirSync } from "fs";
|
||||
import { writeFile, rm } from "fs/promises";
|
||||
import sharp from "sharp";
|
||||
import { schema } from "./graphql/index";
|
||||
import { buildContext } from "./graphql/context";
|
||||
@@ -120,6 +121,54 @@ async function main() {
|
||||
return reply.sendFile(path.join(id, filename));
|
||||
});
|
||||
|
||||
// Upload a file: POST /upload (multipart, requires session)
|
||||
fastify.post("/upload", async (request, reply) => {
|
||||
const token = request.cookies["session_token"];
|
||||
if (!token) return reply.status(401).send({ error: "Unauthorized" });
|
||||
|
||||
const sessionData = await redis.get(`session:${token}`);
|
||||
if (!sessionData) return reply.status(401).send({ error: "Unauthorized" });
|
||||
const { id: userId } = JSON.parse(sessionData);
|
||||
|
||||
const data = await request.file();
|
||||
if (!data) return reply.status(400).send({ error: "No file provided" });
|
||||
|
||||
const id = crypto.randomUUID();
|
||||
const filename = data.filename;
|
||||
const mime_type = data.mimetype;
|
||||
const dir = path.join(UPLOAD_DIR, id);
|
||||
|
||||
mkdirSync(dir, { recursive: true });
|
||||
const buffer = await data.toBuffer();
|
||||
await writeFile(path.join(dir, filename), buffer);
|
||||
|
||||
const [file] = await db
|
||||
.insert(files)
|
||||
.values({ id, filename, mime_type, filesize: buffer.byteLength, uploaded_by: userId })
|
||||
.returning();
|
||||
|
||||
return reply.status(201).send(file);
|
||||
});
|
||||
|
||||
// Delete a file: DELETE /assets/:id (requires session)
|
||||
fastify.delete("/assets/:id", async (request, reply) => {
|
||||
const token = request.cookies["session_token"];
|
||||
if (!token) return reply.status(401).send({ error: "Unauthorized" });
|
||||
|
||||
const sessionData = await redis.get(`session:${token}`);
|
||||
if (!sessionData) return reply.status(401).send({ error: "Unauthorized" });
|
||||
|
||||
const { id } = request.params as { id: string };
|
||||
const result = await db.select().from(files).where(eq(files.id, id)).limit(1);
|
||||
if (!result[0]) return reply.status(404).send({ error: "File not found" });
|
||||
|
||||
await db.delete(files).where(eq(files.id, id));
|
||||
const dir = path.join(UPLOAD_DIR, id);
|
||||
await rm(dir, { recursive: true, force: true });
|
||||
|
||||
return reply.status(200).send({ ok: true });
|
||||
});
|
||||
|
||||
fastify.get("/health", async (_request, reply) => {
|
||||
return reply.send({ status: "ok", timestamp: new Date().toISOString() });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user