From 352faf404ce6ae2e318106d0a7208137cb72049d Mon Sep 17 00:00:00 2001 From: dekun Date: Sun, 2 Aug 2026 09:23:08 +0800 Subject: [PATCH] feat: match settings card width to ops map and add backup download Co-authored-by: Cursor --- apps/api/routes/settings.py | 17 ++++++++++++++ web/dist/index.html | 38 ++++++++++++++++++++++++++++---- web/src/api/client.ts | 20 +++++++++++++++++ web/src/components/LoginGate.tsx | 2 +- web/src/pages/Settings.tsx | 35 ++++++++++++++++++++++++----- web/src/styles.css | 4 +++- 6 files changed, 105 insertions(+), 11 deletions(-) diff --git a/apps/api/routes/settings.py b/apps/api/routes/settings.py index 0260f59..9e64170 100644 --- a/apps/api/routes/settings.py +++ b/apps/api/routes/settings.py @@ -6,6 +6,7 @@ import re import time from fastapi import APIRouter, Depends, HTTPException +from fastapi.responses import FileResponse from pydantic import BaseModel, Field from apps.api.auth import auth_disabled, require_auth, verify_credentials @@ -150,6 +151,22 @@ def create_backup(body: BackupActionBody) -> dict: } +@router.get("/backup/download/{backup_name}") +def download_backup(backup_name: str) -> FileResponse: + s = get_settings() + try: + path = resolve_backup_file(s.backup_dir_path, backup_name.strip()) + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) from e + except FileNotFoundError as e: + raise HTTPException(status_code=404, detail=str(e)) from e + return FileResponse( + path=str(path), + filename=path.name, + media_type="application/octet-stream", + ) + + @router.post("/restore") def restore_backup(body: RestoreBody) -> dict: _verify_password_or_401(body.current_password) diff --git a/web/dist/index.html b/web/dist/index.html index ac79b9e..b009468 100644 --- a/web/dist/index.html +++ b/web/dist/index.html @@ -41,7 +41,9 @@ .hidden { display: none; } pre { background: var(--panel); border: 1px solid #243041; border-radius: 10px; padding: 1rem; overflow: auto; font-size: 0.78rem; color: #b7c5d4; } .center-page { display: flex; justify-content: center; align-items: flex-start; min-height: 50vh; padding: 1.5rem 0 3rem; } - .settings-card { width: 100%; max-width: 460px; } + .settings-card { width: 100%; } + .login-card { width: 100%; max-width: 420px; } + .settings-form { max-width: 520px; } .settings-title { font-size: 1.15rem; font-weight: 650; margin: 0.25rem 0 0.85rem; } .settings-hint { color: var(--muted); font-size: 0.85rem; line-height: 1.5; margin: 0 0 1rem; } .settings-tabs { display: flex; border-bottom: 1px solid #243041; margin: -0.25rem -0.15rem 1.1rem; } @@ -76,7 +78,7 @@