feat: implémentation réelle du service Windows via NSSM (install/uninstall)
This commit is contained in:
Generated
+31
@@ -536,6 +536,12 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
@@ -545,6 +551,12 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_home"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@@ -1302,6 +1314,7 @@ dependencies = [
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tray-icon",
|
||||
"which",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -3246,6 +3259,18 @@ version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "7.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762"
|
||||
dependencies = [
|
||||
"either",
|
||||
"env_home",
|
||||
"rustix",
|
||||
"winsafe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@@ -3510,6 +3535,12 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winsafe"
|
||||
version = "0.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.57.1"
|
||||
|
||||
@@ -57,6 +57,8 @@ image = { version = "0.25", default-features = false, features = ["png"] }
|
||||
arboard = "3"
|
||||
# System tray
|
||||
tray-icon = "0.19"
|
||||
# Find executables in PATH (NSSM discovery)
|
||||
which = "7"
|
||||
# Windows API (pour titre fenêtre active)
|
||||
windows-sys = { version = "0.59", features = [
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Changelog — imagod-web v0.2.0+build.29-9cabfb5-dirty</title>
|
||||
<title>Changelog — imagod-web v0.2.0+build.30-ce93740-dirty</title>
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🖼️</text></svg>">
|
||||
<style>
|
||||
* { margin:0; padding:0; box-sizing:border-box; }
|
||||
@@ -37,7 +37,7 @@ footer { text-align:center; color:#484f58; font-size:0.8rem; padding:2rem; }
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>📋 imagod-web <span class="version-badge">v0.2.0+build.29-9cabfb5-dirty</span></h1>
|
||||
<h1>📋 imagod-web <span class="version-badge">v0.2.0+build.30-ce93740-dirty</span></h1>
|
||||
<nav>
|
||||
<a href="/">🖼️ Galerie</a>
|
||||
<a href="/changelog" class="active">📋 Changelog</a>
|
||||
@@ -107,6 +107,6 @@ footer { text-align:center; color:#484f58; font-size:0.8rem; padding:2rem; }
|
||||
<h2>Légende des tags</h2>
|
||||
|
||||
</main>
|
||||
<footer>imagod-web v0.2.0+build.29-9cabfb5-dirty — <a href="https://git.dracodev.net/Projets/imagod-web">Gitea</a></footer>
|
||||
<footer>imagod-web v0.2.0+build.30-ce93740-dirty — <a href="https://git.dracodev.net/Projets/imagod-web">Gitea</a></footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Aide & Roadmap — imagod-web v0.2.0+build.29-9cabfb5-dirty</title>
|
||||
<title>Aide & Roadmap — imagod-web v0.2.0+build.30-ce93740-dirty</title>
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🖼️</text></svg>">
|
||||
<style>
|
||||
* { margin:0; padding:0; box-sizing:border-box; }
|
||||
@@ -37,7 +37,7 @@ footer { text-align:center; color:#484f58; font-size:0.8rem; padding:2rem; }
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>❓ imagod-web <span class="version-badge">v0.2.0+build.29-9cabfb5-dirty</span></h1>
|
||||
<h1>❓ imagod-web <span class="version-badge">v0.2.0+build.30-ce93740-dirty</span></h1>
|
||||
<nav>
|
||||
<a href="/">🖼️ Galerie</a>
|
||||
<a href="/changelog">📋 Changelog</a>
|
||||
@@ -169,6 +169,6 @@ footer { text-align:center; color:#484f58; font-size:0.8rem; padding:2rem; }
|
||||
<p>5. <strong>Corriger le backend Imago</strong> (thumbnails, upload, stockage)</p>
|
||||
|
||||
</main>
|
||||
<footer>imagod-web v0.2.0+build.29-9cabfb5-dirty — <a href="https://git.dracodev.net/Projets/imagod-web">Gitea</a></footer>
|
||||
<footer>imagod-web v0.2.0+build.30-ce93740-dirty — <a href="https://git.dracodev.net/Projets/imagod-web">Gitea</a></footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -465,7 +465,7 @@
|
||||
<button class="btn" id="clear-btn" style="display:none">✕</button>
|
||||
</div>
|
||||
<button class="btn accent" id="header-upload-btn" onclick="document.getElementById('drop-zone').scrollIntoView({behavior:'smooth'}); document.getElementById('file-input').click()">📤 Upload</button>
|
||||
<span class="version-badge" id="version-badge">v0.2.0+build.29-9cabfb5-dirty</span>
|
||||
<span class="version-badge" id="version-badge">v0.2.0+build.30-ce93740-dirty</span>
|
||||
</div>
|
||||
|
||||
<!-- ─── Navigation ─── -->
|
||||
|
||||
+135
-6
@@ -1,12 +1,114 @@
|
||||
//! Service Windows — Installation via NSSM (stub)
|
||||
//! Phase 10: Code NSSM complet activé sur build Windows natif.
|
||||
//! Service Windows — Installation via NSSM
|
||||
//!
|
||||
//! `install` : crée un service Windows qui lance `imagod-web serve`
|
||||
//! `uninstall` : arrête et supprime le service
|
||||
//!
|
||||
//! NSSM doit être présent dans le même dossier que imagod-web.exe
|
||||
//! ou dans le PATH. Téléchargeable sur https://nssm.cc/download
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
/// Trouve l'exécutable NSSM
|
||||
fn find_nssm() -> Option<PathBuf> {
|
||||
// 1. À côté de l'exécutable imagod-web.exe
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
let nssm_next = exe.with_file_name("nssm.exe");
|
||||
if nssm_next.exists() {
|
||||
return Some(nssm_next);
|
||||
}
|
||||
}
|
||||
// 2. Dans le PATH
|
||||
which::which("nssm").ok()
|
||||
}
|
||||
|
||||
/// Retourne le chemin de l'exécutable imagod-web
|
||||
fn own_exe_path() -> anyhow::Result<String> {
|
||||
Ok(std::env::current_exe()?
|
||||
.to_string_lossy()
|
||||
.to_string())
|
||||
}
|
||||
|
||||
pub fn install() -> anyhow::Result<()> {
|
||||
if !cfg!(windows) {
|
||||
anyhow::bail!("L'installation en service est disponible uniquement sous Windows.\nCompilation Windows: cargo build --release --target x86_64-pc-windows-gnu");
|
||||
anyhow::bail!("L'installation en service est disponible uniquement sous Windows");
|
||||
}
|
||||
println!("Installation du service Windows imagod-web...");
|
||||
println!("(Build Windows natif requis pour l'installation NSSM automatique)");
|
||||
|
||||
let nssm = find_nssm().ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"NSSM introuvable.\n\n\
|
||||
Pour installer le service Windows, téléchargez NSSM :\n \
|
||||
https://nssm.cc/download\n\n\
|
||||
Puis placez nssm.exe dans le même dossier que imagod-web.exe,\n\
|
||||
ou ajoutez-le à votre PATH.\n\n\
|
||||
Alternative manuelle :\n \
|
||||
nssm install imagod-web \"C:\\chemin\\vers\\imagod-web.exe\" serve"
|
||||
)
|
||||
})?;
|
||||
|
||||
let exe_path = own_exe_path()?;
|
||||
let service_name = "imagod-web";
|
||||
|
||||
println!("🔧 Installation du service Windows '{}'...", service_name);
|
||||
println!(" Binaire : {}", exe_path);
|
||||
println!(" NSSM : {}", nssm.display());
|
||||
|
||||
// Arrêter le service s'il tourne déjà
|
||||
let _ = Command::new(&nssm)
|
||||
.args(["stop", service_name])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status();
|
||||
|
||||
// Installer le service
|
||||
let install_status = Command::new(&nssm)
|
||||
.args(["install", service_name, &exe_path])
|
||||
.status()?;
|
||||
|
||||
if !install_status.success() {
|
||||
anyhow::bail!("Échec de l'installation du service avec NSSM");
|
||||
}
|
||||
|
||||
// Configurer le service
|
||||
let settings = [
|
||||
// Commande : "serve" (watcher + dashboard)
|
||||
(["set", service_name, "AppParameters", "serve"], "Paramètres de l'application"),
|
||||
// Démarrage automatique
|
||||
(["set", service_name, "Start", "SERVICE_AUTO_START"], "Type de démarrage"),
|
||||
// Redémarrage automatique en cas d'erreur
|
||||
(["set", service_name, "AppExit", "Default", "Restart"], "Redémarrage auto"),
|
||||
// Nom affiché
|
||||
(["set", service_name, "DisplayName", "Imago Dashboard + Watcher"], "Nom affiché"),
|
||||
// Description
|
||||
(["set", service_name, "Description", "Dashboard web Imago + surveillance du presse-papier"], "Description"),
|
||||
];
|
||||
|
||||
for (args, label) in &settings {
|
||||
let status = Command::new(&nssm).args(args).status()?;
|
||||
if !status.success() {
|
||||
println!("⚠️ Attention : échec configuration '{}'", label);
|
||||
}
|
||||
}
|
||||
|
||||
// Démarrer le service
|
||||
println!("🚀 Démarrage du service...");
|
||||
let start_status = Command::new(&nssm)
|
||||
.args(["start", service_name])
|
||||
.status()?;
|
||||
|
||||
if start_status.success() {
|
||||
println!("✅ Service '{}' installé et démarré !", service_name);
|
||||
println!(" Dashboard : http://127.0.0.1:9199");
|
||||
println!("\n Commandes utiles :");
|
||||
println!(" nssm status imagod-web — Voir l'état");
|
||||
println!(" nssm restart imagod-web — Redémarrer");
|
||||
println!(" nssm stop imagod-web — Arrêter");
|
||||
println!(" imagod-web uninstall — Désinstaller");
|
||||
} else {
|
||||
println!("⚠️ Service installé mais le démarrage a échoué.");
|
||||
println!(" Vérifiez avec : nssm status imagod-web");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -14,6 +116,33 @@ pub fn uninstall() -> anyhow::Result<()> {
|
||||
if !cfg!(windows) {
|
||||
anyhow::bail!("Désinstallation disponible uniquement sous Windows");
|
||||
}
|
||||
println!("Désinstallation du service...");
|
||||
|
||||
let nssm = find_nssm().ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"NSSM introuvable.\n\
|
||||
Désinstallation manuelle : nssm remove imagod-web confirm"
|
||||
)
|
||||
})?;
|
||||
|
||||
let service_name = "imagod-web";
|
||||
|
||||
println!("🛑 Arrêt du service '{}'...", service_name);
|
||||
let _ = Command::new(&nssm)
|
||||
.args(["stop", service_name])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status();
|
||||
|
||||
println!("🗑️ Suppression du service...");
|
||||
let status = Command::new(&nssm)
|
||||
.args(["remove", service_name, "confirm"])
|
||||
.status()?;
|
||||
|
||||
if status.success() {
|
||||
println!("✅ Service '{}' désinstallé.", service_name);
|
||||
} else {
|
||||
anyhow::bail!("Échec de la désinstallation. Essayez manuellement : nssm remove imagod-web confirm");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user