feat: app-to-app integration framework + OnlyOffice handlers
Generic integration system for connecting deployed apps via toggle UI. First handlers: OnlyOffice→FileBrowser (config.yaml patch) and OnlyOffice→Nextcloud (occ CLI). Lifecycle hooks auto-suspend on stop and re-apply on start. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+79
-1
@@ -1197,11 +1197,76 @@ All mutating endpoints trigger an async Cloudflare sync. The `/api/geo/` path ac
|
||||
|
||||
---
|
||||
|
||||
### 14. App-to-App Integrations
|
||||
|
||||
Generic framework for connecting deployed applications to each other. Provider apps declare available integrations in `.felhom.yml`, and users enable/disable them via toggle switches on the provider's app info page.
|
||||
|
||||
#### Architecture (`internal/integrations/`)
|
||||
|
||||
- **`integrations.go`** — Core types: `Handler` interface (`Apply`/`Revoke`), `ApplyContext` (carries domain, decrypted env vars, restart func), `StatusInfo` (UI data)
|
||||
- **`manager.go`** — `Manager` coordinates toggle operations, builds apply contexts, lists integrations for UI. Uses `StackProvider` interface to break circular imports (adapted in main.go)
|
||||
- **`lifecycle.go`** — `OnStackStop` (suspend active integrations), `OnStackStart` (re-apply enabled integrations), `OnStackRemove` (permanent cleanup)
|
||||
- **Handler implementations** — One file per integration pair (e.g. `onlyoffice_filebrowser.go`, `onlyoffice_nextcloud.go`)
|
||||
|
||||
#### Integration State
|
||||
|
||||
Stored in `settings.json` under `integrations` map (key: `"provider:target"`):
|
||||
- `enabled` — User intent (survives stop/restart)
|
||||
- `status` — Current state: `"active"`, `"error"`, `"disabled"`, `"provider_stopped"`, `"target_unavailable"`
|
||||
- `last_error` — Most recent error message
|
||||
- `enabled_at` — RFC3339 timestamp
|
||||
|
||||
#### Lifecycle
|
||||
|
||||
1. **Enable**: User toggles on → validates both apps deployed+running → calls `Handler.Apply()` → persists state as `"active"`
|
||||
2. **Provider/target stops**: `OnStackStop` → calls `Handler.Revoke()` → sets status to `"provider_stopped"` or `"target_unavailable"` (keeps `enabled=true`)
|
||||
3. **Provider/target starts**: `OnStackStart` → finds enabled integrations with non-active status → re-applies if both sides running
|
||||
4. **Provider/target removed**: `OnStackRemove` → revokes and deletes integration state permanently
|
||||
5. **FileBrowser config regen**: `SyncFileBrowserMounts` overwrites `config.yaml` → `OnStackStart("filebrowser")` re-applies active integrations
|
||||
|
||||
#### Built-in Handlers
|
||||
|
||||
**OnlyOffice → FileBrowser** (`onlyoffice_filebrowser.go`):
|
||||
- Apply: Reads JWT_SECRET + SUBDOMAIN from OnlyOffice app.yaml, appends `integrations.office` block to FileBrowser `config.yaml`, restarts FileBrowser
|
||||
- Revoke: Strips `integrations:` block from config.yaml, restarts FileBrowser
|
||||
|
||||
**OnlyOffice → Nextcloud** (`onlyoffice_nextcloud.go`):
|
||||
- Apply: Runs `docker exec -u www-data nextcloud php occ` commands (app:install, app:enable, config:app:set for DocumentServerUrl, jwt_secret)
|
||||
- Revoke: Runs `occ app:disable onlyoffice`
|
||||
|
||||
#### Metadata (`.felhom.yml`)
|
||||
|
||||
```yaml
|
||||
integrations:
|
||||
- target: filebrowser
|
||||
label: "FileBrowser integráció"
|
||||
description: "Dokumentumok szerkesztése a fájlkezelőben"
|
||||
- target: nextcloud
|
||||
label: "Nextcloud integráció"
|
||||
description: "Dokumentumok szerkesztése a Nextcloudban"
|
||||
```
|
||||
|
||||
#### API Endpoints
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | `/api/integrations/{provider}` | List integrations for a provider app |
|
||||
| POST | `/api/integrations/{provider}/{target}` | Enable/disable integration (`{"enable": true/false}`) |
|
||||
|
||||
#### UI
|
||||
|
||||
Toggle switches on the provider's app info page ("Integrációk" section). Each integration shows:
|
||||
- Label and description
|
||||
- Status badge: "Aktív", "Nincs telepítve", "Célalkalmazás leállítva", "Hiba"
|
||||
- Toggle checkbox (disabled when target not deployed/running)
|
||||
|
||||
---
|
||||
|
||||
## Repository Layout
|
||||
|
||||
```
|
||||
controller/
|
||||
├── cmd/controller/main.go # Entry point, wires all 16 modules (setup mode branch + normal startup)
|
||||
├── cmd/controller/main.go # Entry point, wires all 17 modules (setup mode branch + normal startup)
|
||||
├── internal/
|
||||
│ ├── config/config.go # YAML loader, validation, env overrides
|
||||
│ ├── crypto/crypto.go # AES-256-GCM encryption for app.yaml secrets, key management
|
||||
@@ -1237,6 +1302,12 @@ controller/
|
||||
│ │ ├── waf.go # WAF rule CRUD + expression builders
|
||||
│ │ ├── countries.go # ISO 3166-1 country codes + Hungarian names
|
||||
│ │ └── geosync.go # Geo sync orchestrator (diff & apply rules)
|
||||
│ ├── integrations/
|
||||
│ │ ├── integrations.go # Core types: Handler interface, ApplyContext, StatusInfo
|
||||
│ │ ├── manager.go # Manager: Toggle, ListForProvider, StackProvider interface
|
||||
│ │ ├── lifecycle.go # OnStackStop, OnStackStart, OnStackRemove hooks
|
||||
│ │ ├── onlyoffice_filebrowser.go # OnlyOffice → FileBrowser handler (config.yaml patch)
|
||||
│ │ └── onlyoffice_nextcloud.go # OnlyOffice → Nextcloud handler (occ commands)
|
||||
│ ├── assets/syncer.go # Hub asset sync (download, SHA-256 compare, resolve)
|
||||
│ ├── api/
|
||||
│ │ ├── router.go # REST API endpoints (~36 routes)
|
||||
@@ -1484,6 +1555,13 @@ Config endpoints accept session auth OR `Authorization: Bearer <hub_api_key>` (s
|
||||
| POST | `/api/assets/sync` | Trigger on-demand asset sync from Hub (async) |
|
||||
| GET | `/api/assets/status` | Asset sync status (last sync, file count, total bytes) |
|
||||
|
||||
### Integrations
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | `/api/integrations/{provider}` | List integrations for provider app (status, target availability) |
|
||||
| POST | `/api/integrations/{provider}/{target}` | Enable/disable integration (`{"enable": true/false}`) |
|
||||
|
||||
### Debug (debug mode only)
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|
||||
Reference in New Issue
Block a user