feat: gestion des conflits lors de l'upload de fichiers

Ajoute un sélecteur de stratégie dans le formulaire de dépôt CDN,
identique à celui du redimensionnement :
- Écraser (défaut) : comportement précédent, écrase silencieusement
- Backup : renomme l'existant en {stem}_bak_{timestamp}{ext} avant dépôt
- Renommer : auto-incrémente le nom du fichier uploadé ({stem}_1, _2…)
- Ignorer : ne dépose pas si le fichier existe déjà

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Alpinux 2026-05-06 09:29:29 +02:00
parent 14259c59f1
commit 130a901be7
3 changed files with 26 additions and 1 deletions

View file

@ -554,11 +554,26 @@ def upload_file():
if not dest.is_dir():
abort(400)
conflict = request.form.get("conflict", "overwrite")
if conflict not in ("backup", "overwrite", "rename", "skip"):
conflict = "overwrite"
for f in files:
name = secure_filename(f.filename or "")
if not name:
continue
f.save(dest / name)
out_path = dest / name
if out_path.exists():
if conflict == "skip":
continue
elif conflict == "backup":
try:
out_path.rename(_backup_path(out_path))
except Exception:
continue
elif conflict == "rename":
out_path = _auto_rename(out_path)
f.save(out_path)
return redirect(url_for("browse", subpath=subpath) if subpath else url_for("browse"))

View file

@ -165,6 +165,7 @@ main { max-width: 1100px; margin: 2rem auto; padding: 0 1.5rem 3rem; display: fl
/* ── Upload ───────────────────────────────────────────────────────── */
.upload-card h2 { margin-bottom: .8rem; }
.upload-conflict { display: flex; align-items: center; gap: .8rem; flex-wrap: wrap; margin-bottom: .8rem; }
.drop-zone { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .5rem; border: 2px dashed var(--border); border-radius: var(--radius); padding: 2rem 1.5rem; cursor: pointer; background: var(--bg); transition: border-color .15s, background .15s; text-align: center; position: relative; margin-bottom: 1rem; }
.drop-zone:hover, .drop-zone:focus-within { border-color: var(--blue); background: var(--blue-light); }
.drop-zone input[type=file] { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%; }

View file

@ -120,6 +120,15 @@
<span class="drop-names" id="drop-names"></span>
<input type="file" name="files" id="upload-input" multiple>
</label>
<div class="upload-conflict">
<span class="resize-group-label">Si le fichier existe déjà</span>
<div class="resize-chips resize-chips--radio">
<label class="chip"><input type="radio" name="conflict" value="overwrite" checked><span>Écraser</span></label>
<label class="chip"><input type="radio" name="conflict" value="backup"><span>Backup</span></label>
<label class="chip"><input type="radio" name="conflict" value="rename"><span>Renommer</span></label>
<label class="chip"><input type="radio" name="conflict" value="skip"><span>Ignorer</span></label>
</div>
</div>
<button type="submit" class="btn btn-primary" id="upload-btn" disabled>Envoyer</button>
</form>
<script>