Move uploaded images to disk storage, fix Next 16 async params/searchParams
Homepage was rendering 71MB of HTML because every uploaded image (gallery, products, proofs, custom requests, design previews, invoices, receipts) was stored as base64 in Postgres and double-embedded via RSC hydration payloads. Adds src/lib/storage.ts (sharp-based compression, disk storage under public/uploads/) and a new /api/design-uploads endpoint for images added inside the design tool, migrates existing rows, and drops the now-unused base64 columns and the dead ProductImage table. Also fixes a systemic bug from the Next.js 16 upgrade where dynamic pages across the app still destructured params/searchParams synchronously instead of awaiting them as Promises, which was silently breaking filters/params and crashing /products/[slug] outright. Updates README.md and SETUP_AND_BUILD.md to reflect Postgres + Next.js 16 and document the new image storage architecture and backup requirements. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
e23f201d3d
commit
396347b982
+5
-20
@@ -72,21 +72,6 @@ model Product {
|
||||
manualSaleItems ManualSaleItem[]
|
||||
stockLevels StockLevel[]
|
||||
purchaseItems PurchaseItem[]
|
||||
productImages ProductImage[]
|
||||
}
|
||||
|
||||
// Bulk-uploaded product images with auto-detected colors. Supports any product type
|
||||
// (tees, hoodies, mugs, etc.). User uploads a folder, colors are extracted, then saved
|
||||
// as color-swatch references for the product's color palette.
|
||||
model ProductImage {
|
||||
id String @id @default(cuid())
|
||||
productId String
|
||||
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
||||
color String // hex color detected from the image, e.g. "#1EA7E0"
|
||||
imageDataUrl String // the uploaded image itself as a data URL
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@unique([productId, color])
|
||||
}
|
||||
|
||||
// Quantity on hand for one product/colour/size combination. Purchases increase
|
||||
@@ -114,7 +99,7 @@ model Purchase {
|
||||
id String @id @default(cuid())
|
||||
supplierName String
|
||||
purchasedAt DateTime @default(now()) // invoice date, editable — not entry time
|
||||
invoiceDataUrl String? // the uploaded invoice itself, as a data URL
|
||||
invoiceUrl String? // disk path under /uploads/invoices/...
|
||||
invoiceFileName String?
|
||||
total Int // pence — sum of line totals, recomputed server-side
|
||||
notes String?
|
||||
@@ -147,7 +132,7 @@ model Expense {
|
||||
description String
|
||||
category String @default("Stock purchases") // "Stock purchases", "Postage", others as user enters them
|
||||
amount Int // pence
|
||||
receiptDataUrl String? // uploaded receipt as data URL
|
||||
receiptUrl String? // disk path under /uploads/receipts/...
|
||||
receiptFileName String?
|
||||
notes String?
|
||||
purchaseId String? @unique // null = standalone expense; set = auto-created from a Purchase (one-to-one)
|
||||
@@ -167,7 +152,7 @@ model DesignProof {
|
||||
color String
|
||||
quantity Int @default(1)
|
||||
unitPrice Int // cents — defaults to the product's base price if not overridden
|
||||
imageDataUrl String // the finished design image you uploaded, shown as-is to the customer
|
||||
imageUrl String // disk path under /uploads/proofs/...
|
||||
note String? // optional message shown alongside the design
|
||||
status String @default("PENDING") // PENDING | APPROVED
|
||||
createdAt DateTime @default(now())
|
||||
@@ -333,7 +318,7 @@ model CustomRequest {
|
||||
customerName String
|
||||
customerEmail String
|
||||
customerPhone String?
|
||||
imageDataUrl String // the photo they want us to work with
|
||||
imageUrl String // disk path under /uploads/custom-requests/...
|
||||
note String?
|
||||
status String @default("NEW") // NEW | DONE
|
||||
createdAt DateTime @default(now())
|
||||
@@ -430,7 +415,7 @@ model GalleryCategory {
|
||||
|
||||
model GalleryPhoto {
|
||||
id String @id @default(cuid())
|
||||
imageDataUrl String // base64 data URL
|
||||
imageUrl String // disk path under /uploads/gallery/...
|
||||
caption String?
|
||||
categoryId String? // nullable — a photo can be uncategorized
|
||||
category GalleryCategory? @relation(fields: [categoryId], references: [id])
|
||||
|
||||
Reference in New Issue
Block a user