fix: --config flag ignoré par la commande serve + Config::load_from()
- Ajout de Config::load_from(path) pour charger depuis un chemin custom - cmd_serve() accepte maintenant un Option<PathBuf> pour --config - Le flag --config du CLI est maintenant passé à cmd_serve() - Version bump 0.2.1 → 0.2.2 Corrige le bug où le service Windows NSSM passait --config mais la commande serve lisait toujours depuis %APPDATA%/imagod-web/imagod.toml
This commit is contained in:
Generated
+3
-1
@@ -667,6 +667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1285,7 +1286,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "imagod-web"
|
||||
version = "0.2.0"
|
||||
version = "0.2.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arboard",
|
||||
@@ -2237,6 +2238,7 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "imagod-web"
|
||||
version = "0.2.0"
|
||||
version = "0.2.2"
|
||||
edition = "2021"
|
||||
description = "Imago Daemon + Dashboard — Service de capture automatique de screenshots + Dashboard web embarqué"
|
||||
authors = ["Bruno Charest <[email protected]>"]
|
||||
@@ -15,7 +15,7 @@ clap = { version = "4", features = ["derive", "env"] }
|
||||
# Async runtime
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
# HTTP Client (API Imago) — rustls pour éviter OpenSSL
|
||||
reqwest = { version = "0.12", default-features = false, features = ["json", "multipart", "stream", "rustls-tls"] }
|
||||
reqwest = { version = "0.12", default-features = false, features = ["json", "multipart", "stream", "rustls-tls", "blocking"] }
|
||||
# Serialization
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+60
-35
@@ -1,80 +1,105 @@
|
||||
//! Parsing CLI avec clap
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(
|
||||
name = "imagod-web",
|
||||
version = env!("CARGO_PKG_VERSION"),
|
||||
about = "Imago Daemon + Dashboard — Capture automatique de screenshots + Dashboard web embarqué",
|
||||
long_about = "Service de capture automatique de screenshots vers Imago avec dashboard web embarqué.\n\n\
|
||||
Modes :\n \
|
||||
Windows : imagod-web start → watcher clipboard + dashboard\n \
|
||||
Linux : imagod-web serve → watcher dossier + dashboard"
|
||||
version = env!("IMAGOD_BUILD_VERSION"),
|
||||
about = "Imago Daemon + Dashboard — Watcher clipboard et dashboard web",
|
||||
long_about = "Companion multiplateforme pour le backend Imago. Dashboard web, watcher clipboard, gestionnaire d'upload."
|
||||
)]
|
||||
pub struct Args {
|
||||
pub struct Cli {
|
||||
#[command(subcommand)]
|
||||
pub command: Command,
|
||||
pub cmd: Command,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum Command {
|
||||
/// Lancer le watcher + dashboard (Windows uniquement)
|
||||
/// Lancer le watcher + dashboard
|
||||
Start {
|
||||
/// Port du dashboard web
|
||||
#[arg(long, default_value = "9090")]
|
||||
/// Port d'écoute
|
||||
#[arg(long, default_value = "9199")]
|
||||
port: u16,
|
||||
/// Ouvrir le dashboard dans le navigateur
|
||||
|
||||
/// Ouvrir le navigateur
|
||||
#[arg(long)]
|
||||
open_browser: bool,
|
||||
},
|
||||
/// Lancer le dashboard web uniquement (Linux/serveur)
|
||||
|
||||
/// Lancer le dashboard web (+ watcher dossier sur Linux)
|
||||
Serve {
|
||||
/// Port du dashboard web
|
||||
#[arg(long, default_value = "9090")]
|
||||
/// Port d'écoute
|
||||
#[arg(long, default_value = "9199")]
|
||||
port: u16,
|
||||
/// Interface d'écoute
|
||||
|
||||
/// Adresse d'écoute
|
||||
#[arg(long, default_value = "127.0.0.1")]
|
||||
host: String,
|
||||
/// Désactiver le watcher clipboard
|
||||
|
||||
/// Désactiver le watcher (surveillance du presse-papier)
|
||||
#[arg(long)]
|
||||
no_watcher: bool,
|
||||
/// Ouvrir le dashboard dans le navigateur
|
||||
|
||||
/// Ouvrir le navigateur
|
||||
#[arg(long)]
|
||||
open_browser: bool,
|
||||
|
||||
/// Chemin du fichier de configuration
|
||||
#[arg(long)]
|
||||
config: Option<PathBuf>,
|
||||
},
|
||||
|
||||
/// Arrêter le service
|
||||
Stop,
|
||||
/// Afficher l'état du service
|
||||
|
||||
/// Afficher l'état
|
||||
Status,
|
||||
|
||||
/// Gérer la configuration
|
||||
Config {
|
||||
#[command(subcommand)]
|
||||
action: ConfigAction,
|
||||
},
|
||||
/// Installer comme service Windows
|
||||
Install,
|
||||
|
||||
/// Installer en tant que service Windows
|
||||
Install {
|
||||
/// Chemin du fichier de configuration
|
||||
#[arg(short, long)]
|
||||
config: Option<PathBuf>,
|
||||
|
||||
/// Clé API Imago
|
||||
#[arg(short, long)]
|
||||
api_key: Option<String>,
|
||||
},
|
||||
|
||||
/// Désinstaller le service Windows
|
||||
Uninstall,
|
||||
/// Afficher les derniers logs
|
||||
Logs,
|
||||
|
||||
/// Mettre à jour depuis Nexus
|
||||
Update {
|
||||
/// Forcer même si déjà à jour
|
||||
#[arg(short, long)]
|
||||
force: bool,
|
||||
|
||||
/// URL du serveur Nexus
|
||||
#[arg(short, long, default_value = "https://nexus.dracodev.net")]
|
||||
nexus_url: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
#[derive(Subcommand)]
|
||||
pub enum ConfigAction {
|
||||
/// Afficher la configuration actuelle
|
||||
/// Afficher la configuration
|
||||
Show,
|
||||
/// Modifier un paramètre
|
||||
Set {
|
||||
/// Clé de configuration (ex: imago.url)
|
||||
key: String,
|
||||
/// Valeur
|
||||
value: String,
|
||||
},
|
||||
/// Réinitialiser la configuration par défaut
|
||||
/// Modifier une clé
|
||||
Set { key: String, value: String },
|
||||
/// Réinitialiser
|
||||
Reset,
|
||||
}
|
||||
|
||||
pub fn parse() -> Args {
|
||||
Args::parse()
|
||||
/// Parse les arguments CLI
|
||||
pub fn parse() -> Command {
|
||||
<Cli as clap::Parser>::parse().cmd
|
||||
}
|
||||
|
||||
+12
-2
@@ -160,15 +160,25 @@ impl Config {
|
||||
/// Charge la configuration depuis `%APPDATA%/imagod-web/imagod.toml`
|
||||
pub fn load() -> Result<Self> {
|
||||
let path = Self::config_path()?;
|
||||
Self::load_from(&path)
|
||||
}
|
||||
|
||||
/// Charge la configuration depuis un chemin spécifique
|
||||
pub fn load_from(path: &PathBuf) -> Result<Self> {
|
||||
if path.exists() {
|
||||
let content = std::fs::read_to_string(&path)
|
||||
let content = std::fs::read_to_string(path)
|
||||
.with_context(|| format!("Impossible de lire {}", path.display()))?;
|
||||
let config: Config = toml::from_str(&content)
|
||||
.with_context(|| "Erreur de parsing du fichier de configuration")?;
|
||||
Ok(config)
|
||||
} else {
|
||||
let config = Config::default();
|
||||
config.save()?;
|
||||
// Sauvegarder au chemin spécifié
|
||||
if let Some(parent) = path.parent() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
let content = toml::to_string_pretty(&config)?;
|
||||
std::fs::write(path, &content)?;
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.32-fb292ea-dirty</title>
|
||||
<title>Changelog — imagod-web v0.2.2+build.34-8db614c-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.32-fb292ea-dirty</span></h1>
|
||||
<h1>📋 imagod-web <span class="version-badge">v0.2.2+build.34-8db614c-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.32-fb292ea-dirty — <a href="https://git.dracodev.net/Projets/imagod-web">Gitea</a></footer>
|
||||
<footer>imagod-web v0.2.2+build.34-8db614c-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.32-fb292ea-dirty</title>
|
||||
<title>Aide & Roadmap — imagod-web v0.2.2+build.34-8db614c-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.32-fb292ea-dirty</span></h1>
|
||||
<h1>❓ imagod-web <span class="version-badge">v0.2.2+build.34-8db614c-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.32-fb292ea-dirty — <a href="https://git.dracodev.net/Projets/imagod-web">Gitea</a></footer>
|
||||
<footer>imagod-web v0.2.2+build.34-8db614c-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.32-fb292ea-dirty</span>
|
||||
<span class="version-badge" id="version-badge">v0.2.2+build.34-8db614c-dirty</span>
|
||||
</div>
|
||||
|
||||
<!-- ─── Navigation ─── -->
|
||||
|
||||
+32
-21
@@ -21,13 +21,11 @@ mod watcher;
|
||||
mod clipboard;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod tray;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod service;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod window_info;
|
||||
|
||||
mod server;
|
||||
|
||||
#[cfg(windows)]
|
||||
mod service;
|
||||
mod update;
|
||||
mod window_info;
|
||||
use anyhow::{Context, Result};
|
||||
use std::path::PathBuf;
|
||||
use std::process;
|
||||
@@ -212,7 +210,7 @@ async fn cmd_start(port: u16, open_browser: bool) -> Result<()> {
|
||||
}
|
||||
|
||||
/// `serve` — Lance le dashboard web uniquement
|
||||
async fn cmd_serve(host: &str, port: u16, no_watcher: bool, open_browser: bool) -> Result<()> {
|
||||
async fn cmd_serve(host: &str, port: u16, no_watcher: bool, open_browser: bool, config_path: Option<PathBuf>) -> Result<()> {
|
||||
// Vérifier qu'il n'y a pas déjà une instance
|
||||
if let Some(pid) = is_running() {
|
||||
anyhow::bail!(
|
||||
@@ -222,7 +220,12 @@ async fn cmd_serve(host: &str, port: u16, no_watcher: bool, open_browser: bool)
|
||||
);
|
||||
}
|
||||
|
||||
let cfg = config::Config::load()?;
|
||||
let cfg = if let Some(ref path) = config_path {
|
||||
tracing::info!("📄 Configuration chargée depuis : {}", path.display());
|
||||
config::Config::load_from(path)?
|
||||
} else {
|
||||
config::Config::load()?
|
||||
};
|
||||
|
||||
// Écrire le PID
|
||||
write_pid()?;
|
||||
@@ -498,13 +501,12 @@ async fn main() {
|
||||
setup_logging();
|
||||
|
||||
let args = cli::parse();
|
||||
|
||||
let result = match args.command {
|
||||
let result = match args {
|
||||
cli::Command::Start { port, open_browser } => {
|
||||
cmd_start(port, open_browser).await
|
||||
}
|
||||
cli::Command::Serve { port, host, no_watcher, open_browser } => {
|
||||
cmd_serve(&host, port, no_watcher, open_browser).await
|
||||
cli::Command::Serve { port, host, no_watcher, open_browser, config } => {
|
||||
cmd_serve(&host, port, no_watcher, open_browser, config).await
|
||||
}
|
||||
cli::Command::Stop => {
|
||||
cmd_stop()
|
||||
@@ -515,17 +517,16 @@ async fn main() {
|
||||
cli::Command::Config { action } => {
|
||||
cmd_config(&action)
|
||||
}
|
||||
cli::Command::Logs => {
|
||||
cmd_logs()
|
||||
}
|
||||
cli::Command::Install => {
|
||||
install_service()
|
||||
cli::Command::Install { config, api_key } => {
|
||||
install_service(config, api_key)
|
||||
}
|
||||
cli::Command::Uninstall => {
|
||||
uninstall_service()
|
||||
}
|
||||
cli::Command::Update { force, nexus_url } => {
|
||||
update_binary(force, &nexus_url)
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = result {
|
||||
eprintln!("❌ Erreur : {:#}", e);
|
||||
process::exit(1);
|
||||
@@ -533,13 +534,13 @@ async fn main() {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn install_service() -> anyhow::Result<()> {
|
||||
fn install_service(config: Option<PathBuf>, api_key: Option<String>) -> anyhow::Result<()> {
|
||||
tracing::info!("Installation du service Windows...");
|
||||
service::install()
|
||||
service::install(config, api_key)
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn install_service() -> anyhow::Result<()> {
|
||||
fn install_service(_config: Option<PathBuf>, _api_key: Option<String>) -> anyhow::Result<()> {
|
||||
anyhow::bail!("La commande 'install' n'est disponible que sur Windows (service NSSM)")
|
||||
}
|
||||
|
||||
@@ -549,6 +550,16 @@ fn uninstall_service() -> anyhow::Result<()> {
|
||||
service::uninstall()
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn update_binary(_force: bool, _nexus_url: &str) -> anyhow::Result<()> {
|
||||
crate::update::check_and_update(_force, _nexus_url)
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn update_binary(_force: bool, _nexus_url: &str) -> anyhow::Result<()> {
|
||||
anyhow::bail!("La commande 'update' n'est disponible que sur Windows (remplacement du binaire + service NSSM)")
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn uninstall_service() -> anyhow::Result<()> {
|
||||
anyhow::bail!("La commande 'uninstall' n'est disponible que sur Windows (service NSSM)")
|
||||
|
||||
+46
-4
@@ -6,7 +6,8 @@
|
||||
//! 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 anyhow::Context;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
/// Trouve l'exécutable NSSM
|
||||
@@ -29,7 +30,7 @@ fn own_exe_path() -> anyhow::Result<String> {
|
||||
.to_string())
|
||||
}
|
||||
|
||||
pub fn install() -> anyhow::Result<()> {
|
||||
pub fn install(config_override: Option<PathBuf>, api_key: Option<String>) -> anyhow::Result<()> {
|
||||
if !cfg!(windows) {
|
||||
anyhow::bail!("L'installation en service est disponible uniquement sous Windows");
|
||||
}
|
||||
@@ -49,6 +50,44 @@ pub fn install() -> anyhow::Result<()> {
|
||||
let exe_path = own_exe_path()?;
|
||||
let service_name = "imagod-web";
|
||||
|
||||
let exe_dir = Path::new(&exe_path).parent().unwrap_or(Path::new("."));
|
||||
|
||||
// --- Config auto-génération ---
|
||||
let config_path = if let Some(ref cfg) = config_override {
|
||||
cfg.clone()
|
||||
} else {
|
||||
let default_config = exe_dir.join("imagod.toml");
|
||||
if !default_config.exists() {
|
||||
let key = api_key.unwrap_or_default();
|
||||
let config_content = format!(
|
||||
r#"[imago]
|
||||
base_url = "http://imago.dracodev.net"
|
||||
api_key = "{}"
|
||||
|
||||
[server]
|
||||
host = "127.0.0.1"
|
||||
port = 9199
|
||||
"#,
|
||||
key
|
||||
);
|
||||
std::fs::write(&default_config, config_content)?;
|
||||
println!("📝 Config créée : {}", default_config.display());
|
||||
if key.is_empty() {
|
||||
println!("⚠️ Clé API non fournie — éditez imagod.toml pour ajouter votre clé Imago");
|
||||
}
|
||||
}
|
||||
default_config
|
||||
};
|
||||
|
||||
// Vérifier que le fichier de config existe
|
||||
if !config_path.exists() {
|
||||
anyhow::bail!(
|
||||
"Fichier de configuration introuvable : {}\n\
|
||||
Créez un fichier imagod.toml ou utilisez --config <chemin>",
|
||||
config_path.display()
|
||||
);
|
||||
}
|
||||
|
||||
println!("🔧 Installation du service Windows '{}'...", service_name);
|
||||
println!(" Binaire : {}", exe_path);
|
||||
println!(" NSSM : {}", nssm.display());
|
||||
@@ -70,9 +109,12 @@ pub fn install() -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
// Configurer le service
|
||||
let config_arg = format!("serve --config {}", config_path.display());
|
||||
let settings: &[(&[&str], &str)] = &[
|
||||
// Commande : "serve" (watcher + dashboard)
|
||||
(&["set", service_name, "AppParameters", "serve"], "Paramètres de l'application"),
|
||||
// Commande : "serve --config <path>"
|
||||
(&["set", service_name, "AppParameters", &config_arg], "Paramètres de l'application"),
|
||||
// Répertoire de travail = là où est le binaire
|
||||
(&["set", service_name, "AppDirectory", &exe_dir.to_string_lossy()], "Répertoire de travail"),
|
||||
// Démarrage automatique
|
||||
(&["set", service_name, "Start", "SERVICE_AUTO_START"], "Type de démarrage"),
|
||||
// Nom affiché
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
//! Module de mise à jour automatique depuis Nexus (Windows uniquement)
|
||||
|
||||
#[cfg(windows)]
|
||||
mod imp {
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
pub fn check_and_update(force: bool, nexus_url: &str) -> anyhow::Result<()> {
|
||||
let current_version = env!("CARGO_PKG_VERSION");
|
||||
let platform = "windows";
|
||||
let exe_path = std::env::current_exe()?;
|
||||
let exe_dir = exe_path.parent().unwrap_or(Path::new("."));
|
||||
|
||||
// 1. Récupérer la version distante
|
||||
let version_url = format!(
|
||||
"{}/repository/raw-hosted/imagod-web/{}/latest/version.txt",
|
||||
nexus_url.trim_end_matches('/'),
|
||||
platform
|
||||
);
|
||||
|
||||
println!("🔍 Vérification de la version sur Nexus...");
|
||||
println!(" Actuelle : v{}", current_version);
|
||||
|
||||
let remote_version = match reqwest::blocking::get(&version_url) {
|
||||
Ok(resp) if resp.status().is_success() => resp.text()?.trim().to_string(),
|
||||
Ok(resp) => anyhow::bail!("Impossible de récupérer la version distante (HTTP {})", resp.status()),
|
||||
Err(e) => anyhow::bail!("Erreur réseau : {}", e),
|
||||
};
|
||||
|
||||
println!(" Distante : v{}", remote_version);
|
||||
|
||||
if !force && remote_version == current_version {
|
||||
println!("✅ Vous utilisez déjà la dernière version.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("📦 Mise à jour : v{} → v{}", current_version, remote_version);
|
||||
|
||||
// 2. Télécharger
|
||||
let bin_name = "imagod-web.exe";
|
||||
let bin_url = format!(
|
||||
"{}/repository/raw-hosted/imagod-web/{}/latest/{}",
|
||||
nexus_url.trim_end_matches('/'), platform, bin_name
|
||||
);
|
||||
|
||||
println!("⬇️ Téléchargement...");
|
||||
let response = reqwest::blocking::get(&bin_url)?;
|
||||
if !response.status().is_success() {
|
||||
anyhow::bail!("Téléchargement échoué (HTTP {})", response.status());
|
||||
}
|
||||
|
||||
let new_binary = response.bytes()?;
|
||||
println!(" Téléchargé : {:.1} MB", new_binary.len() as f64 / 1_048_576.0);
|
||||
|
||||
// 3. Vérifier SHA256
|
||||
let sha_url = format!("{}.sha256", bin_url);
|
||||
let expected_sha = reqwest::blocking::get(&sha_url)?
|
||||
.text()?
|
||||
.split_whitespace()
|
||||
.next()
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
|
||||
use sha2::{Digest, Sha256};
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(&new_binary);
|
||||
let actual_sha = format!("{:x}", hasher.finalize());
|
||||
|
||||
if expected_sha.is_empty() {
|
||||
println!("⚠️ Pas de checksum — vérification ignorée.");
|
||||
} else if actual_sha != expected_sha {
|
||||
anyhow::bail!("❌ SHA256 mismatch!\n Attendu: {}\n Obtenu: {}", expected_sha, actual_sha);
|
||||
} else {
|
||||
println!("✅ SHA256 vérifié");
|
||||
}
|
||||
|
||||
// 4. Remplacer
|
||||
let temp_path = exe_dir.join(format!("{}.new", bin_name));
|
||||
std::fs::write(&temp_path, &new_binary)?;
|
||||
|
||||
let _ = Command::new("nssm").args(["stop", "imagod-web"]).status();
|
||||
std::thread::sleep(std::time::Duration::from_secs(2));
|
||||
|
||||
let backup_path = exe_dir.join(format!("{}.old", bin_name));
|
||||
let _ = std::fs::remove_file(&backup_path);
|
||||
std::fs::rename(&exe_path, &backup_path)?;
|
||||
std::fs::rename(&temp_path, &exe_path)?;
|
||||
|
||||
// 5. Redémarrer
|
||||
let start = Command::new("nssm").args(["start", "imagod-web"]).status();
|
||||
match start {
|
||||
Ok(s) if s.success() => println!("🚀 Service redémarré !"),
|
||||
_ => println!("⚠️ Redémarrage manuel requis : nssm start imagod-web"),
|
||||
}
|
||||
|
||||
println!("🎉 Mise à jour v{} → v{} terminée !", current_version, remote_version);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
mod imp {
|
||||
pub fn check_and_update(_force: bool, _nexus_url: &str) -> anyhow::Result<()> {
|
||||
anyhow::bail!("La commande 'update' n'est disponible que sur Windows")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_and_update(force: bool, nexus_url: &str) -> anyhow::Result<()> {
|
||||
imp::check_and_update(force, nexus_url)
|
||||
}
|
||||
Reference in New Issue
Block a user