zscripts-token-savers/zconfig.example.json
KellyMichels 27fad897a0 fix(zdeploy): verify Next.js deploys on the server, not the public IP
The nextjs handler verified by requesting http://<ec2-ip>:<prod-port>/.
A compose stack behind the edge proxy normally publishes to 127.0.0.1
only, so that request can never be answered and every deploy ended with
"is the port open in the security group?" — pointing at a firewall rule
for an app that was already serving fine. No security-group change could
have made that probe succeed, which is what made the warning actively
harmful: it named the one fix guaranteed not to work, and opening the
port would have exposed the app directly, bypassing the proxy and TLS.

The nextjs handler now uses the precedence the python handler already
used: verify.port (curl localhost on the server) -> domain (Host-header
check through the edge proxy) -> an honest "NOT verified" instead of a
misleading warning.

Also follow redirects in the health check. An app whose "/" answers 307
(Next.js -> /login) returns a body of a few bytes that read as "not
ready"; -L fetches the page that actually renders. No effect on projects
that point verify.path at a plain 200 endpoint such as /health.

The example config now documents the verify block on the nextjs project,
and CHECKSUMS.txt is regenerated for the changed script.
2026-08-06 18:28:08 -05:00

113 lines
4.1 KiB
JSON

{
"_comment": "Copy this file to zconfig.json and fill in your values. zconfig.json is gitignored — never commit it.",
"ec2": {
"ip": "YOUR_SERVER_IP",
"user": "YOUR_SSH_USER",
"pemKey": "C:\\Users\\YourUser\\.ssh\\YourKey.pem",
"stackRoot": "/home/YOUR_SSH_USER/stack"
},
"paths": {
"temp": "C:\\YourRoot\\temp",
"backupsLocal": "C:\\YourRoot\\backups\\projects",
"backupsEc2": "C:\\YourRoot\\backups\\ec2",
"scriptsRoot": "C:\\YourRoot\\zscripts",
"oneDriveBackups": ""
},
"projects": {
"_comment": "Rename these keys to your own project names — the key IS the command argument: zstart pyapp, zdeploy viteapp, zbackup nextapp. Add as many projects as you like. Keys starting with _ are ignored.",
"pyapp": {
"label": "My Python App",
"kind": "python",
"localRoot": "C:\\YourRoot\\pyapp",
"startModule": "pyapp.main",
"_startApp_note": "ASGI/FastAPI app? Use \"startApp\": \"app.main:app\" instead of startModule (runs uvicorn --port <dev> --reload).",
"install": "-e .",
"ports": { "dev": 8080 },
"domain": "pyapp.yourdomain.com",
"start": {
"_comment": "Optional zstart pre-steps: gitPull runs 'git pull --ff-only' first; env sets variables for the server process.",
"gitPull": true,
"env": { "MYAPP_DEBUG": "1" }
},
"db": { "user": "pyapp_user", "name": "pyapp_db" },
"remote": {
"path": "/home/YOUR_SSH_USER/stack/pyapp",
"composeDir": "/home/YOUR_SSH_USER/stack/pyapp/docker",
"appService": "app"
},
"verify": {
"_comment": "Optional: after deploy, curl this ON the server (localhost:port+path) and require the substring. The accurate check for apps not published through the edge proxy.",
"port": 8080,
"path": "/health",
"expect": "\"status\":\"ok\""
},
"deploy": {
"zipName": "PyAppDeploy.zip",
"gitPull": true,
"exclude": ["docs"],
"_comment": "preserve: server-side files/dirs in the project dir that deploys must never delete (.env* files are always preserved)",
"preserve": ["keys", "seed-data"]
}
},
"viteapp": {
"label": "My Vite Site",
"kind": "vite",
"localRoot": "C:\\YourRoot\\viteapp",
"ports": { "dev": 5173 },
"domain": "www.yourdomain.com",
"remote": {
"path": "/home/YOUR_SSH_USER/stack/viteapp",
"containerName": "viteapp"
},
"deploy": { "zipName": "ViteAppDeploy.zip" }
},
"nextapp": {
"label": "My Next.js App",
"kind": "nextjs",
"localRoot": "C:\\YourRoot\\nextapp",
"ports": { "dev": 4173, "prod": 3000 },
"domain": "app.yourdomain.com",
"db": { "user": "nextapp_user", "name": "nextapp_db" },
"migrations": "prisma",
"remote": {
"path": "/home/YOUR_SSH_USER/stack/nextapp",
"appService": "web"
},
"verify": {
"_comment": "Optional: after deploy, curl this ON the server (localhost:port+path). Add it when compose publishes the app to 127.0.0.1 only, as it usually does behind the edge proxy — probing the public IP on that port can never answer. Redirects are followed, so a Next.js '/' that 307s to '/login' still verifies. Takes precedence over the domain check.",
"port": 3000,
"path": "/",
"expect": ""
},
"deploy": { "zipName": "NextAppDeploy.zip" }
},
"edge": {
"label": "Edge Nginx Proxy",
"kind": "edge",
"localRoot": "C:\\YourRoot\\edge",
"proxyContainer": "edge_proxy",
"certsSource": "",
"remote": {
"path": "/home/YOUR_SSH_USER/stack/edge"
}
},
"analytics": {
"label": "Analytics (any docker compose app)",
"kind": "docker",
"localRoot": "C:\\YourRoot\\analytics",
"domain": "analytics.yourdomain.com",
"remote": {
"path": "/home/YOUR_SSH_USER/stack/analytics"
}
}
}
}