The first iPhone driver photos (Thabo, 2026-10-20, PO202610003TEL and PO26100702STK) arrived as HEIC. The AI only takes JPEG/PNG/WebP/GIF, so the reads were saved as "skipped — Not an image (.heic)" and Check with AI kept answering 0 files read, 0 lines checked; skipped reads were never retried. - heic-to-jpeg.ts: HEIC/HEIF → JPEG with heic-convert (pure JS/WASM; sharp's prebuilt binaries can't decode HEVC HEIC), downscaled to 1600px like the PB thumb. defaultFetchFile uses it (PB has no HEIC thumbs). - HEIC/HEIF count as readable images; a read an older version "skipped" for a now-readable type is re-read, and the tick treats it as unread. - next.config: heic-convert / heic-decode / libheif-js are server externals (libheif's WASM loader uses a dynamic require). Verified on the live photo: 1 MB HEIC → 315 KB JPEG in ~1 s, readable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
57 lines
3.2 KiB
TypeScript
57 lines
3.2 KiB
TypeScript
import type { NextConfig } from "next";
|
|
|
|
const nextConfig: NextConfig = {
|
|
output: "standalone",
|
|
// Server-only deps we don't want webpack to touch. Both pdf-parse and
|
|
// its transitive pdfjs-dist use ES2022 features + top-level `__esModule`
|
|
// marker that webpack's RSC runtime chokes on
|
|
// ("Object.defineProperty called on non-object"). Marking them external
|
|
// means Node resolves them at runtime from node_modules instead of going
|
|
// through webpack's loader — so pdfjs-dist's init runs in a clean CJS
|
|
// context, the way its author expects.
|
|
// heic-convert → heic-decode → libheif-js loads a WASM bundle with a
|
|
// dynamic require webpack can't follow; load it from node_modules too.
|
|
serverExternalPackages: ["pdf-parse", "pdfjs-dist", "@napi-rs/canvas", "heic-convert", "heic-decode", "libheif-js"],
|
|
// Dev server blocks cross-origin requests to its own dev resources by
|
|
// default in Next 16, which shows up as a page stuck on the loading
|
|
// spinner when you open the app from your phone on the LAN.
|
|
//
|
|
// IMPORTANT — this list is matched with `isCsrfOriginAllowed`, which
|
|
// splits the host on "." and compares label by label, treating `*` as
|
|
// exactly ONE label. Two consequences, both learned the hard way:
|
|
//
|
|
// - A bare "*" is useless. It does not mean "any host" — checked
|
|
// against Next 16.2.6, isCsrfOriginAllowed("192.168.1.35", ["*"])
|
|
// is false. It was in this list for months doing nothing.
|
|
// - "192.168.*" is also useless for the same reason: it is two
|
|
// labels against four.
|
|
// - "192.168.1.*" DOES work, and survives the router handing out a
|
|
// different address on a new DHCP lease. The previous version
|
|
// hard-coded individual IPs (.42, .122, .128); when the lease
|
|
// moved to .35 the phone silently stopped loading the app.
|
|
//
|
|
// So: wildcard the last label of the LAN subnet, keep the real
|
|
// hostname, and leave the stale per-address entries out.
|
|
allowedDevOrigins: ["127.0.0.1", "localhost", "192.168.1.*", "coolify.mywire.org"],
|
|
// The agent (and any external tools) sometimes reach for the PocketBase
|
|
// collection name verbatim — `order_items`, `stock_items`, `quote_items`
|
|
// — and get a 404 because the route folders use hyphens (`order-items`).
|
|
// Redirect the underscore variants to the hyphenated route so the agent
|
|
// stops confusing the PB collection name with the URL path.
|
|
async redirects() {
|
|
return [
|
|
{ source: "/api/v1/order_items", destination: "/api/v1/order-items", permanent: true },
|
|
{ source: "/api/v1/stock_items", destination: "/api/v1/stock-items", permanent: true },
|
|
{ source: "/api/v1/quote_items", destination: "/api/v1/quote-items", permanent: true },
|
|
{ source: "/api/v1/stock_purchases", destination: "/api/v1/stock-purchases", permanent: true },
|
|
{ source: "/api/v1/stock_quick_draft", destination: "/api/v1/stock-quick-draft", permanent: true },
|
|
// Counter PWA shortcut — keeps the install URL short.
|
|
{ source: "/counter", destination: "/new-order", permanent: true },
|
|
// TV dashboard shortcut — /tv → /pin (which itself bounces
|
|
// to /dashboard once the cookie is valid).
|
|
{ source: "/tv", destination: "/pin", permanent: true },
|
|
];
|
|
},
|
|
};
|
|
|
|
export default nextConfig;
|