From 7e139d91b82e8e94db34b217d2499650fdc597fa Mon Sep 17 00:00:00 2001 From: Thomas Joise Date: Tue, 21 Jul 2026 01:39:12 +0530 Subject: [PATCH] docs(deploy): record go-live (D39) and correct the deploy references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit deploy/PATHS-AND-NAMES.md now reflects reality: Docker Compose V2 isn't installed on the box (raw docker build/run instead), DB connection lives in db-pg.ts not server.ts, real password is committed (accepted, not a CHANGE_ME_PASSWORD placeholder). docs/DEPLOY-HQ.md marked superseded — it described an RDS/GoDaddy/.com plan that isn't what shipped. STATUS.md and 06-DECISIONS.md (D39) record the actual go-live: real data carried over from local Postgres (not "born empty + APEX import"), and the npm/cli#4828 Dockerfile fix. Co-Authored-By: Claude Sonnet 5 --- STATUS.md | 5 +++++ deploy/PATHS-AND-NAMES.md | 35 +++++++++++++++++++++-------- docs/06-DECISIONS.md | 47 +++++++++++++++++++++++++++++++++++++++ docs/DEPLOY-HQ.md | 6 +++++ 4 files changed, 84 insertions(+), 9 deletions(-) diff --git a/STATUS.md b/STATUS.md index 96ce198..1a2019d 100644 --- a/STATUS.md +++ b/STATUS.md @@ -61,6 +61,11 @@ excluded) and a *settled → document* column — the money engine is unchanged. **stalled-onboarding** query, an owner **onboarding-template editor**, and mobile fixes (the record header stacks instead of overlapping the title). +**2026-07-21 (D39): live.** HQ is deployed at **`https://hq.simssoftware.in`** — Docker on the +existing company EC2 box, the shared `accurate-postgres` container, real client data carried +over (273 clients, 14 staff, 156 documents, 1826 tickets). See `deploy/PATHS-AND-NAMES.md` for +the accurate deployment reference and `docs/06-DECISIONS.md` D39 for what deviated from plan. + ## Verification state diff --git a/deploy/PATHS-AND-NAMES.md b/deploy/PATHS-AND-NAMES.md index d966fe0..4273b1e 100644 --- a/deploy/PATHS-AND-NAMES.md +++ b/deploy/PATHS-AND-NAMES.md @@ -5,6 +5,10 @@ If a name here disagrees with a command you're about to run, this file wins. Ser existing company **Amazon Linux 2** EC2 box; HQ runs as its own Docker container beside the other apps. (D20 target, revised: `.in` not `.com`, Docker not RDS — see notes at bottom.) +> ✅ **Live** at `https://hq.simssoftware.in` since 2026-07-21 (D39 in `docs/06-DECISIONS.md`). +> `docs/DEPLOY-HQ.md` describes an earlier RDS/GoDaddy/`.com` plan that was **not** what +> shipped — this file is the accurate one. + ## Identity | Thing | Value | @@ -27,7 +31,7 @@ other apps. (D20 target, revised: `.in` not `.com`, Docker not RDS — see notes | Path | What it is | |---|---| | `/usr/share/nginx/sims-hq` | **App folder** — the whole project (owned by `ec2-user`). Docker builds from here. | -| `/usr/share/nginx/sims-hq/apps/hq/src/server.ts` | Holds the **hardcoded DB connection** (`HARDCODED_DATABASE_URL`) — no `.env` is used. | +| `/usr/share/nginx/sims-hq/apps/hq/src/db-pg.ts` | Holds the **hardcoded DB connection** (`HARDCODED_DATABASE_URL`) — moved here from `server.ts` (pre-ship audit FIX A, one shared `resolveDbUrl()`). No `.env` is used. | | `/usr/share/nginx/sims-hq/deploy/sims.hq.conf` | nginx vhost **source** (copied into nginx below). | | `/etc/nginx/conf.d/sims.hq.conf` | **Live nginx vhost** (must end in `.conf`; `sudo` to edit). | | `/etc/ssl/sims/sims.crt` · `/etc/ssl/sims/sims.key` | Wildcard TLS cert for `*.simssoftware.in` (shared). | @@ -65,16 +69,19 @@ activates the hardcoded values; dev and the test suite are unaffected and still | Setting | Where | Value | |---|---|---| -| DB connection | `apps/hq/src/server.ts` → `HARDCODED_DATABASE_URL` | `postgres://postgres:@host.docker.internal:5432/hq` — **replace `CHANGE_ME_PASSWORD` on the box, then rebuild** | +| DB connection | `apps/hq/src/db-pg.ts` → `HARDCODED_DATABASE_URL` | `postgres://postgres:inv123@host.docker.internal:5432/hq` | | Owner login email | `apps/hq/src/seed.ts` → `OWNER_EMAIL` default | `simssoftware13@gmail.com` | | Port | `HQ_PORT` env, else default | `5182` | | PIN pepper | *(unset)* | password hashes are un-peppered — a future hardening | | Gmail secret key | *(unset)* | set only when connecting Gmail | -> ⚠️ **To change the DB later:** edit `HARDCODED_DATABASE_URL` in `server.ts` on the box, then -> `sudo docker compose up -d --build` (a rebuild, not just a restart). The real password lives -> only in the box's copy — **never push the filled-in `server.ts` to Gitea** (the repo keeps -> the `CHANGE_ME_PASSWORD` placeholder). +> ⚠️ **Deviation from the original plan (accepted, D39):** the real password is committed to +> Gitea, not kept as a `CHANGE_ME_PASSWORD` placeholder — Gitea is internal/single-user, so +> this was accepted for now. Rotating this password is the top item whenever the hardening +> pass happens; whoever does it must also update the value in `db-pg.ts` and rebuild. +> +> **To change the DB later:** edit `HARDCODED_DATABASE_URL` in `db-pg.ts`, then rebuild +> (see Everyday commands below). ## Owner login @@ -85,14 +92,24 @@ activates the hardcoded values; dev and the test suite are unaffected and still ## Everyday commands (run from `/usr/share/nginx/sims-hq`) +> ⚠️ **Docker Compose V2 is not installed on this box** (only the standalone `docker-compose` +> 1.21.0 from 2018, too old for this file's `extra_hosts: host-gateway`). `docker-compose.yml` +> is kept as the source of truth for what the container needs, but it's run manually via plain +> `docker build`/`docker run` below until the Compose V2 plugin is installed (see install +> command in `docs/06-DECISIONS.md` D39, or run `docker compose version` to check if someone +> already added it). + | Do this | Command | |---|---| -| Build + start | `sudo docker compose up -d --build` | +| Build | `sudo docker build -t sims-hq .` | +| Start (first time / after removing the old container) | `sudo docker run -d --name sims-hq --restart unless-stopped -p 127.0.0.1:5182:5182 --add-host host.docker.internal:host-gateway sims-hq` | +| Rebuild + replace (code changed) | `sudo docker stop sims-hq && sudo docker rm sims-hq`, then Build + Start above | | View logs | `sudo docker logs sims-hq` (`-f` to follow) | -| Restart | `sudo docker compose restart hq` | -| Stop | `sudo docker compose down` | +| Restart (no code change) | `sudo docker restart sims-hq` | +| Stop | `sudo docker stop sims-hq` | | Health check | `curl -s localhost:5182/api/health` → `{"ok":true}` | | nginx test + reload | `sudo nginx -t && sudo systemctl reload nginx` | +| Deploy code changes | WinSCP-copy the whole project folder (except `node_modules`, `dist`, `.git`, `.env`, `data`, `*.log`) into `/usr/share/nginx/sims-hq`, overwriting — this box doesn't use `git pull` | ## Notes / decisions - Domain is **`.in`** (not `.com`): the wildcard cert is `*.simssoftware.in` and every other diff --git a/docs/06-DECISIONS.md b/docs/06-DECISIONS.md index 43d43ab..030d2f6 100644 --- a/docs/06-DECISIONS.md +++ b/docs/06-DECISIONS.md @@ -584,6 +584,17 @@ scripts (`resolveDbUrl`, D19) and logs the resolved host/db before dumping — t `scripts/backup.mjs` read `DATABASE_URL` straight off the environment and so could never reach production, where that variable is not exported. +**Working invocation (final review FIX 3):** `resolveDbUrl` only returns the hardcoded +production connection when `NODE_ENV=production` — and an operator's own interactive shell on +the prod box does NOT have that set (only the deployed service's env does), so a bare +`npm run backup` typed by hand still resolves nothing and refuses. Run the backup with ONE of: +- `NODE_ENV=production npm run backup` — matches how the deployed service resolves it, or +- `DATABASE_URL=postgres://… npm run backup` — explicit target, overrides resolution. + +The refusal error itself now names both of these exact commands, so an operator who hits it +can act without reading source. This does not change `resolveDbUrl`'s production semantics and +does not embed the connection string anywhere new. + ## D34 — Employee master import + invited/first-login flag (2026-07-19) Imported the 11-row employee master (`empmaster.xlsx`) into `staff_user` via `scripts/import-employees.ts`: username = first-name slug, default password `@123` @@ -655,3 +666,39 @@ Also: a **stalled-onboarding** query for the dashboard/MIS, and a mobile fix so stacks instead of overlapping the client name. Spec + plans under `docs/superpowers/`. + +## D39 — Go-live: hq.simssoftware.in ✅ DELIVERED (2026-07-20/21) +HQ is live in production at **`https://hq.simssoftware.in`**, on the existing company EC2 box, +superseding the RDS/GoDaddy/`.com` plan in `docs/DEPLOY-HQ.md` (that doc predates the actual +setup — `deploy/PATHS-AND-NAMES.md` is the accurate reference; DEPLOY-HQ.md is marked superseded). + +**Deployed as built, three deviations from the original runbook:** +- **Docker Compose V2 isn't installed** on the box (only the old standalone `docker-compose` + 1.21.0, too old for this file's `extra_hosts: host-gateway`). Deployed via plain + `docker build` + `docker run --add-host host.docker.internal:host-gateway ...` instead — + the exact equivalent of `docker compose up -d --build`. `deploy/PATHS-AND-NAMES.md` + documents both forms; installing the Compose v2 plugin is a later nice-to-have, not a blocker. +- **`Dockerfile` drops `package-lock.json` before `npm install`** (new step). A Windows-generated + lockfile hit npm/cli#4828 — `@rollup/rollup-linux-x64-gnu` failed to resolve during the + container build even on a clean install. Removing the lockfile forces same-platform resolution. +- **Real data was carried over, not "born empty + APEX import."** The working dataset turned out + to live in a local Postgres instance (273 clients, 14 staff, 156 documents, 1826 tickets, etc.), + not the local SQLite file assumed by D19/D20 — SQLite only had 2 leftover test accounts. A + Postgres→Postgres copy (`apps/hq/scripts/migrate-pg-to-pg.ts`) wiped the target `hq` database's + schema, rebuilt it via the app's own migrations, then copied every table across in one + transaction (FK checks disabled for the load via `session_replication_role=replica`). The + APEX CSV importer is no longer needed for the initial 300 clients — they're already in. + (`apps/hq/scripts/migrate-sqlite-to-pg.ts` was the first attempt, against the wrong source; + left in the repo, harmless — it refuses to run against a non-empty target.) + +**Known accepted debt, deferred on purpose (owner decision):** +- `HARDCODED_DATABASE_URL` (`db-pg.ts`) carries the real Postgres password in git history — + Gitea is internal/single-user, so this is accepted for now; rotating it is the top item + whenever the hardening pass happens. +- Postgres port 5432 is open to the public internet (`0.0.0.0`), not restricted to the EC2 + security group — same deferred bucket. +- `SIMS_PIN_PEPPER` is still unset (login hashes un-peppered) — same bucket. +- Shared `postgres` superuser role, not a dedicated least-privilege `hq` role — same bucket. + +**Still open, not yet done:** `share.base_url` setting, and a backup cron for the shared +Postgres (`pg_dump` — no RDS automated snapshots here). diff --git a/docs/DEPLOY-HQ.md b/docs/DEPLOY-HQ.md index cc1d45f..ade2568 100644 --- a/docs/DEPLOY-HQ.md +++ b/docs/DEPLOY-HQ.md @@ -1,5 +1,11 @@ # Go-Live Runbook — HQ ops console (`apps/hq`) +> ⚠️ **SUPERSEDED (2026-07-21, D39).** HQ is live at `https://hq.simssoftware.in` — Docker on +> the existing EC2 box against the shared `accurate-postgres` container, **not** the RDS + +> GoDaddy + `.com` plan below. For the actual setup, commands, and paths, use +> **`deploy/PATHS-AND-NAMES.md`** instead. This file is kept for its Postgres-engine background +> (§7) but its go-live steps (§0–§6, §6b) do not reflect what shipped. + How to put the internal console on your AWS box for real use. The store product is unaffected (it stays local, D14); this is the vendor-side cloud app doc 11 always prescribed. Engine: SQLite for dev, **Postgres in production via `DATABASE_URL`**