# Photobooth FTP Ingestion This guide explains how to operate the Photobooth FTP workflow end‑to‑end: provisioning FTP users for tenants, running the ingest pipeline, and exposing photobooth photos inside the Guest PWA. ## Architecture Overview 1. **vsftpd container** (port `2121`) accepts uploads into a shared volume (default `/var/www/storage/app/photobooth`). Each event receives isolated credentials and a dedicated directory. 2. **Control Service** (REST) provisions FTP accounts. Laravel calls it during enable/rotate/disable actions. 3. **Photobooth settings** (Filament SuperAdmin) define global port, rate limit, expiry grace, and Control Service connection. 4. **Ingest command** copies uploaded files into the event’s storage disk, generates thumbnails, records `photos.ingest_source = photobooth`, and respects package quotas. 5. **Guest PWA filter** consumes `/api/v1/events/{token}/photos?filter=photobooth` to render the “Fotobox” tab. ``` Photobooth -> FTP (vsftpd) -> photobooth disk photobooth:ingest (queue/scheduler) -> Event media storage (public disk/S3) -> packages_usage, thumbnails, security scan ``` ## Environment Variables Add the following to `.env` (already scaffolded in `.env.example`): ```env PHOTOBOOTH_CONTROL_BASE_URL=https://control.internal/api PHOTOBOOTH_CONTROL_TOKEN=your-control-token PHOTOBOOTH_CONTROL_TIMEOUT=5 PHOTOBOOTH_FTP_HOST=ftp.internal PHOTOBOOTH_FTP_PORT=2121 PHOTOBOOTH_USERNAME_PREFIX=pb PHOTOBOOTH_USERNAME_LENGTH=8 PHOTOBOOTH_PASSWORD_LENGTH=8 PHOTOBOOTH_RATE_LIMIT_PER_MINUTE=20 PHOTOBOOTH_EXPIRY_GRACE_DAYS=1 PHOTOBOOTH_IMPORT_DISK=photobooth PHOTOBOOTH_IMPORT_ROOT=/var/www/storage/app/photobooth PHOTOBOOTH_IMPORT_MAX_FILES=50 PHOTOBOOTH_ALLOWED_EXTENSIONS=jpg,jpeg,png,webp ``` ### Filesystem Disk `config/filesystems.php` registers a `photobooth` disk that must point to the shared volume where vsftpd writes files. Mount the same directory inside both the FTP container and the Laravel app container. ## Control Service Contract Laravel expects the Control Service to expose: ``` POST /users { username, password, path, rate_limit_per_minute, expires_at, ftp_port } POST /users/{username}/rotate { password, rate_limit_per_minute, expires_at } DELETE /users/{username} POST /config { ftp_port, rate_limit_per_minute, expiry_grace_days } ``` Authentication is provided via `PHOTOBOOTH_CONTROL_TOKEN` (Bearer token). ## Scheduler & Commands | Command | Purpose | Default schedule | |---------|---------|------------------| | `photobooth:ingest [--event=ID] [--max-files=N]` | Pulls files from the Photobooth disk and imports them into the event storage. | every 5 minutes | | `photobooth:cleanup-expired` | De-provisions FTP accounts after their expiry. | hourly | You can run the ingest job manually for a specific event: ```bash php artisan photobooth:ingest --event=123 --max-files=20 ``` ## Tenant Admin UX Inside the Event Admin PWA, go to **Event → Fotobox-Uploads** to: 1. Enable/disable the Photobooth link. 2. Rotate credentials (max 10-char usernames, 8-char passwords). 3. View rate limit + expiry info and copy the ftp:// link. ## Guest PWA Filter The Guest gallery now exposes a “Fotobox” tab (both preview card and full gallery). API usage: ``` GET /api/v1/events/{token}/photos?filter=photobooth Headers: X-Device-Id (optional) ``` Response items contain `ingest_source`, allowing the frontend to toggle photobooth-only views. ## Operational Checklist 1. **Set env vars** from above and restart the app. 2. **Ensure vsftpd + Control Service** are deployed; verify port 2121 and REST endpoint connectivity. 3. **Mount shared volume** to `/var/www/storage/app/photobooth` (or update `PHOTOBOOTH_IMPORT_ROOT` + `filesystems.disks.photobooth.root`). 4. **Run migrations** (`php artisan migrate`) to create settings/event columns. 5. **Seed default storage target** (e.g., `MediaStorageTarget::create([... 'key' => 'public', ...])`) in non-test environments if not present. 6. **Verify scheduler** (Horizon or cron) is running commands `photobooth:ingest` and `photobooth:cleanup-expired`. 7. **Test end-to-end**: enable Photobooth on a staging event, upload a file via FTP, wait for ingest, and confirm it appears under the Fotobox filter in the PWA.