This commit is contained in:
2026-03-22 12:17:01 +01:00
parent 7afca6ed04
commit a30e83a724
210 changed files with 318 additions and 2 deletions

View File

@@ -8,9 +8,10 @@ from datetime import datetime, timezone
from pathlib import Path
from typing import Any
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile
from fastapi.responses import FileResponse
from starlette.background import BackgroundTask
from sqlalchemy import text
from sqlmodel import Session, select
from app.database import get_session
from app.models.settings import UserSettings
@@ -283,6 +284,185 @@ def download_backup_zip() -> FileResponse:
)
def _merge_db_add_only(backup_db_path: Path, current_db_path: Path) -> dict[str, int]:
"""Insère dans la BDD courante les lignes absentes de la BDD de sauvegarde (INSERT OR IGNORE)."""
import sqlite3
stats = {"rows_added": 0, "rows_skipped": 0}
backup_conn = sqlite3.connect(str(backup_db_path))
current_conn = sqlite3.connect(str(current_db_path))
current_conn.execute("PRAGMA foreign_keys=OFF")
try:
tables = backup_conn.execute(
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
).fetchall()
for (table,) in tables:
try:
cur = backup_conn.execute(f'SELECT * FROM "{table}"')
cols = [d[0] for d in cur.description]
rows = cur.fetchall()
if not rows:
continue
col_names = ", ".join(f'"{c}"' for c in cols)
placeholders = ", ".join(["?"] * len(cols))
before = current_conn.execute(f'SELECT COUNT(*) FROM "{table}"').fetchone()[0]
current_conn.executemany(
f'INSERT OR IGNORE INTO "{table}" ({col_names}) VALUES ({placeholders})',
rows,
)
after = current_conn.execute(f'SELECT COUNT(*) FROM "{table}"').fetchone()[0]
added = after - before
stats["rows_added"] += added
stats["rows_skipped"] += len(rows) - added
except Exception:
pass
current_conn.commit()
finally:
backup_conn.close()
current_conn.close()
return stats
@router.post("/settings/backup/restore")
async def restore_backup(
file: UploadFile = File(...),
overwrite: bool = Form(default=True),
) -> dict[str, Any]:
"""Restaure une sauvegarde ZIP (DB + uploads). overwrite=true écrase, false ajoute uniquement."""
import shutil
db_path = _resolve_sqlite_db_path()
uploads_dir = Path(UPLOAD_DIR).resolve()
data = await file.read()
if len(data) < 4 or data[:2] != b'PK':
raise HTTPException(400, "Le fichier n'est pas une archive ZIP valide.")
fd, tmp_zip_path = tempfile.mkstemp(suffix=".zip")
os.close(fd)
tmp_zip = Path(tmp_zip_path)
tmp_extract = Path(tempfile.mkdtemp(prefix="jardin_restore_"))
try:
tmp_zip.write_bytes(data)
with zipfile.ZipFile(tmp_zip, "r") as zipf:
zipf.extractall(str(tmp_extract))
stats: dict[str, Any] = {
"uploads_copies": 0,
"uploads_ignores": 0,
"db_restauree": False,
"db_lignes_ajoutees": 0,
"erreurs": 0,
}
# --- Uploads ---
backup_uploads = tmp_extract / "uploads"
if backup_uploads.is_dir():
uploads_dir.mkdir(parents=True, exist_ok=True)
for src in backup_uploads.rglob("*"):
if not src.is_file():
continue
dst = uploads_dir / src.relative_to(backup_uploads)
dst.parent.mkdir(parents=True, exist_ok=True)
if overwrite or not dst.exists():
try:
shutil.copy2(str(src), str(dst))
stats["uploads_copies"] += 1
except Exception:
stats["erreurs"] += 1
else:
stats["uploads_ignores"] += 1
# --- Base de données ---
backup_db_dir = tmp_extract / "db"
db_files = sorted(backup_db_dir.glob("*.db")) if backup_db_dir.is_dir() else []
if db_files and db_path:
backup_db_file = db_files[0]
if overwrite:
from app.database import engine
try:
with engine.connect() as conn:
conn.execute(text("PRAGMA wal_checkpoint(TRUNCATE)"))
except Exception:
pass
engine.dispose()
shutil.copy2(str(backup_db_file), str(db_path))
stats["db_restauree"] = True
else:
merge = _merge_db_add_only(backup_db_file, db_path)
stats["db_lignes_ajoutees"] = merge["rows_added"]
stats["db_restauree"] = True
return {"ok": True, **stats}
except HTTPException:
raise
except Exception as exc:
raise HTTPException(500, f"Erreur lors de la restauration : {exc}") from exc
finally:
_safe_remove(str(tmp_zip))
shutil.rmtree(str(tmp_extract), ignore_errors=True)
@router.post("/settings/images/resize-all")
def resize_all_images(session: Session = Depends(get_session)) -> dict[str, Any]:
"""Redimensionne les images pleine taille de la bibliothèque dont la largeur dépasse le paramètre configuré."""
from PIL import Image
import io as _io
setting = session.exec(select(UserSettings).where(UserSettings.cle == "image_max_width")).first()
max_px = 1200
if setting:
try:
max_px = int(setting.valeur)
except (ValueError, TypeError):
pass
if max_px <= 0:
return {"ok": True, "redimensionnees": 0, "ignorees": 0, "erreurs": 0,
"message": "Taille originale configurée — aucune modification."}
from app.models.media import Media as MediaModel
urls = session.exec(select(MediaModel.url)).all()
uploads_dir = Path(UPLOAD_DIR).resolve()
redimensionnees = 0
ignorees = 0
erreurs = 0
for url in urls:
if not url:
continue
# /uploads/filename.webp → data/uploads/filename.webp
filename = url.lstrip("/").removeprefix("uploads/")
file_path = uploads_dir / filename
if not file_path.is_file():
ignorees += 1
continue
try:
with Image.open(file_path) as img:
w, h = img.size
if w <= max_px and h <= max_px:
ignorees += 1
continue
img_copy = img.copy()
img_copy.thumbnail((max_px, max_px), Image.LANCZOS)
img_copy.save(file_path, "WEBP", quality=85)
redimensionnees += 1
except Exception:
erreurs += 1
return {"ok": True, "redimensionnees": redimensionnees, "ignorees": ignorees, "erreurs": erreurs}
@router.post("/settings/backup/samba")
def backup_to_samba(session: Session = Depends(get_session)) -> dict[str, Any]:
"""Envoie une sauvegarde ZIP vers un partage Samba/CIFS."""

Binary file not shown.

BIN
data/jardin.db-shm Executable file

Binary file not shown.

0
data/jardin.db-wal Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

0
data/uploads/01c05aed-27a4-4902-b99d-9a1a27957b91.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.0 MiB

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

0
data/uploads/0cf928a2-4e1e-4f80-b808-aa066ce3f174.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

0
data/uploads/119cd18a-50a6-40df-8c72-b5711c6ea7f2.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 2.1 MiB

0
data/uploads/14cb5845-82fb-412e-82e8-16eeed8e920b.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.1 MiB

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

0
data/uploads/185159c5-d396-4d1a-aa5b-9c2a6c6267d6.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 2.0 MiB

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

0
data/uploads/2749da16-ebef-4df0-b03f-0905468b1a08.webp Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 44 B

After

Width:  |  Height:  |  Size: 44 B

0
data/uploads/2826ce60-929c-447b-82cd-d95166d1a364.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

0
data/uploads/2e670eec-bf29-4c9e-8634-87dcea54274e.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

0
data/uploads/456fc305-c22b-4a69-bcb5-bc418ed7d982.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

0
data/uploads/4b00ab47-1577-476b-9e83-cdc3cc96af0c.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.2 MiB

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

0
data/uploads/53ac30ee-108e-42f2-b61b-cd810dcbeeb5.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.8 MiB

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

0
data/uploads/72bc5fc7-a3eb-49e9-8bb1-4e0f54d5e4c2.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 2.7 MiB

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

0
data/uploads/7f71435a-a28c-4ff1-91f9-fbf0a442b6b0.webp Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 44 B

After

Width:  |  Height:  |  Size: 44 B

0
data/uploads/8913cdc5-363a-4b00-8af0-1fc62eede9f6.webp Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 44 B

After

Width:  |  Height:  |  Size: 44 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

0
data/uploads/8a7d347c-b3b1-42c5-b1ab-8699ee52e87d.webp Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 44 B

After

Width:  |  Height:  |  Size: 44 B

0
data/uploads/8c108c64-f513-427b-bd16-a7b8fb99245e.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.4 MiB

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

0
data/uploads/92819523-a2dc-476f-8770-f8615d4a5cfe.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.2 MiB

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

0
data/uploads/945c2aa1-896d-4979-bb3c-2a74571b3050.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.4 MiB

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

0
data/uploads/a00759fa-ecee-472e-bfa6-a9b6f90d4c84.webp Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 44 B

After

Width:  |  Height:  |  Size: 44 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

0
data/uploads/a8641f29-f7ab-465c-a739-a1a3367f6628.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.0 MiB

After

Width:  |  Height:  |  Size: 3.0 MiB

0
data/uploads/a9ebb40f-5402-4409-9d0a-5de42eacfba6.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 4.8 MiB

After

Width:  |  Height:  |  Size: 4.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

0
data/uploads/accdf642-36b5-4d9c-b9be-465ea65afb17.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.3 MiB

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

0
data/uploads/b2b098f7-1c09-410c-a440-e95de836bd5f.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.7 MiB

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

0
data/uploads/b5320f20-2d59-47e2-9c92-b48878890bb9.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.5 MiB

After

Width:  |  Height:  |  Size: 3.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

0
data/uploads/bc3fe54e-b894-4ca8-8be8-1b065afaf918.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 2.4 MiB

After

Width:  |  Height:  |  Size: 2.4 MiB

0
data/uploads/be350e09-2e7e-4c22-ab3e-c2f248a750cd.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 2.8 MiB

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

0
data/uploads/bfb03b30-ef05-4710-ab46-0c4cde2f7366.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.1 MiB

After

Width:  |  Height:  |  Size: 3.1 MiB

0
data/uploads/bfdf7863-16e5-4151-95aa-087d0aa920bd.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

0
data/uploads/c1414164-b276-43c5-ab08-229baef76c2b.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 2.9 MiB

After

Width:  |  Height:  |  Size: 2.9 MiB

0
data/uploads/c14d8348-815f-4ebf-9ea1-bef89d122324.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.3 MiB

After

Width:  |  Height:  |  Size: 3.3 MiB

0
data/uploads/c1e27615-b167-4513-910b-e3fa2017fe42.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 4.3 MiB

After

Width:  |  Height:  |  Size: 4.3 MiB

0
data/uploads/c2d2c0bc-3994-431a-8e33-2a8e42903040.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.2 MiB

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Some files were not shown because too many files have changed in this diff Show More