fix(ops): switch pm2 to standalone server + fix wholesale_settings query
Deploy to route.crispygoat.com / deploy (push) Successful in 4m14s
Deploy to route.crispygoat.com / deploy (push) Successful in 4m14s
Three problems were silently breaking the prod Tuxedo redesign and
the offline DB error swallowed all visibility into them:
1. `next start` against `output: "standalone"` is unsupported. Next.js
prints "next start does not work with output: standalone" and
silently disables the image optimizer — every `/_next/image?url=...`
returned "url parameter is not allowed". pm2 was previously
started as `pm2 start npm -- start -- -p 3100`. Switched to
`node /home/tyler/route-commerce/scripts/start-standalone.cjs`.
2. The standalone server reads `.next/static/` relative to itself, so
deploy must `cp -r .next/static .next/standalone/.next/static` on
every sync. Without this, every `_next/static/chunks/*.js` returns
404 and the page hydration dies. Added to the deploy workflow.
3. bash's `set -a; . ./.env` truncates DATABASE_URL at the `&` in
`&channel_binding=require`, so the standalone server boots with
empty DB env. `getBrandSettingsPublic` then caught the empty-pool
error and returned `{success:false, error:'Failed to fetch brand settings'}`,
so `state.heroImageUrl` stayed null and the hero poster never
rendered. The new `scripts/start-standalone.cjs` parses .env in
Node (which handles `&` correctly) and exec's the server with the
loaded env.
4. `getBrandSettingsPublic` queried `ws.wholesale_enabled`, which
no longer exists in `wholesale_settings`. The schema migration
renamed it to `online_payment_enabled`. Switched the column
reference and added a console.error log so future drift surfaces
instead of being swallowed by the empty catch.
Plus `next.config.ts` had `hostname: "s3.crispygoat.com"` added
under `images.remotePatterns` so the optimizer accepts MinIO URLs.
This commit is contained in:
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Production startup wrapper for `next start` when `output: "standalone"`
|
||||
* is set in next.config.ts. Loads .env from a known absolute location,
|
||||
* then exec's the standalone server with the loaded env.
|
||||
*
|
||||
* Why this exists:
|
||||
* 1. `next start` against a standalone build is unsupported by Next.js —
|
||||
* it prints "next start does not work with output: standalone
|
||||
* configuration. Use `node .next/standalone/server.js` instead" — and
|
||||
* silently disables the image optimizer (every `/_next/image?url=...`
|
||||
* returned "url parameter is not allowed"), which is why all the
|
||||
* WP-imported brand imagery never rendered. See commit 129c9d2.
|
||||
*
|
||||
* 2. bash's `set -a; . ./.env` truncates DATABASE_URL at the `&` in
|
||||
* `&channel_binding=require`, so the server boots with empty DB env
|
||||
* and the public storefront falls back to dark gradient backgrounds.
|
||||
* A small Node loader sidesteps the bash word-splitting.
|
||||
*
|
||||
* 3. The standalone server reads `.next/static/` relative to itself, so
|
||||
* the deploy workflow must `cp -r .next/static .next/standalone/.next/static`
|
||||
* after every sync.
|
||||
*
|
||||
* Usage:
|
||||
* PORT=3100 HOSTNAME=0.0.0.0 \
|
||||
* NODE_BIN=/home/tyler/.cache/act/tool_cache/node/22.22.3/x64/bin/node \
|
||||
* node /home/tyler/route-commerce/scripts/start-standalone.cjs
|
||||
*
|
||||
* pm2 typically overrides interpreter with its own node and sets `cwd`
|
||||
* arbitrarily, so we resolve the project root from the script's own path
|
||||
* instead of `process.cwd()`.
|
||||
*/
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const APP_DIR = path.resolve(__dirname, "..");
|
||||
const ENV_PATH = process.env.ENV_FILE || path.join(APP_DIR, ".env");
|
||||
const STANDALONE = path.join(APP_DIR, ".next", "standalone", "server.js");
|
||||
|
||||
if (!fs.existsSync(STANDALONE)) {
|
||||
console.error(`[start-standalone] Missing standalone server at ${STANDALONE}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (fs.existsSync(ENV_PATH)) {
|
||||
for (const line of fs.readFileSync(ENV_PATH, "utf8").split("\n")) {
|
||||
const m = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
|
||||
if (m && !process.env[m[1]]) process.env[m[1]] = m[2];
|
||||
}
|
||||
console.log(`[start-standalone] loaded env from ${ENV_PATH}`);
|
||||
} else {
|
||||
console.warn(`[start-standalone] no .env at ${ENV_PATH}; relying on existing env`);
|
||||
}
|
||||
|
||||
// Hand off to the standalone server. Re-exec keeps the process title at
|
||||
// `next-server` (next-start.sh sets it) and matches `node .next/standalone/server.js`.
|
||||
const { spawn } = require("child_process");
|
||||
const child = spawn(process.execPath, [STANDALONE], { stdio: "inherit", env: process.env });
|
||||
child.on("exit", (code) => process.exit(code ?? 0));
|
||||
for (const sig of ["SIGINT", "SIGTERM"]) {
|
||||
process.on(sig, () => child.kill(sig));
|
||||
}
|
||||
Reference in New Issue
Block a user