add auth support

This commit is contained in:
Mateusz Gruszczyński
2026-05-06 08:38:07 +02:00
parent aea3c92830
commit dc1cac4e6f
20 changed files with 1185 additions and 220 deletions

View File

@@ -3,6 +3,7 @@ from __future__ import annotations
import json
from ..db import connect, utcnow, default_user_id
from . import auth
BOOTSTRAP_THEMES = {
"default": "Default Bootstrap",
@@ -34,43 +35,44 @@ def bootstrap_css_url(theme: str | None) -> str:
def list_profiles(user_id: int | None = None):
user_id = user_id or default_user_id()
user_id = user_id or auth.current_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 ORDER BY is_default DESC, name COLLATE NOCASE"
).fetchall()
if not visible:
return []
placeholders = ",".join("?" for _ in visible)
return conn.execute(
"SELECT * FROM rtorrent_profiles WHERE user_id=? ORDER BY is_default DESC, name COLLATE NOCASE",
(user_id,),
f"SELECT * FROM rtorrent_profiles WHERE id IN ({placeholders}) ORDER BY is_default DESC, name COLLATE NOCASE",
tuple(visible),
).fetchall()
def get_profile(profile_id: int, user_id: int | None = None):
user_id = user_id or default_user_id()
user_id = user_id or auth.current_user_id() or default_user_id()
if not auth.can_access_profile(profile_id, user_id):
return None
with connect() as conn:
return conn.execute(
"SELECT * FROM rtorrent_profiles WHERE id=? AND user_id=?",
(profile_id, user_id),
).fetchone()
return conn.execute("SELECT * FROM rtorrent_profiles WHERE id=?", (profile_id,)).fetchone()
def active_profile(user_id: int | None = None):
user_id = user_id or default_user_id()
user_id = user_id or auth.current_user_id() or default_user_id()
with connect() as conn:
pref = conn.execute("SELECT active_rtorrent_id FROM user_preferences WHERE user_id=?", (user_id,)).fetchone()
if pref and pref.get("active_rtorrent_id"):
row = conn.execute(
"SELECT * FROM rtorrent_profiles WHERE id=? AND user_id=?",
(pref["active_rtorrent_id"], user_id),
).fetchone()
if pref and pref.get("active_rtorrent_id") and auth.can_access_profile(int(pref["active_rtorrent_id"]), user_id):
row = conn.execute("SELECT * FROM rtorrent_profiles WHERE id=?", (pref["active_rtorrent_id"],)).fetchone()
if row:
return row
row = conn.execute(
"SELECT * FROM rtorrent_profiles WHERE user_id=? ORDER BY is_default DESC, id ASC LIMIT 1",
(user_id,),
).fetchone()
return row
profiles = list_profiles(user_id)
return profiles[0] if profiles else None
def save_profile(data: dict, user_id: int | None = None):
user_id = user_id or default_user_id()
user_id = user_id or auth.current_user_id() or default_user_id()
now = utcnow()
name = str(data.get("name") or "rTorrent").strip()
scgi_url = str(data.get("scgi_url") or "").strip()
@@ -79,7 +81,7 @@ def save_profile(data: dict, user_id: int | None = None):
is_remote = 1 if data.get("is_remote") else 0
is_default = 1 if data.get("is_default") else 0
if not scgi_url.startswith("scgi://"):
raise ValueError("SCGI URL musi zaczynać się od scgi://")
raise ValueError("SCGI URL must start with scgi://")
with connect() as conn:
if is_default:
conn.execute("UPDATE rtorrent_profiles SET is_default=0 WHERE user_id=?", (user_id,))
@@ -94,11 +96,11 @@ def save_profile(data: dict, user_id: int | None = None):
"UPDATE user_preferences SET active_rtorrent_id=?, updated_at=? WHERE user_id=?",
(profile_id, now, user_id),
)
return conn.execute("SELECT * FROM rtorrent_profiles WHERE id=? AND user_id=?", (profile_id, user_id)).fetchone()
return conn.execute("SELECT * FROM rtorrent_profiles WHERE id=?", (profile_id,)).fetchone()
def update_profile(profile_id: int, data: dict, user_id: int | None = None):
user_id = user_id or default_user_id()
user_id = user_id or auth.current_user_id() or default_user_id()
now = utcnow()
name = str(data.get("name") or "rTorrent").strip()
scgi_url = str(data.get("scgi_url") or "").strip()
@@ -107,24 +109,25 @@ def update_profile(profile_id: int, data: dict, user_id: int | None = None):
is_remote = 1 if data.get("is_remote") else 0
is_default = 1 if data.get("is_default") else 0
if not scgi_url.startswith("scgi://"):
raise ValueError("SCGI URL musi zaczynać się od scgi://")
raise ValueError("SCGI URL must start with scgi://")
with connect() as conn:
row = conn.execute("SELECT id FROM rtorrent_profiles WHERE id=? AND user_id=?", (profile_id, user_id)).fetchone()
if not row:
row = conn.execute("SELECT id FROM rtorrent_profiles WHERE id=?", (profile_id,)).fetchone()
if not row or not auth.can_write_profile(profile_id, user_id):
raise ValueError("Profil nie istnieje")
if is_default:
conn.execute("UPDATE rtorrent_profiles SET is_default=0 WHERE user_id=?", (user_id,))
conn.execute(
"UPDATE rtorrent_profiles SET name=?, scgi_url=?, is_default=?, timeout_seconds=?, max_parallel_jobs=?, is_remote=?, updated_at=? WHERE id=? AND user_id=?",
(name, scgi_url, is_default, timeout, max_parallel, is_remote, now, profile_id, user_id),
"UPDATE rtorrent_profiles SET name=?, scgi_url=?, is_default=?, timeout_seconds=?, max_parallel_jobs=?, is_remote=?, updated_at=? WHERE id=?",
(name, scgi_url, is_default, timeout, max_parallel, is_remote, now, profile_id),
)
return conn.execute("SELECT * FROM rtorrent_profiles WHERE id=? AND user_id=?", (profile_id, user_id)).fetchone()
return conn.execute("SELECT * FROM rtorrent_profiles WHERE id=?", (profile_id,)).fetchone()
def delete_profile(profile_id: int, user_id: int | None = None):
user_id = user_id or default_user_id()
user_id = user_id or auth.current_user_id() or default_user_id()
auth.require_profile_write(profile_id)
with connect() as conn:
conn.execute("DELETE FROM rtorrent_profiles WHERE id=? AND user_id=?", (profile_id, user_id))
conn.execute("DELETE FROM rtorrent_profiles WHERE id=?", (profile_id,))
active = active_profile(user_id)
conn.execute(
"UPDATE user_preferences SET active_rtorrent_id=?, updated_at=? WHERE user_id=?",
@@ -133,10 +136,10 @@ def delete_profile(profile_id: int, user_id: int | None = None):
def activate_profile(profile_id: int, user_id: int | None = None):
user_id = user_id or default_user_id()
user_id = user_id or auth.current_user_id() or default_user_id()
with connect() as conn:
row = conn.execute("SELECT id FROM rtorrent_profiles WHERE id=? AND user_id=?", (profile_id, user_id)).fetchone()
if not row:
row = conn.execute("SELECT id FROM rtorrent_profiles WHERE id=?", (profile_id,)).fetchone()
if not row or not auth.can_access_profile(profile_id, user_id):
raise ValueError("Profil nie istnieje")
conn.execute(
"UPDATE user_preferences SET active_rtorrent_id=?, updated_at=? WHERE user_id=?",
@@ -146,13 +149,18 @@ def activate_profile(profile_id: int, user_id: int | None = None):
def get_preferences(user_id: int | None = None):
user_id = user_id or default_user_id()
user_id = user_id or auth.current_user_id() or default_user_id()
with connect() as conn:
return conn.execute("SELECT * FROM user_preferences WHERE user_id=?", (user_id,)).fetchone()
pref = conn.execute("SELECT * FROM user_preferences WHERE user_id=?", (user_id,)).fetchone()
if not pref:
now = utcnow()
conn.execute("INSERT INTO user_preferences(user_id, theme, created_at, updated_at) VALUES(?, 'dark', ?, ?)", (user_id, now, now))
pref = conn.execute("SELECT * FROM user_preferences WHERE user_id=?", (user_id,)).fetchone()
return pref
def save_preferences(data: dict, user_id: int | None = None):
user_id = user_id or default_user_id()
user_id = user_id or auth.current_user_id() or default_user_id()
allowed_theme = data.get("theme") if data.get("theme") in {"light", "dark"} else None
bootstrap_theme = data.get("bootstrap_theme") if data.get("bootstrap_theme") in BOOTSTRAP_THEMES else None
font_family = data.get("font_family") if data.get("font_family") in FONT_FAMILIES else None