Client: - Commit: batch `p4 reopen` so huge changelists (63k+) no longer blow the Windows command-line limit; live "N / total" progress + spinner during commit. - Submit dialog: white arrow, "N of total" + % bar, running file log, Cancel button (aborts pre-commit; changelist stays pending). - Disk scan: streaming reconcile with a live "found N files" counter + Cancel. - Thumbnails: only decode browser-renderable image formats; glyph fallback (+ onError) instead of a broken-image icon for bmp/tga/dds/etc. Landing + waitlist: - waitlist-server: Express + SQLite email collector (dedup) with a secure admin panel (scrypt auth, rate-limited login, signed cookies) and CSV export. - landing form now POSTs signups to the waitlist API. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
52 lines
2.1 KiB
Markdown
52 lines
2.1 KiB
Markdown
# Exbyte Depot — Waitlist server + admin
|
|
|
|
Collects waitlist emails from the landing page (deduplicated) and gives you a
|
|
password-protected admin panel to browse them and export to CSV.
|
|
|
|
## Stack
|
|
- **express** — HTTP + static landing
|
|
- **better-sqlite3** — storage; `UNIQUE COLLATE NOCASE` on email → no duplicates, ever
|
|
- **express-rate-limit** — brute-force protection on the login endpoint
|
|
- **node:crypto** — scrypt password hashing + HMAC-signed session cookies (no extra deps)
|
|
|
|
## Setup
|
|
|
|
```bash
|
|
cd waitlist-server
|
|
npm install
|
|
|
|
cp .env.example .env
|
|
npm run gen-secret # → paste into SESSION_SECRET in .env
|
|
npm run set-password # type a password → paste the ADMIN_PASS_HASH line into .env
|
|
# set ADMIN_USER in .env (default: admin)
|
|
|
|
npm start
|
|
```
|
|
|
|
- Landing: http://localhost:8787/
|
|
- Admin: http://localhost:8787/admin
|
|
|
|
## How it works
|
|
|
|
- The landing form POSTs `{ email }` to **`POST /api/waitlist`**.
|
|
- Invalid emails are rejected; a repeat email is silently deduped (no dup row).
|
|
- A hidden honeypot field (`website`) traps bots.
|
|
- **`/admin`** requires login. Session is a signed, HttpOnly, SameSite=Strict cookie
|
|
(Secure in production), valid 8h. Wrong logins are rate-limited (8 / 15 min / IP).
|
|
- **Export CSV**: `GET /admin/export.csv` streams all signups (UTF-8 + BOM for Excel).
|
|
|
|
## Security notes
|
|
- No plaintext password anywhere — only a scrypt hash in `.env` (git-ignored).
|
|
- Refuses to start without `ADMIN_USER`, `ADMIN_PASS_HASH`, and a strong `SESSION_SECRET`.
|
|
- Security headers set (CSP, X-Frame-Options: DENY, nosniff, no-referrer).
|
|
- Behind a reverse proxy, set `TRUST_PROXY=1` so rate-limiting and stored IPs are correct,
|
|
and terminate TLS at the proxy (cookies become `Secure` when `NODE_ENV=production`).
|
|
|
|
## Deploy (Linux VPS, sketch)
|
|
1. `NODE_ENV=production`, real `.env`, `npm ci --omit=dev`.
|
|
2. Run under systemd (or pm2); put nginx/Caddy in front for TLS + `TRUST_PROXY=1`.
|
|
3. Point your domain at it; the landing is served from `../landing` (or set `LANDING_DIR`).
|
|
|
|
## Data
|
|
SQLite file at `data/waitlist.db` (git-ignored). Back it up to keep your list.
|