fix user_id on bypass

This commit is contained in:
Mateusz Gruszczyński
2026-06-07 21:08:00 +02:00
parent d00eb6ee2b
commit 30f3f97f56
3 changed files with 48 additions and 24 deletions
+24 -9
View File
@@ -3,7 +3,7 @@ from datetime import datetime, timezone
from typing import Any
import json
from ..db import connect, default_user_id, utcnow
from . import rtorrent
from . import rtorrent, auth
from .preferences import active_profile
from .workers import enqueue
@@ -11,6 +11,21 @@ AUTOMATION_JOB_CHUNK_SIZE = 100
AUTOMATION_LIGHT_ACTIONS = {'start', 'stop', 'pause', 'resume', 'set_label'}
def _resolve_user_id(profile: dict[str, Any] | None = None, user_id: int | None = None) -> int:
"""Return the user id that should own automation reads, jobs and history.
Note: Request-bound calls must keep the authenticated/bypass user, while
background poller calls can safely fall back to the profile owner.
"""
if user_id:
return int(user_id)
request_user_id = auth.current_user_id()
if request_user_id:
return int(request_user_id)
if profile and profile.get('user_id'):
return int(profile.get('user_id') or 0)
return int(default_user_id())
def _loads(value: str | None, default: Any) -> Any:
try: return json.loads(value or '')
@@ -51,7 +66,7 @@ def _rule_row(row: dict[str, Any]) -> dict[str, Any]:
def list_rules(profile_id: int | None = None, user_id: int | None = None) -> list[dict[str, Any]]:
user_id = user_id or default_user_id()
user_id = _resolve_user_id(user_id=user_id)
if profile_id is None:
profile = active_profile(); profile_id = int(profile['id']) if profile else None
with connect() as conn:
@@ -71,7 +86,7 @@ def list_rules(profile_id: int | None = None, user_id: int | None = None) -> lis
def get_rule(rule_id: int, profile_id: int, user_id: int | None = None) -> dict[str, Any]:
user_id = user_id or default_user_id()
user_id = _resolve_user_id(user_id=user_id)
with connect() as conn:
row = conn.execute('SELECT * FROM automation_rules WHERE id=? AND user_id=? AND profile_id=?', (rule_id, user_id, profile_id)).fetchone()
if not row: raise ValueError('Rule not found')
@@ -95,7 +110,7 @@ def export_rules(profile_id: int, user_id: int | None = None) -> dict[str, Any]:
def import_rules(profile_id: int, payload: dict[str, Any] | list[Any], user_id: int | None = None, replace: bool = False) -> list[dict[str, Any]]:
user_id = user_id or default_user_id()
user_id = _resolve_user_id(user_id=user_id)
raw_rules = payload if isinstance(payload, list) else payload.get('rules', []) if isinstance(payload, dict) else []
if not isinstance(raw_rules, list) or not raw_rules:
raise ValueError('Import file does not contain automation rules')
@@ -117,7 +132,7 @@ def import_rules(profile_id: int, payload: dict[str, Any] | list[Any], user_id:
def save_rule(profile_id: int, data: dict[str, Any], user_id: int | None = None) -> dict[str, Any]:
user_id = user_id or default_user_id()
user_id = _resolve_user_id(user_id=user_id)
name = str(data.get('name') or 'Automation rule').strip() or 'Automation rule'
conditions = data.get('conditions') or []
effects = data.get('effects') or []
@@ -136,20 +151,20 @@ def save_rule(profile_id: int, data: dict[str, Any], user_id: int | None = None)
def delete_rule(rule_id: int, profile_id: int, user_id: int | None = None) -> None:
user_id = user_id or default_user_id()
user_id = _resolve_user_id(user_id=user_id)
with connect() as conn:
conn.execute('DELETE FROM automation_rules WHERE id=? AND user_id=? AND profile_id=?', (rule_id, user_id, profile_id))
conn.execute('DELETE FROM automation_rule_state WHERE rule_id=? AND profile_id=?', (rule_id, profile_id))
def list_history(profile_id: int, user_id: int | None = None, limit: int = 30) -> list[dict[str, Any]]:
user_id = user_id or default_user_id()
user_id = _resolve_user_id(user_id=user_id)
with connect() as conn:
return conn.execute('SELECT * FROM automation_history WHERE user_id=? AND profile_id=? ORDER BY created_at DESC LIMIT ?', (user_id, profile_id, max(1, min(int(limit or 30), 100)))).fetchall()
def clear_history(profile_id: int, user_id: int | None = None) -> int:
user_id = user_id or default_user_id()
user_id = _resolve_user_id(user_id=user_id)
with connect() as conn:
# Note: Manual automation log cleanup is scoped to the active profile and current user.
cur = conn.execute('DELETE FROM automation_history WHERE user_id=? AND profile_id=?', (user_id, profile_id))
@@ -337,7 +352,7 @@ def _apply_effects_bulk(c: Any, profile: dict[str, Any], torrents: list[dict[str
def check(profile: dict | None = None, user_id: int | None = None, force: bool = False, rule_id: int | None = None) -> dict[str, Any]:
profile = profile or active_profile()
if not profile: return {'ok': False, 'error': 'No active rTorrent profile'}
user_id = user_id or default_user_id(); profile_id = int(profile['id'])
user_id = _resolve_user_id(profile, user_id); profile_id = int(profile['id'])
rules = [r for r in list_rules(profile_id, user_id) if (rule_id is None or int(r.get('id') or 0) == int(rule_id)) and (force or int(r.get('enabled') or 0))]
if not rules: return {'ok': True, 'checked': 0, 'applied': [], 'batches': [], 'rules': 0}
torrents = rtorrent.list_torrents(profile); applied = []; batches = []; now = utcnow()