docs(architecture): update for v0.7.3 OAuth2 and single-instance wiring
- Bump header date/version to 2026-04-13 / v0.7.3 - Correct Tauri command count (25 → 34) and add the missing commands - Add `auth_commands.rs` section (5 commands) and expand `license_commands.rs` with the 4 activation commands that already existed - New "Plugins Tauri" section documenting init order constraints (single-instance must be first, deep-link before setup) - New "OAuth2 et deep-link" section explaining the end-to-end flow, why single-instance is required, and why `on_open_url` is used instead of `app.listen()` - Note the temporary auto-update gate opening in entitlements - Update CI/CD: GitHub Actions → Forgejo Actions, add check.yml Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f5d74b4664
commit
43c5be0c84
1 changed files with 69 additions and 7 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
# Architecture technique — Simpl'Résultat
|
# Architecture technique — Simpl'Résultat
|
||||||
|
|
||||||
> Document mis à jour le 2026-04-11 — Version 0.7.0
|
> Document mis à jour le 2026-04-13 — Version 0.7.3
|
||||||
|
|
||||||
## Stack technique
|
## Stack technique
|
||||||
|
|
||||||
|
|
@ -153,7 +153,7 @@ Chaque hook encapsule la logique d'état via `useReducer` :
|
||||||
| `useLicense` | État de la licence et entitlements |
|
| `useLicense` | État de la licence et entitlements |
|
||||||
| `useAuth` | Authentification Compte Maximus (OAuth2 PKCE, subscription status) |
|
| `useAuth` | Authentification Compte Maximus (OAuth2 PKCE, subscription status) |
|
||||||
|
|
||||||
## Commandes Tauri (25)
|
## Commandes Tauri (34)
|
||||||
|
|
||||||
### `fs_commands.rs` — Système de fichiers (6)
|
### `fs_commands.rs` — Système de fichiers (6)
|
||||||
|
|
||||||
|
|
@ -182,7 +182,7 @@ Chaque hook encapsule la logique d'état via `useReducer` :
|
||||||
- `verify_pin` — Vérification du PIN
|
- `verify_pin` — Vérification du PIN
|
||||||
- `repair_migrations` — Réparation des checksums de migration (rusqlite)
|
- `repair_migrations` — Réparation des checksums de migration (rusqlite)
|
||||||
|
|
||||||
### `license_commands.rs` — Licence (6)
|
### `license_commands.rs` — Licence et activation machine (10)
|
||||||
|
|
||||||
- `validate_license_key` — Validation offline d'une clé de licence (JWT Ed25519)
|
- `validate_license_key` — Validation offline d'une clé de licence (JWT Ed25519)
|
||||||
- `store_license` — Stockage de la clé dans le répertoire app data
|
- `store_license` — Stockage de la clé dans le répertoire app data
|
||||||
|
|
@ -190,10 +190,60 @@ Chaque hook encapsule la logique d'état via `useReducer` :
|
||||||
- `read_license` — Lecture de la licence stockée
|
- `read_license` — Lecture de la licence stockée
|
||||||
- `get_edition` — Détection de l'édition active (free/base/premium)
|
- `get_edition` — Détection de l'édition active (free/base/premium)
|
||||||
- `get_machine_id` — Génération d'un identifiant machine unique
|
- `get_machine_id` — Génération d'un identifiant machine unique
|
||||||
|
- `activate_machine` — Activation en ligne (appel API serveur de licences, issue #49)
|
||||||
|
- `deactivate_machine` — Désactivation d'une machine enregistrée
|
||||||
|
- `list_activated_machines` — Liste des machines activées pour la licence
|
||||||
|
- `get_activation_status` — État d'activation de la machine courante
|
||||||
|
|
||||||
|
### `auth_commands.rs` — Compte Maximus / OAuth2 PKCE (5)
|
||||||
|
|
||||||
|
- `start_oauth` — Génère un code verifier PKCE et retourne l'URL d'authentification Logto
|
||||||
|
- `refresh_auth_token` — Rafraîchit l'access token via le refresh token
|
||||||
|
- `get_account_info` — Lecture du cache `account.json` (sans appel réseau)
|
||||||
|
- `check_subscription_status` — Vérifie l'abonnement (max 1×/jour, fallback cache gracieux)
|
||||||
|
- `logout` — Efface tokens.json + account.json
|
||||||
|
|
||||||
|
Note : `handle_auth_callback` n'est PAS exposée comme commande — elle est appelée depuis le handler deep-link `on_open_url` dans `lib.rs`. Voir section "OAuth2 et deep-link" plus bas.
|
||||||
|
|
||||||
### `entitlements.rs` — Entitlements (1)
|
### `entitlements.rs` — Entitlements (1)
|
||||||
|
|
||||||
- `check_entitlement` — Vérifie si une feature est autorisée selon l'édition
|
- `check_entitlement` — Vérifie si une feature est autorisée selon l'édition
|
||||||
|
- Source de vérité : `FEATURE_TIERS` dans `entitlements.rs`. Modifier cette constante pour changer les gates, jamais ailleurs dans le code
|
||||||
|
- Temporaire : `auto-update` est ouvert à `free` en attendant le serveur de licences (issue #49). À re-gater à `[base, premium]` quand l'activation payante sera live
|
||||||
|
|
||||||
|
## Plugins Tauri
|
||||||
|
|
||||||
|
Ordre d'initialisation dans `lib.rs` (certains plugins ont des contraintes d'ordre) :
|
||||||
|
|
||||||
|
| Plugin | Rôle | Contrainte |
|
||||||
|
|--------|------|-----------|
|
||||||
|
| `tauri-plugin-single-instance` | Empêche les doubles lancements et forwarde les URLs deep-link au processus existant | **Doit être le premier plugin** ; feature `deep-link` requise pour le forwarding d'URL |
|
||||||
|
| `tauri-plugin-opener` | Ouverture d'URLs externes et de fichiers | — |
|
||||||
|
| `tauri-plugin-dialog` | Dialogues de sélection de fichier/dossier | — |
|
||||||
|
| `tauri-plugin-process` | Relaunch après mise à jour | — |
|
||||||
|
| `tauri-plugin-deep-link` | Gère le scheme custom `simpl-resultat://` | Doit être initialisé avant `setup()` pour que `on_open_url` soit disponible |
|
||||||
|
| `tauri-plugin-updater` | Mise à jour auto (gated par entitlement `auto-update`) | Initialisé dans `setup()` derrière `#[cfg(desktop)]` |
|
||||||
|
| `tauri-plugin-sql` | SQLite + migrations | Doit être initialisé avec les migrations pour que le schéma soit prêt |
|
||||||
|
|
||||||
|
## OAuth2 et deep-link (Compte Maximus)
|
||||||
|
|
||||||
|
Flow complet (v0.7.3+) :
|
||||||
|
|
||||||
|
1. Frontend appelle `start_oauth` → génère un code verifier PKCE (64 chars), le stocke dans `OAuthState` (Mutex en mémoire du processus), retourne l'URL Logto
|
||||||
|
2. Frontend ouvre l'URL via `tauri-plugin-opener` → le navigateur système affiche la page Logto
|
||||||
|
3. L'utilisateur s'authentifie (ou Logto auto-consent si session existante) → redirection 303 vers `simpl-resultat://auth/callback?code=...`
|
||||||
|
4. L'OS route le custom scheme vers une nouvelle instance de l'app → `tauri-plugin-single-instance` (feature `deep-link`) détecte l'instance existante, **ne démarre PAS un nouveau processus**, et forwarde l'URL à l'instance vivante
|
||||||
|
5. Le callback `app.deep_link().on_open_url(...)` enregistré via `DeepLinkExt` reçoit les URLs. Pour chaque URL :
|
||||||
|
- Si un param `code` est présent → appelle `handle_auth_callback` (token exchange vers `/oidc/token`, fetch `/oidc/me`, écriture `tokens.json` + `account.json` avec perms 0600, émission de l'event `auth-callback-success`)
|
||||||
|
- Si un param `error` est présent → émission de l'event `auth-callback-error` avec `error: error_description`
|
||||||
|
6. Le hook `useAuth` (frontend) écoute `auth-callback-success` / `auth-callback-error` et met à jour l'état
|
||||||
|
|
||||||
|
Pourquoi cet enchaînement est critique :
|
||||||
|
- **Sans `tauri-plugin-single-instance`** : une nouvelle instance démarre à chaque callback, le `OAuthState` est vide (pas de verifier), le token exchange échoue
|
||||||
|
- **Sans `on_open_url`** : l'ancien listener `app.listen("deep-link://new-url", ...)` ne recevait pas les URLs forwardées par single-instance. L'API canonique v2 via `DeepLinkExt` est nécessaire
|
||||||
|
- **Sans gestion des erreurs** : un callback `?error=...` laissait l'UI bloquée en état "loading" infini
|
||||||
|
|
||||||
|
Fichiers : `src-tauri/src/lib.rs` (wiring), `src-tauri/src/commands/auth_commands.rs` (PKCE + token exchange), `src/hooks/useAuth.ts` (frontend).
|
||||||
|
|
||||||
## Pages et routing
|
## Pages et routing
|
||||||
|
|
||||||
|
|
@ -233,12 +283,24 @@ Page spéciale : `ProfileSelectionPage` (affichée quand aucun profil n'est acti
|
||||||
|
|
||||||
## CI/CD
|
## CI/CD
|
||||||
|
|
||||||
Workflow GitHub Actions (`release.yml`) déclenché par les tags `v*` :
|
Deux workflows Forgejo Actions (avec miroir GitHub) dans `.forgejo/workflows/` :
|
||||||
|
|
||||||
|
### `check.yml` — Vérifications sur branches et PR
|
||||||
|
|
||||||
|
Déclenché sur chaque push de branche (sauf `main`) et chaque PR vers `main`. Lance en parallèle :
|
||||||
|
- `cargo check` + `cargo test` (Rust)
|
||||||
|
- `npm run build` (tsc + vite)
|
||||||
|
- `npm test` (vitest)
|
||||||
|
|
||||||
|
Doit être vert avant tout merge. Évite de découvrir des régressions au moment du tag de release.
|
||||||
|
|
||||||
|
### `release.yml` — Build et publication
|
||||||
|
|
||||||
|
Déclenché par les tags `v*`. Deux jobs :
|
||||||
1. **build-windows** (windows-latest) → Installeur `.exe` (NSIS)
|
1. **build-windows** (windows-latest) → Installeur `.exe` (NSIS)
|
||||||
2. **build-linux** (ubuntu-22.04) → `.deb` + `.AppImage`
|
2. **build-linux** (ubuntu-22.04) → `.deb` + `.rpm`
|
||||||
|
|
||||||
Fonctionnalités :
|
Fonctionnalités :
|
||||||
- Signature des binaires (clés TAURI_SIGNING_PRIVATE_KEY)
|
- Signature des binaires (clés TAURI_SIGNING_PRIVATE_KEY)
|
||||||
- JSON d'updater pour mises à jour automatiques
|
- JSON d'updater publié sur `https://git.lacompagniemaximus.com/api/packages/maximus/generic/simpl-resultat/latest/latest.json`
|
||||||
- Release GitHub automatique avec notes d'installation
|
- Release Forgejo automatique avec assets et release notes extraites du CHANGELOG.md
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue