chore: initialiser le projet imagod-web (Rust)

- Structure du projet Cargo avec dépendances (clap, axum, reqwest, rusqlite, arboard, tray-icon)
- CLI avec sous-commandes start, serve, stop, status, config, install, uninstall, logs
- Module config: parsing TOML, chemins Windows/Linux
- Module dedup: dédoublonnage SHA-256 avec cache SQLite
- Module imago_client: client HTTP pour API Imago (upload, search, get)
- Module watcher: boucle de polling stub
- Module server: serveur web axum avec routes API REST stub
- Platform-specific: clipboard, tray, service, window_info (Windows)
- Frontend placeholder HTML
- Config par défaut (imagod.toml)
- README.md, LICENSE (MIT), .gitignore
This commit is contained in:
2026-06-22 16:44:47 -04:00
commit a3b853de04
19 changed files with 923 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
//! Gestion de la configuration TOML
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
/// Configuration complète d'imagod-web
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
#[serde(default)]
pub imago: ImagoConfig,
#[serde(default)]
pub watcher: WatcherConfig,
#[serde(default)]
pub dedup: DedupConfig,
#[serde(default)]
pub ui: UiConfig,
#[serde(default)]
pub logging: LoggingConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImagoConfig {
pub url: String,
pub api_key: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WatcherConfig {
pub capture_images: bool,
pub poll_interval_ms: u64,
pub filters: WatcherFilters,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WatcherFilters {
pub only_apps: Vec<String>,
pub exclude_apps: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DedupConfig {
pub method: String,
pub cache_size: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiConfig {
pub show_notifications: bool,
pub show_tray_icon: bool,
pub notification_timeout_sec: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoggingConfig {
pub level: String,
}
impl Default for Config {
fn default() -> Self {
Self {
imago: ImagoConfig {
url: "http://imago.dracodev.net:8000".into(),
api_key: String::new(),
},
watcher: WatcherConfig {
capture_images: true,
poll_interval_ms: 500,
filters: WatcherFilters {
only_apps: vec![],
exclude_apps: vec!["mstsc".into()],
},
},
dedup: DedupConfig {
method: "sha256".into(),
cache_size: 10000,
},
ui: UiConfig {
show_notifications: true,
show_tray_icon: true,
notification_timeout_sec: 3,
},
logging: LoggingConfig {
level: "info".into(),
},
}
}
}
impl Config {
/// Charge la configuration depuis `%APPDATA%/imagod-web/imagod.toml`
pub fn load() -> Result<Self> {
let path = Self::config_path()?;
if path.exists() {
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 {
// Premier lancement — créer la config par défaut
let config = Config::default();
config.save()?;
Ok(config)
}
}
/// Sauvegarde la configuration
pub fn save(&self) -> Result<()> {
let path = Self::config_path()?;
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
let content = toml::to_string_pretty(self)?;
std::fs::write(&path, content)?;
Ok(())
}
/// Chemin du fichier de configuration
fn config_path() -> Result<PathBuf> {
let base = if cfg!(target_os = "windows") {
let appdata = std::env::var("APPDATA")
.context("Variable APPDATA non définie")?;
PathBuf::from(appdata)
} else {
let home = std::env::var("HOME")
.context("Variable HOME non définie")?;
PathBuf::from(home).join(".config")
};
Ok(base.join("imagod-web").join("imagod.toml"))
}
}