Refactor SRP : decoupage de main.py (Brain) en routers et du SettingsComponent (web)

Brain : main.py (1496 l.) reduit a l'assemblage (~95 l.) ; un router par
responsabilite (generation, chat, tables, imports, notebooks, settings, models),
factories DI dans api/deps.py, DTOs chat + mapping anti-corruption separes,
auto-pull embeddings deplace en infrastructure. Chemins HTTP inchanges.

Web : SettingsComponent (729 l.) recentre sur le formulaire (~330 l.) ;
sous-composants standalone updates-section (MAJ + licence Patreon + switch
canal) et ollama-model-manager (liste/pull/suppression de modeles).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 14:41:43 +02:00
parent b0e8fade03
commit 49a94bb73e
21 changed files with 2875 additions and 2560 deletions

View File

@@ -0,0 +1,241 @@
<!-- Bloc Mises a jour (canal stable + canal beta Patreon fusionnes) -->
<section class="card" *ngIf="config.updateCheckEnabled || licenseStatus?.enabled">
<h2>Mises a jour</h2>
<p class="hint">Verifie aupres du registry Docker si une nouvelle version
des conteneurs (core, brain, web) est disponible. Postgres et MinIO sont
exclus — ils sont mis a jour manuellement.</p>
<div *ngIf="licenseSuccess" class="alert alert-success">
<lucide-icon [img]="Check" [size]="16"></lucide-icon>
<span>{{ licenseSuccess }}</span>
</div>
<!-- ====================================================== -->
<!-- Sous-section : canal stable -->
<!-- ====================================================== -->
<div class="channel-block" *ngIf="config.updateCheckEnabled">
<h3 class="channel-title">Canal stable</h3>
<div class="form-row">
<button type="button" class="btn-secondary" (click)="checkUpdates()" [disabled]="updateChecking">
<lucide-icon [img]="RefreshCw" [size]="14"></lucide-icon>
<span>{{ updateChecking ? 'Verification...' : 'Verifier maintenant' }}</span>
</button>
</div>
<div *ngIf="updateStatus && !updateStatus.enabled" class="hint">
Feature non configuree (WATCHTOWER_TOKEN absent).
</div>
<div *ngIf="updateStatus?.enabled">
<div *ngIf="updateStatus?.updateAvailable" class="alert alert-success">
<lucide-icon [img]="Download" [size]="16"></lucide-icon>
<span>Une mise a jour est disponible.</span>
</div>
<div *ngIf="updateStatus?.anyUnknown && !updateStatus?.updateAvailable" class="alert alert-warn">
<lucide-icon [img]="Download" [size]="16"></lucide-icon>
<span>Verification impossible (baseline absente ou registry injoignable).</span>
</div>
<div *ngIf="!updateStatus?.updateAvailable && !updateStatus?.anyUnknown" class="hint">
Tout est a jour (verifie le {{ updateStatus?.checkedAt | date:'short' }}).
</div>
<div class="form-row" *ngIf="updateStatus?.updateAvailable">
<button type="button" class="btn-primary" (click)="applyUpdate()" [disabled]="updateApplying">
<lucide-icon [img]="Download" [size]="16"></lucide-icon>
<span>{{ updateApplying ? 'Mise a jour en cours...' : 'Mettre a jour maintenant' }}</span>
</button>
</div>
<div *ngIf="updateMessage" class="alert alert-success">
<lucide-icon [img]="Check" [size]="16"></lucide-icon>
<span>{{ updateMessage }}</span>
</div>
</div>
</div>
<!-- ====================================================== -->
<!-- Sous-section : canal beta (Patreon) -->
<!-- ====================================================== -->
<div class="channel-block" *ngIf="licenseStatus?.enabled">
<h3 class="channel-title">
<lucide-icon [img]="Heart" [size]="16"></lucide-icon>
Canal beta &mdash; reserve aux patrons
</h3>
<p class="hint">
Soutiens LoreMind sur Patreon pour acceder aux nouvelles features en avant-premiere.
Le tier <strong>Compagnon</strong> (7&euro;/mois) ou superieur debloque ce canal.
</p>
<!-- Pas de licence installee -->
<ng-container *ngIf="licenseStatus?.status === 'NONE'">
<div class="form-row">
<button type="button" class="btn-primary" (click)="connectPatreon()">
<lucide-icon [img]="Link2" [size]="16"></lucide-icon>
<span>Connecter mon compte Patreon</span>
</button>
</div>
<p class="hint">
Une nouvelle fenetre va s'ouvrir vers Patreon. Apres autorisation, copie le token affiche
et colle-le ci-dessous.
</p>
<div class="form-row">
<label for="license-jwt">Token Patreon</label>
<input
id="license-jwt"
type="text"
[(ngModel)]="licenseJwtInput"
placeholder="eyJhbGciOiJFZERTQS..."
autocomplete="off"
>
</div>
<div class="form-row">
<button type="button" class="btn-primary" (click)="installLicense()" [disabled]="!licenseJwtInput.trim()">
<lucide-icon [img]="Check" [size]="16"></lucide-icon>
<span>Activer la licence</span>
</button>
</div>
<div *ngIf="licenseError" class="alert alert-error">
<lucide-icon [img]="AlertCircle" [size]="16"></lucide-icon>
<span>{{ licenseError }}</span>
</div>
</ng-container>
<!-- Licence installee (VALID / GRACE / EXPIRED / UNVERIFIABLE) -->
<ng-container *ngIf="licenseStatus && licenseStatus.status !== 'NONE'">
<div *ngIf="licenseStatus.status === 'VALID'" class="alert alert-success">
<lucide-icon [img]="Check" [size]="16"></lucide-icon>
<span>Compte Patreon connecte. Tier {{ tierLabel(licenseStatus.tierId) }} actif.</span>
</div>
<div *ngIf="licenseStatus.status === 'GRACE'" class="alert alert-warn">
<lucide-icon [img]="AlertCircle" [size]="16"></lucide-icon>
<span>
Connexion Patreon expiree, mais acces beta maintenu pendant la periode de tolerance.
Verifie que ton abonnement Patreon est toujours actif et clique sur "Verifier maintenant".
</span>
</div>
<div *ngIf="licenseStatus.status === 'EXPIRED'" class="alert alert-error">
<lucide-icon [img]="AlertCircle" [size]="16"></lucide-icon>
<span>
Connexion Patreon expiree depuis trop longtemps. Reconnecte-toi pour retrouver l'acces beta.
</span>
</div>
<div *ngIf="licenseStatus.status === 'UNVERIFIABLE'" class="alert alert-error">
<lucide-icon [img]="AlertCircle" [size]="16"></lucide-icon>
<span>Le token installe ne peut plus etre verifie. Reconnecte-toi.</span>
</div>
<ul class="license-info">
<li *ngIf="licenseStatus.tierId"><strong>Tier :</strong> {{ tierLabel(licenseStatus.tierId) }}</li>
<li *ngIf="licenseStatus.expiresAt">
<strong>Validite :</strong>
jusqu'au {{ formatDate(licenseStatus.expiresAt) }}
<span *ngIf="daysUntilExpiry !== null && daysUntilExpiry > 0">
(renouvellement dans {{ daysUntilExpiry }} jour<span *ngIf="daysUntilExpiry > 1">s</span>)
</span>
</li>
<li *ngIf="licenseStatus.lastRefreshAttemptAt">
<strong>Dernier refresh :</strong>
{{ formatDate(licenseStatus.lastRefreshAttemptAt) }}
<span *ngIf="licenseStatus.lastRefreshSucceeded === true" class="badge-ok">OK</span>
<span *ngIf="licenseStatus.lastRefreshSucceeded === false" class="badge-warn">echec</span>
</li>
</ul>
<div class="form-row form-row-inline">
<label class="checkbox">
<input
type="checkbox"
[checked]="licenseStatus.betaChannelEnabled"
(change)="toggleBetaChannel(!licenseStatus.betaChannelEnabled)"
[disabled]="licenseStatus.status !== 'VALID' && licenseStatus.status !== 'GRACE'"
>
<span>Activer le canal beta</span>
</label>
</div>
<div class="form-row form-row-actions">
<button type="button" class="btn-secondary" (click)="refreshLicense()" [disabled]="licenseLoading">
<lucide-icon [img]="RefreshCw" [size]="14"></lucide-icon>
<span>{{ licenseLoading ? 'Verification...' : 'Verifier maintenant' }}</span>
</button>
<button type="button" class="btn-secondary btn-danger" (click)="disconnectPatreon()">
<lucide-icon [img]="Unlink" [size]="14"></lucide-icon>
<span>Deconnecter Patreon</span>
</button>
</div>
<!-- Etat du canal beta -->
<div *ngIf="licenseStatus.betaChannelEnabled" class="beta-status">
<div *ngIf="betaChecking" class="hint">Verification des images beta...</div>
<div *ngIf="!betaChecking && betaStatus && !betaStatus.enabled" class="hint">
Indisponible : {{ betaStatus.disabledReason }}
</div>
<div *ngIf="!betaChecking && betaStatus?.enabled">
<div *ngIf="betaStatus?.anyUnknown" class="alert alert-warn">
<lucide-icon [img]="AlertCircle" [size]="16"></lucide-icon>
<span>Verification beta impossible (registry beta injoignable ou baseline absente).</span>
</div>
</div>
</div>
<!-- Bascule de canal (stable <-> beta) via sidecar switcher -->
<div class="channel-switch" *ngIf="channelStatus">
<div class="channel-current">
<span class="channel-label">Canal actuel :</span>
<span class="channel-badge"
[class.channel-stable]="channelStatus.currentChannel === 'stable'"
[class.channel-beta]="channelStatus.currentChannel === 'beta'">
{{ channelStatus.currentChannel === 'beta' ? 'Bêta' : 'Stable' }}
</span>
</div>
<!-- Sidecar dispo : boutons d'action -->
<ng-container *ngIf="channelStatus.switcherAvailable">
<!-- On stable -> proposer passage beta (uniquement si licence active) -->
<button *ngIf="channelStatus.currentChannel === 'stable'"
type="button" class="btn-primary"
[disabled]="switchInFlight"
(click)="requestChannelSwitch('beta')">
<lucide-icon [img]="Download" [size]="14"></lucide-icon>
<span>{{ switchInFlight ? 'Bascule en cours...' : 'Passer sur le canal beta' }}</span>
</button>
<!-- On beta -> proposer retour stable -->
<button *ngIf="channelStatus.currentChannel === 'beta'"
type="button" class="btn-secondary"
[disabled]="switchInFlight"
(click)="requestChannelSwitch('stable')">
<lucide-icon [img]="ArrowLeft" [size]="14"></lucide-icon>
<span>{{ switchInFlight ? 'Bascule en cours...' : 'Repasser sur le canal stable' }}</span>
</button>
<!-- Switch en cours : on prévient que la page va se rendre injoignable -->
<div *ngIf="switchInFlight" class="alert alert-warn">
<lucide-icon [img]="RefreshCw" [size]="16"></lucide-icon>
<span>Bascule en cours. L'application va etre indisponible 10 a 30 secondes — la page se rechargera automatiquement quand le nouveau Core sera pret.</span>
</div>
<!-- Erreur eventuelle remontee par le sidecar -->
<div *ngIf="switchError && !switchInFlight" class="alert alert-error">
<lucide-icon [img]="AlertCircle" [size]="16"></lucide-icon>
<span>{{ switchError }}</span>
</div>
</ng-container>
<!-- Sidecar PAS dispo : fallback instructions manuelles (vieilles installs) -->
<div *ngIf="!channelStatus.switcherAvailable" class="alert alert-warn">
<lucide-icon [img]="AlertCircle" [size]="16"></lucide-icon>
<span>
Le sidecar de bascule n'est pas installe. Pour beneficier du switch
automatique, recupere le dernier <code>docker-compose.yml</code> du repo
et fais <code>docker compose pull &amp;&amp; docker compose up -d</code> une
fois. Sinon, bascule manuellement en editant <code>IMAGE_NAMESPACE</code>
dans ton <code>.env</code> (<code>igmlcreation/loremind-</code> pour stable,
<code>igmlcreation/loremind-beta-</code> pour beta).
</span>
</div>
</div>
</ng-container>
</div>
</section>