diff --git a/docs/architecture.md b/docs/architecture.md index ad4d8b6..231eca8 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,6 +1,6 @@ # 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 @@ -153,7 +153,7 @@ Chaque hook encapsule la logique d'état via `useReducer` : | `useLicense` | État de la licence et entitlements | | `useAuth` | Authentification Compte Maximus (OAuth2 PKCE, subscription status) | -## Commandes Tauri (25) +## Commandes Tauri (34) ### `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 - `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) - `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 - `get_edition` — Détection de l'édition active (free/base/premium) - `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) - `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 @@ -233,12 +283,24 @@ Page spéciale : `ProfileSelectionPage` (affichée quand aucun profil n'est acti ## 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) -2. **build-linux** (ubuntu-22.04) → `.deb` + `.AppImage` +2. **build-linux** (ubuntu-22.04) → `.deb` + `.rpm` Fonctionnalités : - Signature des binaires (clés TAURI_SIGNING_PRIVATE_KEY) -- JSON d'updater pour mises à jour automatiques -- Release GitHub automatique avec notes d'installation +- JSON d'updater publié sur `https://git.lacompagniemaximus.com/api/packages/maximus/generic/simpl-resultat/latest/latest.json` +- Release Forgejo automatique avec assets et release notes extraites du CHANGELOG.md