bc2b648cc481478984e3f6813c4e7ceee6910ba4
9 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2ca2c8de44 |
Update caching headers to ensure all devices get the latest site version
Modify Nginx configuration to include aggressive caching headers for service worker and index.html to prevent stale content on various devices. |
||
|
|
608e15cf91 |
Task #589: Live build stamp + server start time in System Updates panel
Problem: After `git pull && docker compose up -d --build api` on the
Mac, the admin → System Updates panel kept showing the same version
(0.1.0-dev) and the same build (20260517.8a5bd5db) because both come
from version.json, a hand-edited file that nothing rewrites on every
build. No visible signal that a new image was actually running.
Two independent signals were added — neither needs editing version.json
by hand:
1) Auto-stamp version.json at Docker build time
- docker/api-server.Dockerfile: new ARGs GIT_SHA and BUILD_DATE.
A `node -e` step rewrites version.json's `build` field to
`${YYYYMMDDTHHmm}.${sha}` (e.g. 20260518T1042.71da8e01). When
the args are missing/"unknown", version.json is left untouched
so plain `docker build` and Replit `pnpm dev` still work.
- docker-compose.yml: api.build.args wires through GIT_SHA and
BUILD_DATE with `${GIT_SHA:-unknown}` fallbacks.
- scripts/redeploy.sh: exports GIT_SHA (git rev-parse --short HEAD)
and BUILD_DATE (date -u +%Y-%m-%dT%H:%M:%SZ) before `docker
compose build`, so the helper script just works.
2) startedAt timestamp from the API
- artifacts/api-server/src/routes/system.ts: SERVER_STARTED_AT
captured at module load (frozen for the process lifetime) and
added to every branch of GET /system/version (not-configured,
error, invalid-version, up-to-date, update-available, catch).
- lib/api-spec/openapi.yaml: added `startedAt: date-time` to
SystemVersionResponse (required). Regenerated api-client-react
and api-zod via `pnpm --filter @workspace/api-spec run codegen`.
3) Admin UI surfacing both signals
- artifacts/tx-os/src/pages/admin.tsx: new formatBuildStamp parses
`YYYYMMDD[THHmm].sha` and renders a localized date next to the
raw token. A new "Server started" line below the build number
formats the boot timestamp via the existing formatChecked helper.
- artifacts/tx-os/src/locales/{en,ar}.json: added
admin.systemUpdates.serverStartedAt ("Server started" /
"بدأ تشغيل الخادم").
4) Docs
- replit.md: documented how to verify a real redeploy via the
System Updates panel and the manual one-liner for `docker
compose build` without the helper script.
Verified: codegen succeeds, API workflow restarted clean, Vite served
fine. Pre-existing typecheck errors in push.ts and font-settings are
unrelated and untouched.
Mac next steps:
cd ~/Downloads/TX && git pull && ./scripts/redeploy.sh
Then open admin → System Updates: build line should show a new
`YYYYMMDDTHHmm.sha` stamp and "Server started" should reflect the
new boot time.
|
||
|
|
ff508eeaba |
Task #587: Fix Docker build — copy version.json into build stage
The api-server docker build failed on the Mac with:
ERROR: Could not resolve "../../../../version.json"
src/routes/system.ts:4
`version.json` lives at the repo root and is tracked in git, but
`docker/api-server.Dockerfile` never copied it into the `build` stage,
so esbuild (running in /app/artifacts/api-server) couldn't resolve the
`../../../../version.json` import added by commit
|
||
|
|
7f86db26db |
Add bilingual support for notifications and automatic HTTPS
Implement bilingual text for push notifications, allowing users to receive them in their preferred language. Automatically configure HTTPS with Let's Encrypt for custom domains to ensure persistent subscriptions. |
||
|
|
d1d4f6baaa |
feat(setup): Stage 1 first-time setup wizard backend (no UI)
Task #534 — backend, infra, tooling. UI ships in Stage 2 (#535). Backend - New system_settings table (id=1 singleton): installed flag, base_url, local_domain, local_ip, https_mode, app_version. Pushed to dev DB. - New /api/setup/status (open) and /api/setup/{validate,complete} (gated by requireSetupOpen — 409 once installed). - completeInstall is fully transactional: pg_advisory_xact_lock serializes concurrent callers, double-gates on installed flag and admin existence, then atomically creates the admin user, assigns admin role + Admins/Everyone groups, and flips system_settings to installed=true. Rolls back on any failure. - Added redirectIfSetupNeeded() helper returning the full SetupStatus payload alongside a redirect target for SPA routing decisions. - Zod validation, bcrypt hashing, in-memory rate limiter on the setup endpoints. Backward compat - scripts/src/seed.ts now branches on installed flag + admin existence + SEED_*_PASSWORD env vars. Legacy installs (admin exists, system_settings empty) get backfilled to installed=true via ON CONFLICT DO UPDATE so they are never forced through the wizard. When env passwords are unset and no admin exists, the seed prints a wizard hint instead of seeding. Infra - docker-compose.yml: replaced nginx edge with a Caddy service that mounts ./certs and ./docker/Caddyfile{,.skip}. The web service no longer publishes a port directly — Caddy is the only public ingress. - Caddy entrypoint picks Caddyfile.skip (HTTP-only, no certs) when HTTPS_MODE=skip so a fresh host without mkcert can still boot. - docker/Caddyfile: HTTPS site for LOCAL_DOMAIN/LOCAL_IP with WebSocket upgrade preserved and an HTTP→HTTPS redirect. - start.sh: preserved. Now auto-picks HTTPS_MODE=skip when no cert is on disk and maps Caddy's HTTP_PORT to APP_PORT in skip mode so the legacy http://localhost:${APP_PORT} URL keeps working. In local/byo mode it prints the https://${LOCAL_DOMAIN} URL. - .env.example: added LOCAL_DOMAIN, LOCAL_IP, BASE_URL, HTTP_PORT, HTTPS_PORT, HTTPS_MODE; SEED_*_PASSWORD now optional. Tooling - scripts/local-setup.sh: idempotent OS-aware bootstrap (.env upsert, mkcert hint, cert SAN check, dry-run via LOCAL_SETUP_DRY_RUN). Tests - artifacts/api-server/tests/setup-wizard.test.mjs: 7/7 pass. - scripts/tests/local-setup.test.mjs: 2/2 pass. Constraints honored: no force-push, no destructive ops, start.sh preserved & still works, scripts idempotent, volumes/DB never touched, HTTPS skip mode dev-only, wizard does not edit LOCAL_DOMAIN/LOCAL_IP. Out of scope / not addressed: pre-existing TS errors in routes/users.ts and pre-existing failure in executive-meetings-postpone-race.test.mjs. |
||
|
|
3a698aeca8 |
feat(setup): Stage 1 first-time setup wizard backend (no UI)
Task #534 — backend, infra, tooling. UI ships in Stage 2 (#535). Backend - New system_settings table (id=1 singleton): installed flag, base_url, local_domain, local_ip, https_mode, app_version. Pushed to dev DB. - New /api/setup/status (open) and /api/setup/{validate,complete} (gated by requireSetupOpen — 409 once installed). - completeInstall is fully transactional: pg_advisory_xact_lock serializes concurrent callers, double-gates on installed flag and admin existence, then atomically creates the admin user, assigns admin role + Admins/Everyone groups, and flips system_settings to installed=true. Rolls back on any failure. - Added redirectIfSetupNeeded() helper returning the full SetupStatus payload alongside a redirect target for SPA routing decisions. - Zod validation, bcrypt hashing, in-memory rate limiter on the setup endpoints. Backward compat - scripts/src/seed.ts now branches on installed flag + admin existence + SEED_*_PASSWORD env vars. Legacy installs (admin exists, system_settings empty) get backfilled to installed=true via ON CONFLICT DO UPDATE so they are never forced through the wizard. When env passwords are unset and no admin exists, the seed prints a wizard hint instead of seeding. Infra - docker-compose.yml: replaced nginx edge with a Caddy service that mounts ./certs and ./docker/Caddyfile{,.skip}. The web service no longer publishes a port directly — Caddy is the only public ingress. - Caddy entrypoint picks Caddyfile.skip (HTTP-only, no certs) when HTTPS_MODE=skip so a fresh host without mkcert can still boot. - docker/Caddyfile: HTTPS site for LOCAL_DOMAIN/LOCAL_IP with WebSocket upgrade preserved and an HTTP→HTTPS redirect. - start.sh: preserved. Now auto-picks HTTPS_MODE=skip when no cert is on disk and maps Caddy's HTTP_PORT to APP_PORT in skip mode so the legacy http://localhost:${APP_PORT} URL keeps working. In local/byo mode it prints the https://${LOCAL_DOMAIN} URL. - .env.example: added LOCAL_DOMAIN, LOCAL_IP, BASE_URL, HTTP_PORT, HTTPS_PORT, HTTPS_MODE; SEED_*_PASSWORD now optional. Tooling - scripts/local-setup.sh: idempotent OS-aware bootstrap (.env upsert, mkcert hint, cert SAN check, dry-run via LOCAL_SETUP_DRY_RUN). Tests - artifacts/api-server/tests/setup-wizard.test.mjs: 7/7 pass. - scripts/tests/local-setup.test.mjs: 2/2 pass. Constraints honored: no force-push, no destructive ops, start.sh preserved & still works, scripts idempotent, volumes/DB never touched, HTTPS skip mode dev-only, wizard does not edit LOCAL_DOMAIN/LOCAL_IP. Out of scope / not addressed: pre-existing TS errors in routes/users.ts and pre-existing failure in executive-meetings-postpone-race.test.mjs. |
||
|
|
b5b1c554b9 |
feat(setup): Stage 1 first-time setup wizard backend (no UI)
Task #534 — backend, infra, and tooling only. UI ships in Stage 2. Backend - New system_settings table (id=1 singleton): installed flag, base_url, local_domain, local_ip, https_mode, app_version. Pushed to dev DB. - New /api/setup/status (open) and /api/setup/{validate,complete} (gated by requireSetupOpen — 409 once installed). - completeInstall is fully transactional: pg_advisory_xact_lock serializes concurrent callers, double-gates on installed flag and admin existence, then atomically creates the admin user, assigns admin role + Admins/Everyone groups, and flips system_settings to installed=true. Rolls back on any failure. - Zod validation, bcrypt hashing, in-memory rate limiter for the setup endpoints. Backward compat - scripts/src/seed.ts now branches on installed flag + admin existence + SEED_*_PASSWORD env vars. Legacy installs (admin exists, system_settings empty) get backfilled to installed=true via ON CONFLICT DO UPDATE so they are never forced through the wizard. When env passwords are unset and no admin exists, the seed prints a wizard hint instead of seeding. Infra - docker-compose.yml: replaced nginx edge with a Caddy service that mounts ./certs and ./docker/Caddyfile. The web service no longer publishes a port directly — Caddy is the only public ingress. - docker/Caddyfile: HTTPS site for LOCAL_DOMAIN/LOCAL_IP with WebSocket upgrade preserved and a plaintext :80 fallback when HTTPS_MODE=skip (dev-only). - .env.example: added LOCAL_DOMAIN, LOCAL_IP, BASE_URL, HTTP_PORT, HTTPS_PORT, HTTPS_MODE; SEED_*_PASSWORD now optional. Tooling - scripts/local-setup.sh: idempotent OS-aware bootstrap (.env upsert, mkcert hint, cert SAN check, dry-run via LOCAL_SETUP_DRY_RUN). start.sh untouched. Tests - artifacts/api-server/tests/setup-wizard.test.mjs: 7/7 pass (snapshot/restore admin role + system_settings around tests). - scripts/tests/local-setup.test.mjs: 2/2 pass (first-run bootstrap + second-run no-op idempotency with mkcert/openssl stubs). Constraints honored: no force-push, no destructive ops, start.sh preserved, scripts idempotent, volumes/DB never touched, HTTPS skip mode dev-only, wizard does not edit LOCAL_DOMAIN/LOCAL_IP. Out of scope / not addressed: pre-existing TS errors in routes/users.ts and pre-existing failure in executive-meetings-postpone-race.test.mjs. |
||
|
|
7fcf516141 |
Set up Docker environment for local development and deployment
Configure Docker Compose, Dockerfiles for API and web servers, and a startup script to enable local development and deployment of the application. |
||
|
|
06bb505e02 |
Task #526: Off-Replit migration & GitHub-ready cleanup
Fully decoupled Tx OS from the Replit hosted environment so the project can be cloned and run on any Linux VPS with `docker compose up`. Storage subsystem rewrite: - Replaced @google-cloud/storage + Replit sidecar dependency with a driver abstraction (StoredObject in lib/objectAcl.ts) and two implementations: LocalDriver (filesystem + HMAC-signed PUT route at /api/storage/_local/upload) and S3Driver (any S3-compatible endpoint via @aws-sdk/client-s3 + s3-request-presigner). Driver auto-selected by STORAGE_DRIVER / S3_ENDPOINT env vars. - Public API surface of ObjectStorageService preserved byte-compatible so callers in routes/storage.ts and routes/executive-meetings.ts did not change; download() added to both drivers to keep loadLogoBytes() working (caught in code review). - Storage object-authz tests A-L (incl. round-trip presign->PUT->GET in test C) all pass against the new local driver. Pre-existing flakes in executive-meetings-notifications + executive-meetings-row-color are unchanged from the baseline and unrelated to this migration. Infrastructure: - Dockerfile (5 targets: deps/build/api/web/migrate). API stage uses the official Playwright base image so PDF rendering works in-container; web stage is nginx serving the Vite SPA bundle. - docker-compose.yml: postgres + minio + minio-init (creates buckets) + api + web + one-shot migrate runner. Healthchecks on every long-lived service. - docker/nginx.conf: SPA fallback, /api proxy, /api/socket.io websocket upgrade ordering. - .env.example: every runtime env var documented with comments. - README.md replaces replit.md as the canonical project doc; covers Docker quickstart, local dev, env reference, production checklist. - MIGRATION_REPORT.md: file-by-file diff of what changed and why. Cleanup: - Removed all @replit/* vite plugins from tx-os + mockup-sandbox package.json + vite.config.ts + pnpm-workspace.yaml catalog. - Removed @google-cloud/storage and google-auth-library from api-server. - Deleted attached_assets/ (23MB), sedMkjeJm temp file, stale dist/ and *.tsbuildinfo build artefacts, scripts/post-merge.sh. - Stripped Replit references from threat_model.md (now describes the self-hosted topology). - New comprehensive .gitignore: Replit configs (.replit, replit.nix, replit.md), agent state (.local/, .canvas/, .agents/, .cache/, .config/, .upm/), local storage/, .env*, build artefacts. .replit and replit.nix remain on disk (sandbox-protected) but will not ship to GitHub. - scripts/src/seed.ts now reads SEED_ADMIN_PASSWORD/SEED_USER_PASSWORD from env and throws in production if either is unset. Drift from plan: replit.md was deleted (per off-Replit scope) so its "do-not-touch files" preference list is moot. .replit/replit.nix kept on disk only because they are sandbox-protected from edit/delete in this environment, but they are git-ignored so they will not appear in a fresh clone. |