Files
fotospiel-app/docs/ops/photobooth/README.md

148 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Photobooth FTP Ingestion
This guide explains how to operate the Photobooth FTP workflow endtoend: 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 events 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. Sparkbooth uploads reuse this filter via `ingest_source = sparkbooth`.
```
Photobooth -> FTP (vsftpd) -> photobooth disk
photobooth:ingest (queue/scheduler)
-> Event media storage (public disk/S3)
-> packages_usage, thumbnails, security scan
Sparkbooth -> HTTP upload endpoint -> ingest (direct, no staging disk)
```
## 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
# Sparkbooth defaults (optional overrides)
SPARKBOOTH_ALLOWED_EXTENSIONS=jpg,jpeg,png,webp
SPARKBOOTH_MAX_SIZE_KB=8192
SPARKBOOTH_RATE_LIMIT_PER_MINUTE=20
SPARKBOOTH_RESPONSE_FORMAT=json
```
### 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
```
## Sparkbooth HTTP Uploads (Custom Upload)
Use this when Sparkbooth runs in “Custom Upload” mode instead of FTP.
- Endpoint: `POST /api/v1/photobooth/sparkbooth/upload`
- Auth: per-event username/password (set in Event Admin → Fotobox-Uploads; switch mode to “Sparkbooth”).
- Body (multipart/form-data): `media` (file or base64), `username`, `password`, optionally `name`, `email`, `message`.
- Response:
- JSON success: `{"status":true,"error":null,"url":null}`
- JSON failure: `{"status":false,"error":"Invalid credentials"}`
- XML (if `format=xml` or event preference is XML):
- Success: `<rsp status="ok" url="..."/>`
- Failure: `<rsp status="fail"><err msg="Invalid credentials" /></rsp>`
- Limits: allowed extensions reuse photobooth defaults; max size `SPARKBOOTH_MAX_SIZE_KB` (default 8 MB); per-event rate limit `SPARKBOOTH_RATE_LIMIT_PER_MINUTE` (fallback to photobooth rate limit).
- Ingest: writes straight to the events hot storage, applies thumbnail/watermark/security scan, sets `photos.ingest_source = sparkbooth`.
Example cURL (JSON response):
```bash
curl -X POST https://app.example.com/api/v1/photobooth/sparkbooth/upload \
-F "media=@/path/to/photo.jpg" \
-F "username=PB123" \
-F "password=SECRET" \
-F "message=Wedding booth"
```
Example cURL (request XML response):
```bash
curl -X POST https://app.example.com/api/v1/photobooth/sparkbooth/upload \
-F "media=@/path/to/photo.jpg" \
-F "username=PB123" \
-F "password=SECRET" \
-F "format=xml"
```
## 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. Switch mode (FTP or Sparkbooth), view rate limit + expiry info, copy ftp:// or POST URL + creds.
## 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.
8. **Test Sparkbooth**: switch event mode to Sparkbooth, copy Upload URL/user/pass, send a sample POST (or real Sparkbooth upload), verify it appears under the Fotobox filter.