added infomaniak
This commit is contained in:
parent
24bd592da4
commit
111faae088
3 changed files with 229 additions and 0 deletions
121
c-work/2-build/2026-04-infomaniak-connector.md
Normal file
121
c-work/2-build/2026-04-infomaniak-connector.md
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
<!-- status: build -->
|
||||
<!-- started: 2026-04-26 -->
|
||||
<!-- component: gateway, frontend-nyla -->
|
||||
|
||||
# Infomaniak Connector (kDrive + Mail) + UDB-Integration
|
||||
|
||||
## Beschreibung und Kontext
|
||||
|
||||
PowerOn besitzt das Provider-Connector-Pattern fuer externe Datenanbindungen
|
||||
(Microsoft, Google, ClickUp). Infomaniak war bisher nicht angebunden. Ziel: ein
|
||||
neuer `InfomaniakConnector`, der OAuth gegen `login.infomaniak.com` durchfuehrt
|
||||
und Daten aus zwei Services bereitstellt:
|
||||
|
||||
- **kDrive** (Pendant zu OneDrive / Google Drive)
|
||||
- **Mail** (Pendant zu Outlook / Gmail)
|
||||
|
||||
Die Verbindung ist eine reine **Daten-Connection** (`TokenPurpose.DATA_CONNECTION`).
|
||||
Infomaniak ist explizit **kein** Login-Provider fuer PowerOn -- entsprechend gibt
|
||||
es nur `_FLOW_CONNECT`, kein `_FLOW_LOGIN`.
|
||||
|
||||
Zusatzlich wurden zwei kleine Inkonsistenzen in der ClickUp-UDB-Anzeige
|
||||
behoben (`_SERVICE_ICONS` + `_SERVICE_TO_SOURCE_TYPE`).
|
||||
|
||||
## Fokus und kritische Details
|
||||
|
||||
- **Refresh-Token-Persistenz**: Infomaniak rotiert Refresh-Tokens nicht in jedem
|
||||
Token-Response; falls `refresh_token` im Callback fehlt, wird aus dem letzten
|
||||
gespeicherten Token rekonstruiert (analog Google).
|
||||
- **API-Pfad-Konvention**: Drei-Ebenen-Hierarchie kDrive (`/{driveId}/{fileId}`)
|
||||
und vier-Ebenen Mail (`/{mailboxId}/{folderId}/{uid}`); `ServiceAdapter.browse`
|
||||
unterscheidet die Tiefe via Segment-Count.
|
||||
- **Antwort-Wrapping**: Erfolgreiche Responses sind als
|
||||
`{result: 'success', data: ...}` gewrappt -- `_unwrapData()` normalisiert.
|
||||
- **Authority-Filter** in `tokenRefreshService` muss `INFOMANIAK` enthalten,
|
||||
sonst werden Token nie proaktiv refreshed.
|
||||
|
||||
## Ziel und Nicht-Ziele
|
||||
|
||||
- Ziel: Infomaniak-Daten (kDrive + Mail) wie MSFT/Google im UDB browsbar.
|
||||
- Ziel: ConnectionsPage hat Button "Infomaniak" (analog ClickUp).
|
||||
- Ziel: Refresh-Token-Lifecycle vollautomatisch (kein User-Reconnect noetig).
|
||||
- Ziel: ClickUp-UDB-Inkonsistenz beheben (Service-Icon + Source-Type-Mapping).
|
||||
- NICHT: Infomaniak als PowerOn-Login (`User`-Erstellung via Infomaniak-OAuth).
|
||||
- NICHT: Upload-Implementierung in kDrive/Mail (gibt nur Browse + Download).
|
||||
|
||||
## Betroffene Module
|
||||
|
||||
- Gateway:
|
||||
- `modules/datamodels/datamodelUam.py` (Enum)
|
||||
- `modules/auth/oauthProviderConfig.py` (Scopes)
|
||||
- `modules/auth/tokenManager.py` (Refresh)
|
||||
- `modules/auth/tokenRefreshService.py` (Background-Refresh)
|
||||
- `modules/connectors/providerInfomaniak/connectorInfomaniak.py` (neu)
|
||||
- `modules/connectors/connectorResolver.py` (Registry)
|
||||
- `modules/routes/routeSecurityInfomaniak.py` (neu)
|
||||
- `modules/routes/routeDataConnections.py` (Dispatch)
|
||||
- `app.py` (Router-Mount)
|
||||
- Frontend:
|
||||
- `src/api/connectionApi.ts` (Authority-Typ)
|
||||
- `src/hooks/useConnections.ts` (Popup-Handler)
|
||||
- `src/pages/basedata/ConnectionsPage.tsx` (Button)
|
||||
- `src/components/UnifiedDataBar/SourcesTab.tsx` (Icons + Colors + Mapping, ClickUp-Fix)
|
||||
- DB-Migration: nein (nur Enum-Wert, alle Tabellen abwaertskompatibel).
|
||||
- Andere: keine.
|
||||
|
||||
## Entscheidungen
|
||||
|
||||
| Datum | Entscheidung | Begruendung |
|
||||
|------------|--------------|-------------|
|
||||
| 2026-04-26 | Self-contained Connector (httpx im Modul, kein eigener Service) | Folgt Google/MSFT-Pattern, nicht ClickUp-Pattern (das delegiert an `ClickupService`) |
|
||||
| 2026-04-26 | Nur DATA_CONNECTION, kein Login | User explicitly: "wir benoetigen nur den userconnection auth" |
|
||||
| 2026-04-26 | `kdrive` + `mail` als Service-Namen | Kurz, konsistent mit Infomaniak-Branding (kDrive heisst offiziell so) |
|
||||
|
||||
## Umsetzungs-Checkliste
|
||||
|
||||
- [x] AuthAuthority-Enum erweitert
|
||||
- [x] Scopes definiert (`user_info kdrive mail`)
|
||||
- [x] InfomaniakConnector + KdriveAdapter + MailAdapter
|
||||
- [x] ConnectorResolver-Registry
|
||||
- [x] OAuth-Route `/api/infomaniak/auth/connect[/callback]`
|
||||
- [x] Token-Refresh (`refreshInfomaniakToken` + Background-Service)
|
||||
- [x] DataConnections-Dispatch (`authority_map`, Labels, `connect_service`)
|
||||
- [x] App-Router-Mount
|
||||
- [x] Frontend-Typen
|
||||
- [x] Hook `createInfomaniakConnectionAndAuth` + Event-Listener
|
||||
- [x] ConnectionsPage-Button
|
||||
- [x] UDB-Integration (Authority-Icon, Service-Icons, Source-Colors, Mapping)
|
||||
- [x] ClickUp-UDB-Fix
|
||||
- [x] Setup-Guide `wiki/d-guides/infomaniak-oauth-setup.md`
|
||||
- [ ] Manueller End-to-End-Test (ClientID/Secret im Manager registrieren, Verbinden, kDrive browsen, Mail browsen, Datei downloaden)
|
||||
- [ ] `wiki/b-reference/connectors.md` (falls vorhanden) ergaenzen
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
| # | Kriterium (Given-When-Then) | Prio |
|
||||
|---|-----------------------------|------|
|
||||
| 1 | Given Infomaniak-Credentials in `APP_CONFIG`, When Nutzer "Infomaniak" auf ConnectionsPage klickt, Then oeffnet sich der OAuth-Popup auf `login.infomaniak.com/authorize` | must |
|
||||
| 2 | Given erfolgreicher OAuth-Callback, Then ist die UserConnection `ACTIVE`, Token gespeichert (`tokenAccess`+`tokenRefresh`), `externalId/Email` gesetzt | must |
|
||||
| 3 | Given aktive Connection, When im UDB die Authority expandiert wird, Then werden Services `kdrive` und `mail` mit Icons angezeigt | must |
|
||||
| 4 | Given Token vor Ablauf < 5 min, When Background-Refresh laeuft, Then wird `_refresh_infomaniak_token` aufgerufen und Token erneuert | must |
|
||||
| 5 | Given ClickUp-Service-Node im UDB, Then ist das Service-Icon das Klemmbrett (Fix) | should |
|
||||
|
||||
## Testplan
|
||||
|
||||
| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
|
||||
|----|----|-----|---------------|-----------|--------|
|
||||
| T1 | 1-2 | manual | nein | UI: ConnectionsPage | pending |
|
||||
| T2 | 3 | manual | nein | UI: UDB SourcesTab | pending |
|
||||
| T3 | 4 | unit | spaeter | gateway/tests/auth/test_tokenManager.py | pending |
|
||||
| T4 | 5 | manual | nein | UI: UDB SourcesTab | pending |
|
||||
|
||||
## Links
|
||||
|
||||
- PR: tbd
|
||||
- Setup-Guide: `wiki/d-guides/infomaniak-oauth-setup.md`
|
||||
|
||||
## Abschluss
|
||||
|
||||
- [ ] `b-reference/` aktualisiert
|
||||
- [ ] `TOPICS.md` aktualisiert
|
||||
- [ ] Dokument nach `z-archive/` verschoben
|
||||
|
|
@ -14,6 +14,9 @@ Skip: reine Refactors, Formatting, Lint, Dep-Bumps, Test-only, Wiki-Tippfehler.
|
|||
|
||||
## 2026-04-26
|
||||
|
||||
- 2026-04-26 | fix | gateway | Feature-Data-Subagent (`queryFeatureInstance`) Schema-Prompt war zu duenn: Agent erhielt nur Tabellennamen + flache Spalten-Liste, ohne Typen / Beschreibungen / FK-Beziehungen. Folge: bei Trustee-Saldo-Queries summierte er `TrusteeDataJournalLine.debitAmount/creditAmount` ohne Datumsfilter (JournalLine hat gar kein `bookingDate`!) statt die Periode-bezogenen `TrusteeDataAccountBalance.closingBalance` zu nutzen; ISO-Date-Strings wurden gegen Float-Unix-Timestamps gefiltert (`bookingDate <= '2025-12-31'`). Fix in `featureDataAgent._buildSchemaContext`: pro Tabelle wird jetzt der zugehoerige Pydantic-Klasse via `MODEL_REGISTRY` resolved und pro selektiertem Feld eine angereicherte Zeile gerendert -- Python-Typ aus `field.annotation`, deutsches Label aus `json_schema_extra.label`, Description aus `Field(description=...)`, FK-Target aus `fk_target`. Dazu drei neue Regeln im System-Prompt: (a) Float-Felder mit "unix timestamp" in der Description sind Sekunden-seit-Epoch (Beispiel `'2025-12-31' -> 1735603200.0`), (b) Tools koennen nicht JOINen -- FK-Tabellen separat abfragen, (c) Periode-aggregierte Tabellen (Opening/Closing-Balances) bevorzugt vor SUM ueber Rohdaten. Fallback auf flache Feldliste wenn die Tabelle nicht im Pydantic-Registry ist. Unit-Tests in `tests/unit/services/test_featureDataAgent_schema.py`
|
||||
- 2026-04-26 | feat | gateway+frontend-nyla | Infomaniak-Connector (kDrive + Mail) als neuer ProviderConnector analog Google/MSFT/ClickUp. Backend: `AuthAuthority.INFOMANIAK`, `providerInfomaniak/connectorInfomaniak.py` mit `KdriveAdapter`+`MailAdapter` (httpx, OAuth-Bearer, Path-Konvention `/{driveId}/{fileId}` bzw. `/{mailboxId}/{folderId}/{uid}`), `routeSecurityInfomaniak.py` mit `_FLOW_CONNECT`-only (kein Login -- pure DATA_CONNECTION), Token-Refresh via `tokenManager.refreshInfomaniakToken` + `tokenRefreshService._refresh_infomaniak_token`, `connectorResolver`-Registry-Eintrag, Authority-Map/Labels/Dispatch in `routeDataConnections`, Router-Mount in `app.py`. Refresh-Token-Persistierung holt bei fehlendem `refresh_token`-Response aus dem letzten gespeicherten Token (analog Google). Frontend: `connectionApi.ts` Authority-Typ erweitert, `useConnections.createInfomaniakConnectionAndAuth` + `infomaniak_connection_success/error`-Event-Listener, `ConnectionsPage` mit Infomaniak-Button (FaCloud), `SourcesTab` Icons/Colors/Service-Mapping fuer `infomaniak`/`kdrive`/`mail` -- inkl. Fix der bisher fehlenden ClickUp-Eintraege in `_SERVICE_ICONS` + `_SERVICE_TO_SOURCE_TYPE`. Setup-Guide unter `wiki/d-guides/infomaniak-oauth-setup.md` (c-work: c-work/2-build/2026-04-infomaniak-connector.md)
|
||||
- 2026-04-26 | fix | gateway | Feature-Data-Subagent (`queryFeatureInstance`) hat seine Loop hardcoded auf 8 Runden begrenzt, unabhaengig vom Workspace `maxAgentRounds`. Im int-System brachen Trustee-Saldo-Queries deshalb mit `Maximum rounds reached. Progress after 8 rounds` ab, obwohl der Parent-Agent z.B. mit 25 Runden konfiguriert war. Fix: `agentLoop._executeToolCalls` propagiert jetzt `parentMaxRounds` + `parentMaxCostCHF` ueber den Tool-Context; `_featureSubAgentTools._queryFeatureInstance` liest sie aus und reicht sie an `runFeatureDataAgent(maxRounds=, maxCostCHF=)` weiter. Default fuer Direktaufrufer/Tests bleibt 8. Cost-Cap skaliert linear (`_MAX_COST_CHF_PER_ROUND = 0.02 * maxRounds`), damit nicht der 0.15-CHF-Guard die Loop vor Erreichen der konfigurierten Runden abschiesst (25 Runden -> 0.50 CHF). Subagent-Start loggt jetzt effektive `maxRounds`/`maxCostCHF` zur Diagnose
|
||||
- 2026-04-26 | feat | gateway+frontend-nyla | Database-Health Orphan-Cleanup: neue Checkbox `Ohne FK-Referenzen zu UserInDB.id` (default ON). Deleted-User-Reste in Audit/Billing/Membership-Tabellen sammeln sich natuerlich an wenn ein User geloescht wird und gehoeren in den separaten User-Purge-Workflow, nicht in die generische FK-Bereinigung. Backend: `_isUserIdFk(targetTable, targetColumn)`-Helper (case-insensitive auf Tabellenname); `_cleanAllOrphans(force, excludeUserFks)` ueberspringt entsprechende Relationen; `/orphans?excludeUserFks=true` filtert Scan-Resultate; `OrphanCleanAllRequest.excludeUserFks` filtert clean-all. Frontend: Checkbox neben `Nur Probleme`, default checked, mit Tooltip; URL-Param + clean-all Body-Field synchron; `Alle bereinigen`-Counter zeigt jetzt nur Non-User-FK-Orphans
|
||||
- 2026-04-26 | fix | gateway | aicorePluginOpenai: `max_tokens` durch `max_completion_tokens` ersetzt in `callAiBasic` und `callAiBasicStream`. Hintergrund: OpenAI lehnt `max_tokens` fuer gpt-5.x / o-series Modelle mit HTTP 400 `unsupported_parameter` ab (`Use 'max_completion_tokens' instead`). Im Log `log_app_20260426.log` (L741-764) sichtbar: `gpt-5.4-nano` failover scheiterte sofort, ModelSelector wechselte auf `claude-opus-4-6`. Per OpenAI API-Reference akzeptieren ALLE aktuellen Chat-Completions-Modelle (legacy gpt-4o/gpt-4.1, gpt-5.x, o1/o3/o4) `max_completion_tokens`, daher universeller Wechsel statt Modell-spezifischer Verzweigung
|
||||
- 2026-04-26 | feat | gateway | PDF-Renderer Emoji-Support: Noto Emoji (monochrome, OFL) als Fallback-Font registriert. Bisher rendern WinAnsi-Core-Fonts (Helvetica/Courier) Emoji-Codepoints (U+2600+, U+1F300+) als fehlende Glyphen-Quadrate. Neu unter `gateway/assets/fonts/NotoEmoji-Regular.ttf` (~419 KB, 887 Codepoints) + `_pdfFontFallback.py` Helper: registriert die TTF einmalig bei reportlab, scannt deren cmap, und `wrapEmojiSpansInXml` umschliesst zusammenhaengende Emoji-Runs (codepoint >= U+2000 ∧ in cmap) mit `<font name="NotoEmoji">…</font>` — nestet sauber in `<b>`/`<i>`/`<font name="Courier">`. `rendererPdf._markdownInlineToReportlabXml` wendet das am Ende an, also greift es ueberall wo Paragraph-Markup gebaut wird (Headings, Paragraphs, Bullet-Lists, Table-Cells, extracted_text). `Preformatted` (Code-Blocks) ist Single-Font-only und bleibt unveraendert — Emojis in Code-Bloecken sind selten, Box-Drawing wird wie bisher zu ASCII normalisiert. Smoke-Test in `test_renderer_pdf_smoke.py`
|
||||
|
|
|
|||
105
d-guides/infomaniak-oauth-setup.md
Normal file
105
d-guides/infomaniak-oauth-setup.md
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
# Infomaniak OAuth 2.0 Setup Guide
|
||||
|
||||
## Overview
|
||||
|
||||
This guide explains how to register an Infomaniak OAuth application so that PowerOn
|
||||
users can connect their Infomaniak account as a data source. The connection
|
||||
exposes two services in the Unified Data Bar:
|
||||
|
||||
- **kDrive** -- browse and download files from any drive accessible to the user.
|
||||
- **Mail** -- browse mailboxes, folders, and download messages as `.eml`.
|
||||
|
||||
Infomaniak is a **data-only** authority in PowerOn. It is **not** a login
|
||||
provider; users still authenticate against PowerOn via Local / Google / Microsoft.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- An Infomaniak account with admin rights to create API credentials.
|
||||
- Access to the Infomaniak Manager (https://manager.infomaniak.com).
|
||||
- The PowerOn gateway publicly reachable (or `http://localhost:8000` for development).
|
||||
|
||||
## Step 1: Create an Infomaniak API Application
|
||||
|
||||
1. Login to https://manager.infomaniak.com.
|
||||
2. Navigate to your account icon (top right) > **API**.
|
||||
3. Click **Create a new application**.
|
||||
4. Fill in:
|
||||
- **Name:** PowerOn (or your project name)
|
||||
- **Description:** Data connector for kDrive + Mail
|
||||
- **Redirect URI:** must exactly match the gateway endpoint:
|
||||
- Development: `http://localhost:8000/api/infomaniak/auth/connect/callback`
|
||||
- Production: `https://<gateway-host>/api/infomaniak/auth/connect/callback`
|
||||
5. Select scopes:
|
||||
- `user_info` -- required to read `/1/profile` for `externalId` / `email`.
|
||||
- `kdrive` -- required for the kDrive ServiceAdapter.
|
||||
- `mail` -- required for the Mail ServiceAdapter.
|
||||
6. Save and copy the generated **Client ID** and **Client Secret**.
|
||||
|
||||
## Step 2: Configure PowerOn Gateway
|
||||
|
||||
Add the following keys to your gateway environment file (e.g. `gateway/env_dev.env`):
|
||||
|
||||
```env
|
||||
# Infomaniak OAuth -- Data App (kDrive + Mail)
|
||||
Service_INFOMANIAK_DATA_CLIENT_ID = your-client-id
|
||||
Service_INFOMANIAK_DATA_CLIENT_SECRET = your-client-secret
|
||||
Service_INFOMANIAK_OAUTH_REDIRECT_URI = http://localhost:8000/api/infomaniak/auth/connect/callback
|
||||
```
|
||||
|
||||
For production, replace the redirect URI with your HTTPS gateway host. The URI
|
||||
must be identical (byte-for-byte) to the one registered in step 1.
|
||||
|
||||
Restart the gateway after editing the env file so `APP_CONFIG` is reloaded.
|
||||
|
||||
## Step 3: Verify the Flow
|
||||
|
||||
1. Login to PowerOn and open **Basisdaten > Verbindungen**.
|
||||
2. Click **Infomaniak** in the header actions.
|
||||
3. A popup opens against `https://login.infomaniak.com/authorize`.
|
||||
4. Sign in with your Infomaniak account, grant the requested scopes.
|
||||
5. The popup closes automatically and the new connection appears with status
|
||||
`connected`.
|
||||
6. Open the **Unified Data Bar > Sources** tab; you should see the Infomaniak
|
||||
authority node with `kdrive` and `mail` services.
|
||||
|
||||
## Token Lifecycle
|
||||
|
||||
- Access tokens are short-lived (refresh handled automatically by
|
||||
`tokenRefreshService._refresh_infomaniak_token`).
|
||||
- Refresh tokens are stored alongside the connection (`Token.tokenRefresh`).
|
||||
- If a refresh returns `invalid_grant`, the user must reconnect (popup again
|
||||
via the Infomaniak button on `ConnectionsPage`).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### `invalid_redirect_uri`
|
||||
The URI registered on Infomaniak Manager does not match `Service_INFOMANIAK_OAUTH_REDIRECT_URI`.
|
||||
They must be byte-identical (including protocol, host, port, and path).
|
||||
|
||||
### `invalid_client`
|
||||
Client ID or secret in the env file does not match what Infomaniak issued.
|
||||
Re-copy the values from Manager > API.
|
||||
|
||||
### Profile lookup returns 401
|
||||
The `user_info` scope was not granted. Re-create the application with the
|
||||
correct scope set, or reconnect the user.
|
||||
|
||||
### Empty kDrive listing
|
||||
The user has no kDrives associated. Verify on https://kdrive.infomaniak.com
|
||||
that at least one drive is accessible.
|
||||
|
||||
## Security Notes
|
||||
|
||||
- Never commit the client secret. Use the encrypted env mechanism described in
|
||||
`wiki/d-guides/encrypt-env-secrets.md`.
|
||||
- Rotate the client secret if it leaks; update `Service_INFOMANIAK_DATA_CLIENT_SECRET`
|
||||
and restart the gateway. Existing tokens remain valid only until they expire.
|
||||
- The connector is scoped to `DATA_CONNECTION` only. Even with valid Infomaniak
|
||||
credentials, no PowerOn login session is created.
|
||||
|
||||
## Reference
|
||||
|
||||
- Code: `gateway/modules/connectors/providerInfomaniak/connectorInfomaniak.py`
|
||||
- OAuth route: `gateway/modules/routes/routeSecurityInfomaniak.py`
|
||||
- Token refresh: `gateway/modules/auth/tokenManager.py::refreshInfomaniakToken`
|
||||
- Frontend hook: `frontend_nyla/src/hooks/useConnections.ts::createInfomaniakConnectionAndAuth`
|
||||
Loading…
Reference in a new issue