The panel's HTTP client does not follow redirects. It asks for /api/setup/ with a trailing slash, Next answered 308 to normalise it, and the firmware reported "returned code is not OK. Code - 308" and gave up. Never having obtained a token, it then called /api/display with an empty one, got 401, and told the user it could not reach the API. Not TLS, not the network, not the port — a slash. Two earlier fixes were aimed at hypotheses the evidence did not support: a certificate chain the firmware genuinely cannot validate, and a port the shop's network turned out not to block. Both were reasoned from silence, because neither Traefik nor a production Next server logs requests by default. The answer came from a packet capture, and from the device's own words. /api/log now accepts a report from a panel that cannot authenticate. Refusing it with a 401 threw away the one diagnostic that mattered: the firmware was saying exactly what was wrong and we were discarding the message. Nothing is stored — the rows would reference a device that does not exist — but it reaches the server log, and the route was already rate-limited. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012cSY9pVhZmJUKNN7wf1Myd
47 lines
1.9 KiB
TypeScript
47 lines
1.9 KiB
TypeScript
import type { NextConfig } from 'next';
|
|
|
|
const nextConfig: NextConfig = {
|
|
// Standalone output keeps the production image small: only the traced
|
|
// dependencies ship, not the whole node_modules tree.
|
|
output: 'standalone',
|
|
reactStrictMode: true,
|
|
// Next writes its own AGENTS.md/CLAUDE.md otherwise; this project documents
|
|
// itself in README.md and PLAN.md.
|
|
agentRules: false,
|
|
|
|
// The panel's HTTP client does not follow redirects: it reports any non-2xx
|
|
// as "returned code is not OK" and gives up. The firmware asks for
|
|
// /api/setup/ with a trailing slash, which Next would answer with a 308 —
|
|
// so the device never obtained a token, then failed /api/display with an
|
|
// empty one, and reported that it could not reach the API at all.
|
|
//
|
|
// Serving both spellings is the fix. The redirect is disabled and the
|
|
// slashed paths are rewritten, rather than the device being asked to behave
|
|
// differently: it cannot.
|
|
skipTrailingSlashRedirect: true,
|
|
async rewrites() {
|
|
return [
|
|
{ source: '/api/setup/', destination: '/api/setup' },
|
|
{ source: '/api/display/', destination: '/api/display' },
|
|
{ source: '/api/log/', destination: '/api/log' },
|
|
{ source: '/api/health/', destination: '/api/health' },
|
|
// Admin pages keep working when someone types the slash by hand, which
|
|
// Next would otherwise have redirected for us.
|
|
{ source: '/admin/', destination: '/admin' },
|
|
];
|
|
},
|
|
// These must be required from node_modules at runtime, not bundled.
|
|
// satori loads harfbuzz and yoga as WebAssembly by relative path; bundling
|
|
// rewrites that path and the renderer dies with a missing hb.wasm. resvg is
|
|
// a native addon, and node-cron is started from instrumentation.ts.
|
|
serverExternalPackages: [
|
|
'satori',
|
|
'yoga-wasm-web',
|
|
'harfbuzzjs',
|
|
'@resvg/resvg-js',
|
|
'node-cron',
|
|
],
|
|
};
|
|
|
|
export default nextConfig;
|