feat(app-tauri): cycle de vie du serveur embarqué et persistance de l'exposition (#68 B2)
Permet d'activer le serveur web depuis l'app desktop — la fonctionnalité demandée à l'origine du ticket. - `embedded_server.rs` : `EmbeddedServerController` porté par `AppState`, avec les commandes `embedded_server_start` / `stop` / `status` et `get` / `save` / `preview_server_exposure_settings`. - `FsServerExposureSettingsStore` : écrit `<app-data-dir>/deployment/server-exposure.json`. C'est de la config de sécurité, pas une préférence d'UI — d'où l'app-data-dir global plutôt que `localStorage`. Écriture atomique : temporaire, `0600` posé AVANT le `rename`, donc le fichier final n'est jamais brièvement lisible par tous. - Arrêt du serveur câblé sur la sortie de l'app (`lib.rs`), aux côtés des autres nettoyages. Le serveur embarqué appelle `run_embedded_with_core` avec le `BackendCore` partagé du desktop, jamais `run_embedded` — c'est tout l'intérêt du seam ouvert par B1 : une seule composition root dans le processus, pas deux. DETTE CONNUE, consignée au carnet, non bloquante : - `preview_settings` valide avant de prévisualiser (`embedded_server.rs:211`), ce qui verrouille la liste des candidats : un brouillon en mode `remoteProxyOtherMachine` ne peut pas être prévisualisé tant qu'il n'a pas le `lanBindAddress` que cette liste doit justement fournir. Contourné côté frontend par une sonde `localOnly` toujours valide. Défaut d'ordonnancement, pas une fatalité. - Le warning `missingTrustedProxy` (`embedded_server.rs:452`) est du code mort : `validate_settings` rejette les `trustedProxies` vides en mode 3 avant que `preview_settings` ne puisse le produire. QA ré-exécutée par Git HORS SANDBOX (DevBackend et QA sont bloqués par EPERM sur `bind` ; un vert sandboxé ne prouve rien sur ces crates) : `cargo test -p app-tauri` 43 passed, 1 ignored · `-p web-server` 66 passed, 0 échec. Le test ignoré `mcp_bridge::tests::end_to_end_over_real_loopback` est PRÉEXISTANT sur `develop` et non touché par ce lot ; lancé explicitement hors sandbox avec `--ignored`, il passe. C'est une garde d'environnement, pas un faux vert — distinction qui nous a déjà coûté deux fois aujourd'hui. Invariants vérifiés par Git : appelant `run_embedded_with_core` confirmé, aucune fuite d'`EmbeddedServer*`/`ServerExposure*` dans `domain` ni `application`, ordre chmod/rename correct, `stop()` bien appelé à la fermeture. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -14,6 +14,7 @@ use infrastructure::TicketToolProvider;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::chat::ChatBridge;
|
||||
use crate::embedded_server::EmbeddedServerController;
|
||||
use crate::pty::PtyBridge;
|
||||
use crate::tickets::AppTicketToolProvider;
|
||||
|
||||
@ -38,6 +39,8 @@ pub struct AppState {
|
||||
pub pty_bridge: Arc<PtyBridge>,
|
||||
/// Structured reply to Tauri Channel bridge registry.
|
||||
pub chat_bridge: Arc<ChatBridge>,
|
||||
/// Desktop-owned embedded HTTP server lifecycle manager.
|
||||
pub embedded_server: Arc<EmbeddedServerController>,
|
||||
/// Project currently focused by the main window; panel-only windows follow it.
|
||||
focused_project: Mutex<Option<FocusedProjectDto>>,
|
||||
}
|
||||
@ -48,7 +51,7 @@ impl AppState {
|
||||
/// into the backend core.
|
||||
#[must_use]
|
||||
pub fn build(app_data_dir: PathBuf) -> Self {
|
||||
let core = Arc::new(BackendCore::build(app_data_dir));
|
||||
let core = Arc::new(BackendCore::build(app_data_dir.clone()));
|
||||
core.ticket_tool_binder
|
||||
.bind(Arc::new(AppTicketToolProvider {
|
||||
create: Arc::clone(&core.create_issue),
|
||||
@ -66,10 +69,18 @@ impl AppState {
|
||||
core,
|
||||
pty_bridge: Arc::new(PtyBridge::new()),
|
||||
chat_bridge: Arc::new(ChatBridge::new()),
|
||||
embedded_server: Arc::new(EmbeddedServerController::new(app_data_dir)),
|
||||
focused_project: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clones the shared backend core for adapter components that must share
|
||||
/// the desktop composition root.
|
||||
#[must_use]
|
||||
pub fn core(&self) -> Arc<BackendCore> {
|
||||
Arc::clone(&self.core)
|
||||
}
|
||||
|
||||
/// Updates the focused project state. `None` means no project is focused/open.
|
||||
pub fn set_focused_project(&self, project: Option<FocusedProjectDto>) {
|
||||
*self
|
||||
|
||||
Reference in New Issue
Block a user