profile id support in api requests
This commit is contained in:
@@ -23,7 +23,7 @@ from flask import Blueprint, jsonify, request, abort, send_file, redirect, Respo
|
||||
from ..config import DB_PATH, JOBS_RETENTION_DAYS, SMART_QUEUE_HISTORY_RETENTION_DAYS, LOG_RETENTION_DAYS, WORKERS, PYTORRENT_TMP_DIR
|
||||
from ..db import connect, utcnow
|
||||
from ..services.auth import current_user_id as default_user_id, current_user, list_users, save_user, delete_user, login_user, logout_user, enabled as auth_enabled, require_profile_write, require_admin, is_admin
|
||||
from ..services import preferences, rtorrent, torrent_stats, speed_peaks, tracker_cache, rss as rss_service, ratio_rules, backup as backup_service, download_planner, operation_logs, poller_control, database_maintenance
|
||||
from ..services import auth, preferences, rtorrent, torrent_stats, speed_peaks, tracker_cache, rss as rss_service, ratio_rules, backup as backup_service, download_planner, operation_logs, poller_control, database_maintenance
|
||||
from ..services.torrent_cache import torrent_cache
|
||||
from ..services.torrent_summary import cached_summary
|
||||
from ..services.workers import enqueue, list_jobs, cancel_job, retry_job, force_job, clear_jobs, emergency_clear_jobs
|
||||
@@ -39,6 +39,78 @@ from .auth_api import register_auth_routes
|
||||
register_auth_routes(bp)
|
||||
|
||||
|
||||
|
||||
|
||||
def _request_profile_selector() -> tuple[int | None, str]:
|
||||
"""Return the optional profile selector supplied by external API clients."""
|
||||
payload = {}
|
||||
if request.method in {"POST", "PUT", "PATCH", "DELETE"}:
|
||||
try:
|
||||
payload = request.get_json(silent=True) or {}
|
||||
except Exception:
|
||||
payload = {}
|
||||
profile_id = request.args.get("profile_id") or request.form.get("profile_id") or payload.get("profile_id") or request.headers.get("X-PyTorrent-Profile-Id")
|
||||
profile_name = request.args.get("profile_name") or request.form.get("profile_name") or payload.get("profile_name") or request.headers.get("X-PyTorrent-Profile-Name") or ""
|
||||
try:
|
||||
return (int(profile_id), "") if profile_id not in (None, "") else (None, str(profile_name or "").strip())
|
||||
except (TypeError, ValueError):
|
||||
raise ValueError("profile_id must be an integer")
|
||||
|
||||
|
||||
def _profile_by_name(profile_name: str, user_id: int | None = None):
|
||||
name = str(profile_name or "").strip()
|
||||
if not name:
|
||||
return None
|
||||
user_id = user_id or default_user_id()
|
||||
visible = auth.visible_profile_ids(user_id)
|
||||
with connect() as conn:
|
||||
if visible is None:
|
||||
return conn.execute(
|
||||
"SELECT * FROM rtorrent_profiles WHERE lower(name)=lower(?) ORDER BY is_default DESC, id LIMIT 1",
|
||||
(name,),
|
||||
).fetchone()
|
||||
if not visible:
|
||||
return None
|
||||
placeholders = ",".join("?" for _ in visible)
|
||||
return conn.execute(
|
||||
f"SELECT * FROM rtorrent_profiles WHERE id IN ({placeholders}) AND lower(name)=lower(?) ORDER BY is_default DESC, id LIMIT 1",
|
||||
(*tuple(visible), name),
|
||||
).fetchone()
|
||||
|
||||
|
||||
def request_profile(require_write: bool = False):
|
||||
"""Resolve API profile context from profile_id/profile_name, then active profile for compatibility."""
|
||||
try:
|
||||
profile_id, profile_name = _request_profile_selector()
|
||||
except ValueError:
|
||||
raise
|
||||
user_id = default_user_id()
|
||||
profile = None
|
||||
if profile_id:
|
||||
profile = preferences.get_profile(int(profile_id), user_id)
|
||||
elif profile_name:
|
||||
profile = _profile_by_name(profile_name, user_id)
|
||||
else:
|
||||
profile = preferences.active_profile(user_id)
|
||||
if not profile and auth.can_access_profile(1, user_id):
|
||||
profile = preferences.get_profile(1, user_id)
|
||||
if not profile and (profile_id or profile_name):
|
||||
abort(404)
|
||||
if not profile:
|
||||
return None
|
||||
pid = int(profile["id"])
|
||||
if require_write and not auth.can_write_profile(pid, user_id):
|
||||
abort(403)
|
||||
if not require_write and not auth.can_access_profile(pid, user_id):
|
||||
abort(403)
|
||||
return profile
|
||||
|
||||
|
||||
def request_profile_id(require_write: bool = False) -> int | None:
|
||||
profile = request_profile(require_write=require_write)
|
||||
return int(profile["id"]) if profile else None
|
||||
|
||||
|
||||
def _job_profile_id(job_id: str) -> int | None:
|
||||
with connect() as conn:
|
||||
row = conn.execute("SELECT profile_id FROM jobs WHERE id=?", (job_id,)).fetchone()
|
||||
|
||||
Reference in New Issue
Block a user