feat: misc — cooking mode, OpenAPI docs, email, storage, upload, homepage

Cooking mode step-by-step view. OpenAPI 3.1 spec auto-generated from Zod schemas.
Email lib (resend). Redis client. S3 storage helper. Photo upload endpoint.
Landing page. Short recipe URL redirect /r/[id]. API docs page.
This commit is contained in:
Arnaud
2026-07-01 08:11:31 +02:00
parent 3f96d1ea41
commit 9d9dfb46c6
26 changed files with 1427 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
import { S3Client, PutObjectCommand, DeleteObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const bucket = process.env["STORAGE_BUCKET"] ?? "epicure-uploads";
const endpoint = process.env["STORAGE_ENDPOINT"] ?? "http://localhost:9000";
const publicUrl = process.env["STORAGE_PUBLIC_URL"] ?? "http://localhost:9000";
const s3 = new S3Client({
region: process.env["STORAGE_REGION"] ?? "us-east-1",
endpoint,
forcePathStyle: true,
credentials: {
accessKeyId: process.env["STORAGE_ACCESS_KEY"] ?? "minioadmin",
secretAccessKey: process.env["STORAGE_SECRET_KEY"] ?? "minioadmin",
},
});
export async function createPresignedUploadUrl(key: string, contentType: string): Promise<string> {
const command = new PutObjectCommand({ Bucket: bucket, Key: key, ContentType: contentType });
return getSignedUrl(s3, command, { expiresIn: 300 });
}
export async function deleteObject(key: string): Promise<void> {
await s3.send(new DeleteObjectCommand({ Bucket: bucket, Key: key }));
}
export function getPublicUrl(key: string): string {
return `${publicUrl}/${bucket}/${key}`;
}