diff --git a/web/backend/routers/appearances.py b/web/backend/routers/appearances.py index 027f21b..9c52cf6 100644 --- a/web/backend/routers/appearances.py +++ b/web/backend/routers/appearances.py @@ -1615,9 +1615,9 @@ def send_appearance_notification(notifier: PushoverNotifier, celebrity_name: str # TMDb image URL format poster_url = f"https://image.tmdb.org/t/p/w500{poster_url_path}" - # Download poster to temp file - import asyncio - response = asyncio.run(http_client.get(poster_url)) + # Download poster to temp file (sync HTTP call since this runs in background thread) + import httpx + response = httpx.get(poster_url, timeout=10.0) if response.status_code == 200: # Create temp file - strip query params before getting extension diff --git a/web/backend/routers/cloud_backup.py b/web/backend/routers/cloud_backup.py index 6b25234..391069c 100644 --- a/web/backend/routers/cloud_backup.py +++ b/web/backend/routers/cloud_backup.py @@ -25,7 +25,7 @@ from pydantic import BaseModel, Field from slowapi import Limiter from slowapi.util import get_remote_address -from ..core.dependencies import get_current_user, get_app_state +from ..core.dependencies import get_current_user, require_admin, get_app_state from modules.universal_logger import get_logger logger = get_logger('CloudBackup') @@ -837,7 +837,7 @@ async def get_config(user=Depends(get_current_user)): @router.put("/config") -async def update_config(update: CloudBackupConfigUpdate, user=Depends(get_current_user)): +async def update_config(update: CloudBackupConfigUpdate, user=Depends(require_admin)): """Save cloud backup configuration and regenerate rclone.conf.""" existing = _load_config() update_dict = update.model_dump(exclude_unset=True) diff --git a/web/backend/routers/maintenance.py b/web/backend/routers/maintenance.py index 92b0810..93b721a 100644 --- a/web/backend/routers/maintenance.py +++ b/web/backend/routers/maintenance.py @@ -15,7 +15,7 @@ from fastapi import APIRouter, Depends, Request, BackgroundTasks from slowapi import Limiter from slowapi.util import get_remote_address -from ..core.dependencies import get_current_user, get_app_state +from ..core.dependencies import get_current_user, require_admin, get_app_state from ..core.config import settings from ..core.responses import now_iso8601 from ..core.exceptions import handle_exceptions @@ -63,7 +63,7 @@ async def cleanup_missing_files( request: Request, background_tasks: BackgroundTasks, dry_run: bool = True, - current_user: Dict = Depends(get_current_user) + current_user: Dict = Depends(require_admin) ): """ Scan all database tables for file references and remove entries for missing files. diff --git a/web/backend/routers/press.py b/web/backend/routers/press.py index 389ec12..0126d68 100644 --- a/web/backend/routers/press.py +++ b/web/backend/routers/press.py @@ -1075,7 +1075,7 @@ def cache_press_image(image_url: str, use_flaresolverr: bool = False) -> Optiona @router.get("/images/{filename}") -async def serve_press_image(filename: str): +async def serve_press_image(filename: str, current_user: Dict = Depends(get_current_user)): """Serve a cached press article image.""" # Sanitize filename if '/' in filename or '..' in filename: diff --git a/web/backend/routers/private_gallery.py b/web/backend/routers/private_gallery.py index 0b9f5eb..9852a6e 100644 --- a/web/backend/routers/private_gallery.py +++ b/web/backend/routers/private_gallery.py @@ -21,6 +21,7 @@ import json import mimetypes import os import re +import secrets import shutil import tempfile import time @@ -6968,7 +6969,7 @@ async def migrate_to_chunked( # Run migration in background thread job_id = f"pg_migrate_{secrets.token_hex(6)}" - _update_pg_job(job_id, { + _create_pg_job(job_id, { 'status': 'running', 'total_files': len(to_migrate), 'processed_files': 0, diff --git a/web/backend/routers/recycle.py b/web/backend/routers/recycle.py index fd2a124..f799e94 100644 --- a/web/backend/routers/recycle.py +++ b/web/backend/routers/recycle.py @@ -490,7 +490,7 @@ def _get_or_create_thumbnail(file_path: Path, media_type: str, content_hash: str from datetime import datetime try: - with sqlite3.connect('thumbnails', timeout=30.0) as conn: + with sqlite3.connect(str(settings.PROJECT_ROOT / 'database' / 'thumbnails.db'), timeout=30.0) as conn: cursor = conn.cursor() # 1. Try content hash first (new method - survives file moves) @@ -546,7 +546,7 @@ def _get_or_create_thumbnail(file_path: Path, media_type: str, content_hash: str file_mtime = file_path.stat().st_mtime if file_path.exists() else None # Compute file_hash if not provided thumb_file_hash = content_hash if content_hash else hashlib.sha256(str(file_path).encode()).hexdigest() - with sqlite3.connect('thumbnails') as conn: + with sqlite3.connect(str(settings.PROJECT_ROOT / 'database' / 'thumbnails.db')) as conn: conn.execute(""" INSERT OR REPLACE INTO thumbnails (file_hash, file_path, thumbnail_data, created_at, file_mtime) diff --git a/web/backend/routers/scheduler.py b/web/backend/routers/scheduler.py index 2a47ed8..f978f88 100644 --- a/web/backend/routers/scheduler.py +++ b/web/backend/routers/scheduler.py @@ -91,7 +91,7 @@ async def get_scheduler_status( if forum_cfg.get('enabled', False): enabled_forums.add(forum_cfg.get('name')) - with sqlite3.connect('scheduler_state') as sched_conn: + with sqlite3.connect(str(settings.PROJECT_ROOT / 'database' / 'scheduler_state.db')) as sched_conn: cursor = sched_conn.cursor() # Get all tasks @@ -332,7 +332,7 @@ async def pause_scheduler_task( """Pause a specific scheduler task.""" app_state = get_app_state() - with sqlite3.connect('scheduler_state') as sched_conn: + with sqlite3.connect(str(settings.PROJECT_ROOT / 'database' / 'scheduler_state.db')) as sched_conn: cursor = sched_conn.cursor() cursor.execute(""" @@ -372,7 +372,7 @@ async def resume_scheduler_task( """Resume a paused scheduler task.""" app_state = get_app_state() - with sqlite3.connect('scheduler_state') as sched_conn: + with sqlite3.connect(str(settings.PROJECT_ROOT / 'database' / 'scheduler_state.db')) as sched_conn: cursor = sched_conn.cursor() cursor.execute(""" @@ -412,7 +412,7 @@ async def skip_next_run( """Skip the next scheduled run by advancing next_run time.""" app_state = get_app_state() - with sqlite3.connect('scheduler_state') as sched_conn: + with sqlite3.connect(str(settings.PROJECT_ROOT / 'database' / 'scheduler_state.db')) as sched_conn: cursor = sched_conn.cursor() # Get current task info @@ -480,7 +480,7 @@ async def reschedule_task( except ValueError: raise HTTPException(status_code=400, detail="Invalid datetime format") - with sqlite3.connect('scheduler_state') as sched_conn: + with sqlite3.connect(str(settings.PROJECT_ROOT / 'database' / 'scheduler_state.db')) as sched_conn: cursor = sched_conn.cursor() cursor.execute( "UPDATE scheduler_state SET next_run = ? WHERE task_id = ?", diff --git a/web/backend/routers/stats.py b/web/backend/routers/stats.py index b0ec799..f62536c 100644 --- a/web/backend/routers/stats.py +++ b/web/backend/routers/stats.py @@ -396,7 +396,7 @@ async def update_setting( request: Request, key: str, body: Dict, - current_user: Dict = Depends(get_current_user) + current_user: Dict = Depends(require_admin) ): """Update a specific setting value.""" app_state = get_app_state()