# Photobooth Control Service API The control service is a lightweight sidecar responsible for provisioning vsftpd accounts. Laravel talks to it via REST whenever an Event Admin enables, rotates, or disables the Photobooth feature. ## Authentication - **Scheme:** Bearer token. - **Header:** `Authorization: Bearer ${PHOTOBOOTH_CONTROL_TOKEN}`. - **Timeout:** Configurable via `PHOTOBOOTH_CONTROL_TIMEOUT` (default 5 s). - **Token generation:** `openssl rand -hex 32` (or `php -r "echo bin2hex(random_bytes(32)), "`); store in `.env`/Dokploy secrets as `PHOTOBOOTH_CONTROL_TOKEN`. ## Endpoints | Method & Path | Description | |---------------|-------------| | `POST /users` | Create a new FTP account for an event. | | `POST /users/{username}/rotate` | Rotate credentials / extend expiry for an existing user. | | `DELETE /users/{username}` | Remove an FTP account (called when an event disables Photobooth or expires). | | `POST /config` | Optionally push global config changes (port, rate-limit, expiry grace) to the control service. | ### `POST /users` ```json { "username": "pbA12345", "password": "F4P9K2QX", "path": "tenant-slug/123", "rate_limit_per_minute": 20, "expires_at": "2025-06-15T22:59:59Z", "ftp_port": 2121, "allowed_ip_ranges": ["1.2.3.4/32"], "metadata": { "event_id": 123, "tenant_id": 5 } } ``` **Response:** `201 Created` with `{ "ok": true }`. On failure return 4xx/5xx JSON with `error.code` + `message`. Implementation tips: - Ensure the system user or virtual user’s home directory is set to the provided `path` (prefixed with the shared Photobooth root). - Apply the rate limit token-bucket before writing to disk (or integrate with HAProxy). - Store `expires_at` and automatically disable the account when reached (in addition to Laravel’s scheduled cleanup). Reference implementation (current stack): `photobooth-ftp` builds from `docker/photobooth-control`, starts pure-ftpd and a Node-based control API on port 8080. Healthcheck: `GET /health` on 8080 (wired in docker-compose.dokploy.yml). Rate-limit/expiry enforcement inside the FTP tier is still to be implemented. ### `POST /users/{username}/rotate` ```json { "password": "K9M4T6QZ", "rate_limit_per_minute": 20, "expires_at": "2025-06-16T22:59:59Z" } ``` - Rotate the password atomically and respond with `{ "ok": true }`. - If username does not exist return `404` with a descriptive message so Laravel can re-provision. ### `DELETE /users/{username}` No request body. Delete or disable the FTP account, removing access to the assigned directory. ### `POST /config` Optional hook used when SuperAdmins change defaults: ```json { "ftp_port": 2121, "rate_limit_per_minute": 20, "expiry_grace_days": 1 } ``` Use this to reload vsftpd or adjust proxy rules without redeploying the control service. ## Error Contract Return JSON structured as: ```json { "error": { "code": "user_exists", "message": "Username already provisioned", "context": { "username": "pbA12345" } } } ``` Laravel treats any non-2xx as fatal and logs the payload (sans password). Prefer descriptive `code` values: `user_exists`, `user_not_found`, `rate_limit_violation`, `invalid_payload`, etc. ## Observability - Emit structured logs for every create/rotate/delete with event + tenant IDs. - Expose `/health` so Laravel (or uptime monitors) can verify connectivity. - Consider metrics (e.g., Prometheus) for active accounts, rotations, and failures.