Remove the browser admin SPA under web/ that let users log in via
a Nostr signer and manage subscription filters. The server is now
a headless API only.
Changes:
- Delete web/ directory entirely (SPA source, config, deps)
- Remove express.static('web/dist') serving from server.ts
- Simplify GET / handler to always return JSON (no fallback)
- Remove build:web from package.json build pipeline
- Remove web build steps from Dockerfile
- Remove web references from build-in-production.sh
Kept: core subscription/unsubscribe/confirm/notify backend and
transactional src/pages/*.html (part of email flow, not the UI).
Switch the subscribe endpoint from POST to an idempotent PUT so clients can
always upsert without a stateful lookup first.
- Add GET /subscription/email?pubkey= lookup so clients can avoid re-POSTing.
- insertSubscription no longer clears confirmed_at unless the email address
changes; a frequency change keeps the existing confirmation.
- registerSubscription only sends a confirmation email when the subscription
is new, unconfirmed, or the email address changed.
- Update integration test and README for the PUT endpoint.
Adds a test that validates the fix for the web UI crash when
VITE_NOTIFIER_RELAY is not set. The test exercises the guarded
pattern (ternary guard before calling normalizeRelayUrl) that
prevents the TypeError crash on undefined input.
Closes mailship-4ic
NOTIFIER_RELAY is string | undefined after the earlier guard fix.
deleteAlert and publishAlert passed it directly to publish() which
expects string[]. Add early-return guards to both functions.
Fixes the 2 new TS2322 errors introduced by the previous commit.
The web UI crashed on load when VITE_NOTIFIER_RELAY was not set
because normalizeRelayUrl(undefined) calls url.match(...) on the
undefined argument, producing:
TypeError: can't access property 'match', A is undefined
Fix: guard with a truthy check before calling normalizeRelayUrl,
and early-return from loadAlerts when NOTIFIER_RELAY is undefined.
Fixes bead mailship-4ic
Multi-stage Dockerfile:
- Stage 1: install deps, build TS, build web UI
- Stage 2: minimal production image with sqlite, only prod deps
- Exposes port 4738, uses /data volume for SQLite
docker-compose.yml for easy local deployment with .env support.
.dockerignore to keep the build context lean.
README updated with Docker usage.
.env.template adds DATA_DIR.
- test/integration.sh: runs full E2E flow against fresh server
- pnpm test and pnpm test:server scripts added
- Root endpoint handles missing web UI gracefully (returns JSON)
- database.ts reads DATA_DIR env var for test isolation
- .gitignore updated for test artifacts