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"
|
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1285,7 +1286,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "imagod-web"
|
name = "imagod-web"
|
||||||
version = "0.2.0"
|
version = "0.2.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arboard",
|
"arboard",
|
||||||
@@ -2237,6 +2238,7 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "imagod-web"
|
name = "imagod-web"
|
||||||
version = "0.2.0"
|
version = "0.2.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Imago Daemon + Dashboard — Service de capture automatique de screenshots + Dashboard web embarqué"
|
description = "Imago Daemon + Dashboard — Service de capture automatique de screenshots + Dashboard web embarqué"
|
||||||
authors = ["Bruno Charest <[email protected]>"]
|
authors = ["Bruno Charest <[email protected]>"]
|
||||||
@@ -15,7 +15,7 @@ clap = { version = "4", features = ["derive", "env"] }
|
|||||||
# Async runtime
|
# Async runtime
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
# HTTP Client (API Imago) — rustls pour éviter OpenSSL
|
# 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
|
# Serialization
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
+60
-35
@@ -1,80 +1,105 @@
|
|||||||
//! Parsing CLI avec clap
|
//! Parsing CLI avec clap
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(
|
#[command(
|
||||||
name = "imagod-web",
|
name = "imagod-web",
|
||||||
version = env!("CARGO_PKG_VERSION"),
|
version = env!("IMAGOD_BUILD_VERSION"),
|
||||||
about = "Imago Daemon + Dashboard — Capture automatique de screenshots + Dashboard web embarqué",
|
about = "Imago Daemon + Dashboard — Watcher clipboard et dashboard web",
|
||||||
long_about = "Service de capture automatique de screenshots vers Imago avec dashboard web embarqué.\n\n\
|
long_about = "Companion multiplateforme pour le backend Imago. Dashboard web, watcher clipboard, gestionnaire d'upload."
|
||||||
Modes :\n \
|
|
||||||
Windows : imagod-web start → watcher clipboard + dashboard\n \
|
|
||||||
Linux : imagod-web serve → watcher dossier + dashboard"
|
|
||||||
)]
|
)]
|
||||||
pub struct Args {
|
pub struct Cli {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: Command,
|
pub cmd: Command,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
/// Lancer le watcher + dashboard (Windows uniquement)
|
/// Lancer le watcher + dashboard
|
||||||
Start {
|
Start {
|
||||||
/// Port du dashboard web
|
/// Port d'écoute
|
||||||
#[arg(long, default_value = "9090")]
|
#[arg(long, default_value = "9199")]
|
||||||
port: u16,
|
port: u16,
|
||||||
/// Ouvrir le dashboard dans le navigateur
|
|
||||||
|
/// Ouvrir le navigateur
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
open_browser: bool,
|
open_browser: bool,
|
||||||
},
|
},
|
||||||
/// Lancer le dashboard web uniquement (Linux/serveur)
|
|
||||||
|
/// Lancer le dashboard web (+ watcher dossier sur Linux)
|
||||||
Serve {
|
Serve {
|
||||||
/// Port du dashboard web
|
/// Port d'écoute
|
||||||
#[arg(long, default_value = "9090")]
|
#[arg(long, default_value = "9199")]
|
||||||
port: u16,
|
port: u16,
|
||||||
/// Interface d'écoute
|
|
||||||
|
/// Adresse d'écoute
|
||||||
#[arg(long, default_value = "127.0.0.1")]
|
#[arg(long, default_value = "127.0.0.1")]
|
||||||
host: String,
|
host: String,
|
||||||
/// Désactiver le watcher clipboard
|
|
||||||
|
/// Désactiver le watcher (surveillance du presse-papier)
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
no_watcher: bool,
|
no_watcher: bool,
|
||||||
/// Ouvrir le dashboard dans le navigateur
|
|
||||||
|
/// Ouvrir le navigateur
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
open_browser: bool,
|
open_browser: bool,
|
||||||
|
|
||||||
|
/// Chemin du fichier de configuration
|
||||||
|
#[arg(long)]
|
||||||
|
config: Option<PathBuf>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Arrêter le service
|
/// Arrêter le service
|
||||||
Stop,
|
Stop,
|
||||||
/// Afficher l'état du service
|
|
||||||
|
/// Afficher l'état
|
||||||
Status,
|
Status,
|
||||||
|
|
||||||
/// Gérer la configuration
|
/// Gérer la configuration
|
||||||
Config {
|
Config {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
action: ConfigAction,
|
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
|
/// Désinstaller le service Windows
|
||||||
Uninstall,
|
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 {
|
pub enum ConfigAction {
|
||||||
/// Afficher la configuration actuelle
|
/// Afficher la configuration
|
||||||
Show,
|
Show,
|
||||||
/// Modifier un paramètre
|
/// Modifier une clé
|
||||||
Set {
|
Set { key: String, value: String },
|
||||||
/// Clé de configuration (ex: imago.url)
|
/// Réinitialiser
|
||||||
key: String,
|
|
||||||
/// Valeur
|
|
||||||
value: String,
|
|
||||||
},
|
|
||||||
/// Réinitialiser la configuration par défaut
|
|
||||||
Reset,
|
Reset,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse() -> Args {
|
/// Parse les arguments CLI
|
||||||
Args::parse()
|
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`
|
/// Charge la configuration depuis `%APPDATA%/imagod-web/imagod.toml`
|
||||||
pub fn load() -> Result<Self> {
|
pub fn load() -> Result<Self> {
|
||||||
let path = Self::config_path()?;
|
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() {
|
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()))?;
|
.with_context(|| format!("Impossible de lire {}", path.display()))?;
|
||||||
let config: Config = toml::from_str(&content)
|
let config: Config = toml::from_str(&content)
|
||||||
.with_context(|| "Erreur de parsing du fichier de configuration")?;
|
.with_context(|| "Erreur de parsing du fichier de configuration")?;
|
||||||
Ok(config)
|
Ok(config)
|
||||||
} else {
|
} else {
|
||||||
let config = Config::default();
|
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)
|
Ok(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<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>">
|
<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>
|
<style>
|
||||||
* { margin:0; padding:0; box-sizing:border-box; }
|
* { 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>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<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>
|
<nav>
|
||||||
<a href="/">🖼️ Galerie</a>
|
<a href="/">🖼️ Galerie</a>
|
||||||
<a href="/changelog" class="active">📋 Changelog</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>
|
<h2>Légende des tags</h2>
|
||||||
|
|
||||||
</main>
|
</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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<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>">
|
<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>
|
<style>
|
||||||
* { margin:0; padding:0; box-sizing:border-box; }
|
* { 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>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<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>
|
<nav>
|
||||||
<a href="/">🖼️ Galerie</a>
|
<a href="/">🖼️ Galerie</a>
|
||||||
<a href="/changelog">📋 Changelog</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>
|
<p>5. <strong>Corriger le backend Imago</strong> (thumbnails, upload, stockage)</p>
|
||||||
|
|
||||||
</main>
|
</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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -465,7 +465,7 @@
|
|||||||
<button class="btn" id="clear-btn" style="display:none">✕</button>
|
<button class="btn" id="clear-btn" style="display:none">✕</button>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn accent" id="header-upload-btn" onclick="document.getElementById('drop-zone').scrollIntoView({behavior:'smooth'}); document.getElementById('file-input').click()">📤 Upload</button>
|
<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>
|
</div>
|
||||||
|
|
||||||
<!-- ─── Navigation ─── -->
|
<!-- ─── Navigation ─── -->
|
||||||
|
|||||||
+32
-21
@@ -21,13 +21,11 @@ mod watcher;
|
|||||||
mod clipboard;
|
mod clipboard;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
mod tray;
|
mod tray;
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
mod service;
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
mod window_info;
|
|
||||||
|
|
||||||
mod server;
|
mod server;
|
||||||
|
#[cfg(windows)]
|
||||||
|
mod service;
|
||||||
|
mod update;
|
||||||
|
mod window_info;
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process;
|
use std::process;
|
||||||
@@ -212,7 +210,7 @@ async fn cmd_start(port: u16, open_browser: bool) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// `serve` — Lance le dashboard web uniquement
|
/// `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
|
// Vérifier qu'il n'y a pas déjà une instance
|
||||||
if let Some(pid) = is_running() {
|
if let Some(pid) = is_running() {
|
||||||
anyhow::bail!(
|
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
|
// Écrire le PID
|
||||||
write_pid()?;
|
write_pid()?;
|
||||||
@@ -498,13 +501,12 @@ async fn main() {
|
|||||||
setup_logging();
|
setup_logging();
|
||||||
|
|
||||||
let args = cli::parse();
|
let args = cli::parse();
|
||||||
|
let result = match args {
|
||||||
let result = match args.command {
|
|
||||||
cli::Command::Start { port, open_browser } => {
|
cli::Command::Start { port, open_browser } => {
|
||||||
cmd_start(port, open_browser).await
|
cmd_start(port, open_browser).await
|
||||||
}
|
}
|
||||||
cli::Command::Serve { port, host, no_watcher, open_browser } => {
|
cli::Command::Serve { port, host, no_watcher, open_browser, config } => {
|
||||||
cmd_serve(&host, port, no_watcher, open_browser).await
|
cmd_serve(&host, port, no_watcher, open_browser, config).await
|
||||||
}
|
}
|
||||||
cli::Command::Stop => {
|
cli::Command::Stop => {
|
||||||
cmd_stop()
|
cmd_stop()
|
||||||
@@ -515,17 +517,16 @@ async fn main() {
|
|||||||
cli::Command::Config { action } => {
|
cli::Command::Config { action } => {
|
||||||
cmd_config(&action)
|
cmd_config(&action)
|
||||||
}
|
}
|
||||||
cli::Command::Logs => {
|
cli::Command::Install { config, api_key } => {
|
||||||
cmd_logs()
|
install_service(config, api_key)
|
||||||
}
|
|
||||||
cli::Command::Install => {
|
|
||||||
install_service()
|
|
||||||
}
|
}
|
||||||
cli::Command::Uninstall => {
|
cli::Command::Uninstall => {
|
||||||
uninstall_service()
|
uninstall_service()
|
||||||
}
|
}
|
||||||
|
cli::Command::Update { force, nexus_url } => {
|
||||||
|
update_binary(force, &nexus_url)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
eprintln!("❌ Erreur : {:#}", e);
|
eprintln!("❌ Erreur : {:#}", e);
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
@@ -533,13 +534,13 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[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...");
|
tracing::info!("Installation du service Windows...");
|
||||||
service::install()
|
service::install(config, api_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[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)")
|
anyhow::bail!("La commande 'install' n'est disponible que sur Windows (service NSSM)")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,6 +550,16 @@ fn uninstall_service() -> anyhow::Result<()> {
|
|||||||
service::uninstall()
|
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"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
fn uninstall_service() -> anyhow::Result<()> {
|
fn uninstall_service() -> anyhow::Result<()> {
|
||||||
anyhow::bail!("La commande 'uninstall' n'est disponible que sur Windows (service NSSM)")
|
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
|
//! 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
|
//! 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};
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
/// Trouve l'exécutable NSSM
|
/// Trouve l'exécutable NSSM
|
||||||
@@ -29,7 +30,7 @@ fn own_exe_path() -> anyhow::Result<String> {
|
|||||||
.to_string())
|
.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn install() -> anyhow::Result<()> {
|
pub fn install(config_override: Option<PathBuf>, api_key: Option<String>) -> anyhow::Result<()> {
|
||||||
if !cfg!(windows) {
|
if !cfg!(windows) {
|
||||||
anyhow::bail!("L'installation en service est disponible uniquement sous 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 exe_path = own_exe_path()?;
|
||||||
let service_name = "imagod-web";
|
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!("🔧 Installation du service Windows '{}'...", service_name);
|
||||||
println!(" Binaire : {}", exe_path);
|
println!(" Binaire : {}", exe_path);
|
||||||
println!(" NSSM : {}", nssm.display());
|
println!(" NSSM : {}", nssm.display());
|
||||||
@@ -70,9 +109,12 @@ pub fn install() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Configurer le service
|
// Configurer le service
|
||||||
|
let config_arg = format!("serve --config {}", config_path.display());
|
||||||
let settings: &[(&[&str], &str)] = &[
|
let settings: &[(&[&str], &str)] = &[
|
||||||
// Commande : "serve" (watcher + dashboard)
|
// Commande : "serve --config <path>"
|
||||||
(&["set", service_name, "AppParameters", "serve"], "Paramètres de l'application"),
|
(&["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
|
// Démarrage automatique
|
||||||
(&["set", service_name, "Start", "SERVICE_AUTO_START"], "Type de démarrage"),
|
(&["set", service_name, "Start", "SERVICE_AUTO_START"], "Type de démarrage"),
|
||||||
// Nom affiché
|
// 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