fix: déduplication SQLite AVANT upload — bloque tout doublon, même séquentiel

- Vérifie already_seen() dans la DB SQLite avant chaque upload vers Imago
- Si le hash existe déjà → retourne status=duplicate sans re-uploader
- Protège contre les doublons séquentiels (pas juste concurrents)
- Testé: 1er upload OK (ID 81), 2e upload même fichier → duplicate, 3e fichier différent → OK (ID 82)
This commit is contained in:
2026-06-25 14:35:24 -04:00
parent 5a2e12da01
commit 0309359231
4 changed files with 28 additions and 7 deletions
+3 -3
View File
@@ -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.22-3254c33-dirty</title>
<title>Changelog — imagod-web v0.2.0+build.23-5a2e12d-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.22-3254c33-dirty</span></h1>
<h1>📋 imagod-web <span class="version-badge">v0.2.0+build.23-5a2e12d-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.22-3254c33-dirty — <a href="https://git.dracodev.net/Projets/imagod-web">Gitea</a></footer>
<footer>imagod-web v0.2.0+build.23-5a2e12d-dirty — <a href="https://git.dracodev.net/Projets/imagod-web">Gitea</a></footer>
</body>
</html>
+3 -3
View File
@@ -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.22-3254c33-dirty</title>
<title>Aide & Roadmap — imagod-web v0.2.0+build.23-5a2e12d-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.22-3254c33-dirty</span></h1>
<h1>❓ imagod-web <span class="version-badge">v0.2.0+build.23-5a2e12d-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.22-3254c33-dirty — <a href="https://git.dracodev.net/Projets/imagod-web">Gitea</a></footer>
<footer>imagod-web v0.2.0+build.23-5a2e12d-dirty — <a href="https://git.dracodev.net/Projets/imagod-web">Gitea</a></footer>
</body>
</html>
+1 -1
View File
@@ -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.22-3254c33-dirty</span>
<span class="version-badge" id="version-badge">v0.2.0+build.23-5a2e12d-dirty</span>
</div>
<!-- ─── Navigation ─── -->
+21
View File
@@ -227,6 +227,27 @@ async fn upload_image(
// Calculer le hash AVANT de consommer `data` (upload_image prend ownership)
let file_hash = crate::dedup::Dedup::hash_bytes(&data);
// ═══ Déduplication SQLite : vérifier si ce hash a déjà été uploadé ═══
if let Ok(dedup) = crate::dedup::Dedup::open(&state.dedup_db_path, 1000) {
if dedup.already_seen(&file_hash).unwrap_or(false) {
tracing::info!(
"🔄 Fichier déjà connu — hash {}... ignoré (uploadeur : {})",
hex::encode(&file_hash[..8.min(file_hash.len())]),
filename
);
return (
axum::http::StatusCode::OK,
Json(serde_json::json!({
"id": null,
"uuid": null,
"filename": filename,
"status": "duplicate",
"message": "Image déjà existante (hash identique)"
})),
);
}
}
// ═══ Garde anti-doublon : vérifier qu'aucun upload concurrent n'est en cours pour ce hash ═══
{
let mut in_flight = state.in_flight.lock().await;