Files
famapp/src/app/api/uploads/[...key]/route.ts
T
ginnoir 2fd0677c5f feat: add garden module infrastructure (task 70)
- Add MinIO service to compose.yaml with named volume
- Add generic /api/uploads POST+GET route with auth, 5 MB, image/* guards
- Add src/lib/minio.ts singleton client with ensureBucket helper
- Add garden Drizzle schema: containers, plants, care_logs, care_schedules, species_cache
- Register garden module in src/modules/index.ts and src/lib/db.ts
- Apply migration 0015_garden_schema.sql
- Add MINIO_* and PERENUAL_API_KEY to .env.example
- Add task briefs 70-75 for the full garden feature arc
- Add uploads.spec.ts E2E test (RED → GREEN on route auth guard)
2026-06-01 19:23:19 -05:00

35 lines
1.1 KiB
TypeScript

import { NextResponse } from "next/server";
import { minioClient, MINIO_BUCKET } from "@/lib/minio";
export const runtime = "nodejs";
export async function GET(_request: Request, { params }: { params: Promise<{ key: string[] }> }) {
const { key } = await params;
const objectKey = key.join("/");
try {
const stat = await minioClient.statObject(MINIO_BUCKET, objectKey);
const stream = await minioClient.getObject(MINIO_BUCKET, objectKey);
const chunks: Buffer[] = [];
for await (const chunk of stream) {
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as Uint8Array));
}
const buffer = Buffer.concat(chunks);
const metaData = stat.metaData as Record<string, string> | undefined;
const contentType =
metaData?.["content-type"] ?? metaData?.["Content-Type"] ?? "application/octet-stream";
return new Response(buffer, {
headers: {
"Content-Type": contentType,
"Cache-Control": "public, max-age=31536000, immutable",
"Content-Length": String(buffer.length),
},
});
} catch {
return NextResponse.json({ error: "Not found" }, { status: 404 });
}
}