feat(superadmin): migrate internal docs from docusaurus to guava kb
This commit is contained in:
147
docs/superadmin-kb/de/04-photobooth/01-overview.md
Normal file
147
docs/superadmin-kb/de/04-photobooth/01-overview.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# 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. 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/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 event’s 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/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/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.
|
||||
100
docs/superadmin-kb/de/04-photobooth/02-control-service.md
Normal file
100
docs/superadmin-kb/de/04-photobooth/02-control-service.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# 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.
|
||||
53
docs/superadmin-kb/de/04-photobooth/03-ops-playbook.md
Normal file
53
docs/superadmin-kb/de/04-photobooth/03-ops-playbook.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Photobooth Operations Playbook
|
||||
|
||||
Use this checklist when bringing Photobooth FTP online for a tenant or debugging ingest issues.
|
||||
|
||||
## 1. Provisioning Flow
|
||||
|
||||
1. **SuperAdmin config** – set defaults in Filament → Platform Management → Photobooth Settings.
|
||||
2. **Tenant enablement** – Event Admin opens the event → Fotobox-Uploads → “Photobooth aktivieren”.
|
||||
3. Laravel generates credentials and calls the control service (`POST /users`).
|
||||
4. vsftpd accepts uploads at `ftp://username:password@HOST:PORT/`.
|
||||
5. `photobooth:ingest` copies files into the hot storage disk and applies moderation/security pipelines.
|
||||
|
||||
## 2. Troubleshooting
|
||||
|
||||
| Symptom | Action |
|
||||
|---------|--------|
|
||||
| Tenant’s Photobooth page shows “Deaktiviert” immediately | Check `storage/logs/laravel.log` for control-service errors; re-run `photobooth:ingest --event=ID -vv`. |
|
||||
| Files remain under `/storage/app/photobooth/<tenant>/<event>` | Ensure scheduler (Horizon/cron) runs `photobooth:ingest`; run manual command to force ingestion. |
|
||||
| Photos missing from guest “Fotobox” tab | Confirm `photos.ingest_source = photobooth` and that `/api/v1/events/{token}/photos?filter=photobooth` returns data. |
|
||||
| Rate-limit complaints | Inspect control service logs; adjust `PHOTOBOOTH_RATE_LIMIT_PER_MINUTE` and re-save settings (fires `/config`). |
|
||||
| Credentials leaked/compromised | Click “Zugang neu generieren” in Event Admin; optional `php artisan photobooth:cleanup-expired --event=ID` to force deletion before expiry. |
|
||||
|
||||
## 3. Command Reference
|
||||
|
||||
```bash
|
||||
# Manually ingest pending files for a single event
|
||||
php artisan photobooth:ingest --event=123 --max-files=100
|
||||
|
||||
# Check ingest for all active events (dry run)
|
||||
php artisan photobooth:ingest --max-files=10
|
||||
|
||||
# Remove expired accounts (safe to run ad hoc)
|
||||
php artisan photobooth:cleanup-expired
|
||||
```
|
||||
|
||||
## 4. Pre-flight Checklist for New Deployments
|
||||
|
||||
1. `php artisan migrate`
|
||||
2. Configure `.env` Photobooth variables.
|
||||
3. Mount shared Photobooth volume in all containers (FTP + Laravel).
|
||||
4. Verify `MediaStorageTarget` records exist (hot target pointing at the hot disk).
|
||||
5. Seed baseline emotions (Photobooth ingest assigns `emotion_id` from existing rows).
|
||||
6. Confirm scheduler runs (Horizon supervisor or system cron).
|
||||
|
||||
## 5. Incident Response
|
||||
|
||||
1. **Identify scope** – which events/tenants are affected? Check ingestion logs for specific usernames/path.
|
||||
2. **Quarantine** – disable the Photobooth toggle for impacted events via Admin UI.
|
||||
3. **Remediate** – fix FTP/control issues, rotate credentials, run `photobooth:ingest`.
|
||||
4. **Audit** – review `photobooth_metadata` on events and `photos.ingest_source`.
|
||||
5. **Communicate** – notify tenant admins via in-app message or email template referencing incident ID.
|
||||
|
||||
Keep this playbook updated whenever infra/process changes. PRs to `/docs/ops/photobooth` welcome.
|
||||
Reference in New Issue
Block a user