feat(signup): add Gleam signup service gated by shared invite code

A small Gleam HTTP server in signup/ exposes POST /signup that validates
a shared INVITE_CODE env var, then PUTs a user document to CouchDB's
_users database using the existing admin credentials. The frontend gets
back 201 + the username and is expected to call /_session itself to
obtain the AuthSession cookie (chosen over server-side cookie forwarding
because vaquant.at and couch.apoena.dev are different sites and the
cross-site cookie path is fragile under modern browser privacy modes).

Wired into docker-compose as a second service, reachable on
signup-couch.apoena.dev with its own Traefik labels and the same
hardcoded UUID network attachment as couchdb. Reuses COUCHDB_USER /
COUCHDB_PASSWORD for the admin call. INVITE_CODE must be set in Coolify
env vars.
This commit is contained in:
Julien Calixte
2026-06-01 22:16:29 +02:00
parent 67787bdf31
commit c3c75787f4
6 changed files with 324 additions and 0 deletions

View File

@@ -52,6 +52,39 @@ They're hardcoded because Coolify does not interpolate `${...}` inside the `labe
A `sed` swap before push, or a Coolify "Custom Build Command" running `sed -i` over the file, is the simplest way to keep this template portable.
## Signup service
A small Gleam HTTP service in `signup/` exposes one endpoint that creates CouchDB users gated by a shared invite code. Lives on `signup-couch.apoena.dev`.
### Env vars (set in Coolify UI)
- `INVITE_CODE` — required. The shared code users must present to sign up.
The signup service reuses `COUCHDB_USER` / `COUCHDB_PASSWORD` for its CouchDB admin call (already required by the couchdb service).
### Endpoint
```
POST https://signup-couch.apoena.dev/signup
Content-Type: application/json
{"username": "julien", "password": "at-least-8-chars", "invite_code": "<INVITE_CODE>"}
```
Responses:
- `201 {"ok": true, "username": "..."}` — user created
- `400 {"error": "invalid_payload" | "invalid_username" | "password_too_short"}`
- `401 {"error": "invalid_invite_code"}`
- `409 {"error": "user_exists"}`
- `502 {"error": "couchdb_unreachable" | "couchdb_error"}`
The frontend should then `POST https://couch.apoena.dev/_session` with the same credentials to obtain the AuthSession cookie.
### CORS
`SIGNUP_ALLOWED_ORIGINS` (already wired in `docker-compose.yml`) holds the comma-separated origin allow-list. Edit it there or override via Coolify env vars.
## Files
- `docker-compose.yml` — services, volume, Traefik labels, init sidecar
- `signup/` — Gleam signup service (Dockerfile, gleam.toml, src/)