diff --git a/app/app.py b/app/app.py index 3c24a6c..bcf98f6 100644 --- a/app/app.py +++ b/app/app.py @@ -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")) diff --git a/app/static/app.css b/app/static/app.css index db1531c..d888214 100644 --- a/app/static/app.css +++ b/app/static/app.css @@ -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%; } diff --git a/app/templates/browse.html b/app/templates/browse.html index f30888f..71210ef 100644 --- a/app/templates/browse.html +++ b/app/templates/browse.html @@ -120,6 +120,15 @@ +