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)
This commit is contained in:
ginnoir
2026-06-01 19:23:19 -05:00
parent 5dcd49d4c9
commit 2fd0677c5f
21 changed files with 1250 additions and 1 deletions
+8 -1
View File
@@ -4,9 +4,16 @@ import * as coreSchema from "@/modules/_core/schema";
import * as calendarSchema from "@/modules/calendar/schema";
import * as listsSchema from "@/modules/lists/schema";
import * as notesSchema from "@/modules/notes/schema";
import * as gardenSchema from "@/modules/garden/schema";
const client = postgres(process.env["DATABASE_URL"]!);
const schema = { ...coreSchema, ...calendarSchema, ...listsSchema, ...notesSchema };
const schema = {
...coreSchema,
...calendarSchema,
...listsSchema,
...notesSchema,
...gardenSchema,
};
export const db = drizzle(client, { schema });
+29
View File
@@ -0,0 +1,29 @@
import { Client } from "minio";
const rawEndpoint = process.env.MINIO_ENDPOINT ?? "http://localhost:9000";
const endpointUrl = new URL(rawEndpoint);
export const minioClient = new Client({
endPoint: endpointUrl.hostname,
port: endpointUrl.port
? parseInt(endpointUrl.port)
: endpointUrl.protocol === "https:"
? 443
: 80,
useSSL: endpointUrl.protocol === "https:",
accessKey: process.env.MINIO_ROOT_USER ?? "",
secretKey: process.env.MINIO_ROOT_PASSWORD ?? "",
});
export const MINIO_BUCKET = process.env.MINIO_BUCKET ?? "garden";
let bucketReady = false;
export async function ensureBucket(): Promise<void> {
if (bucketReady) return;
const exists = await minioClient.bucketExists(MINIO_BUCKET);
if (!exists) {
await minioClient.makeBucket(MINIO_BUCKET);
}
bucketReady = true;
}