fix profile-scoped backups and shared profile rules

This commit is contained in:
Mateusz Gruszczyński
2026-06-07 23:12:00 +02:00
parent 51e00a4e37
commit 8990f2b404
10 changed files with 264 additions and 86 deletions
+19 -9
View File
@@ -5,7 +5,7 @@ import time
from datetime import datetime, timezone
from ..db import connect, utcnow, default_user_id
from . import rtorrent
from . import auth, rtorrent
from .workers import enqueue
@@ -67,12 +67,14 @@ def _should_apply(profile: dict, group: dict, torrent: dict) -> tuple[bool, str]
def check(profile: dict, user_id: int | None = None) -> dict:
user_id = user_id or default_user_id()
viewer_user_id = user_id or default_user_id()
profile_id = int(profile["id"])
with connect() as conn:
groups = conn.execute("SELECT * FROM ratio_groups WHERE user_id=? AND profile_id=? AND enabled=1", (user_id, profile_id)).fetchall()
groups = conn.execute("SELECT * FROM ratio_groups WHERE profile_id=? AND enabled=1 ORDER BY lower(name), id", (profile_id,)).fetchall()
already = {row["torrent_hash"] for row in conn.execute("SELECT torrent_hash FROM ratio_assignments WHERE profile_id=? AND last_status='applied'", (profile_id,)).fetchall()}
groups_by_name = {str(g.get("name") or ""): g for g in groups}
groups_by_name: dict[str, dict] = {}
for group in groups:
groups_by_name.setdefault(str(group.get("name") or ""), group)
applied = 0
skipped = 0
queued_jobs = []
@@ -93,6 +95,11 @@ def check(profile: dict, user_id: int | None = None) -> dict:
)
continue
action = str(group.get("action") or "stop")
owner_user_id = int(group.get("user_id") or viewer_user_id)
if not auth.can_write_profile(profile_id, owner_user_id):
skipped += 1
_record(owner_user_id, profile_id, group, torrent, action, "skipped", "owner has no write access to profile")
continue
payload = {"hashes": [torrent["hash"]], "source": "ratio", "job_context": {"source": "ratio", "rule_name": group.get("name"), "hash_count": 1}}
if action == "remove_data":
api_action = "remove"
@@ -105,10 +112,10 @@ def check(profile: dict, user_id: int | None = None) -> dict:
payload["label"] = group.get("set_label") or group.get("name") or ""
else:
api_action = action if action in {"stop", "remove", "pause"} else "stop"
job_id = enqueue(api_action, profile_id, payload, user_id=user_id)
job_id = enqueue(api_action, profile_id, payload, user_id=owner_user_id)
queued_jobs.append(job_id)
applied += 1
_record(user_id, profile_id, group, torrent, action, "applied", reason, {"job_id": job_id, "api_action": api_action})
_record(owner_user_id, profile_id, group, torrent, action, "applied", reason, {"job_id": job_id, "api_action": api_action})
return {"applied": applied, "skipped": skipped, "job_ids": queued_jobs}
@@ -127,12 +134,15 @@ def start_scheduler(socketio=None) -> None:
try:
from .preferences import get_profile
with connect() as conn:
profiles = conn.execute("SELECT DISTINCT user_id, profile_id FROM ratio_groups WHERE enabled=1 AND profile_id IS NOT NULL").fetchall()
profiles = conn.execute("SELECT DISTINCT profile_id FROM ratio_groups WHERE enabled=1 AND profile_id IS NOT NULL").fetchall()
for row in profiles:
profile = get_profile(int(row["profile_id"]), int(row["user_id"]))
profile_id = int(row["profile_id"])
with connect() as conn:
owner = conn.execute("SELECT user_id FROM rtorrent_profiles WHERE id=?", (profile_id,)).fetchone()
profile = get_profile(profile_id, int(owner["user_id"] if owner and owner.get("user_id") else default_user_id()))
if not profile:
continue
result = check(profile, int(row["user_id"]))
result = check(profile)
if socketio and result.get("applied"):
socketio.emit("ratio_rules_checked", {"profile_id": profile["id"], **result}, to=f"profile:{profile['id']}")
except Exception: