fix profile-scoped backups and shared profile rules
This commit is contained in:
@@ -35,9 +35,12 @@ def operation_logs_settings_save():
|
||||
profile = _active_profile_or_400()
|
||||
if not profile:
|
||||
return jsonify({"ok": False, "error": "No profile"}), 400
|
||||
settings = operation_logs.save_settings(int(profile["id"]), request.get_json(silent=True) or {})
|
||||
result = operation_logs.apply_retention(int(profile["id"]))
|
||||
return ok({"settings": settings, "retention": result})
|
||||
try:
|
||||
settings = operation_logs.save_settings(int(profile["id"]), request.get_json(silent=True) or {})
|
||||
result = operation_logs.apply_retention(int(profile["id"]))
|
||||
return ok({"settings": settings, "retention": result})
|
||||
except Exception as exc:
|
||||
return jsonify({"ok": False, "error": str(exc)}), 403 if isinstance(exc, PermissionError) else 400
|
||||
|
||||
|
||||
@bp.post("/operation-logs/clear")
|
||||
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from ._shared import *
|
||||
from ..services.rtorrent.diagnostics import profile_diagnostics
|
||||
from ..services import auth
|
||||
|
||||
@bp.get("/profiles")
|
||||
def profiles_list():
|
||||
@@ -108,8 +109,19 @@ def prefs_table_columns_recommended():
|
||||
def labels_list():
|
||||
profile = preferences.active_profile()
|
||||
pid = profile["id"] if profile else None
|
||||
if not pid:
|
||||
return ok({"labels": []})
|
||||
with connect() as conn:
|
||||
rows = conn.execute("SELECT * FROM labels WHERE user_id=? AND (profile_id=? OR profile_id IS NULL) ORDER BY name COLLATE NOCASE", (default_user_id(), pid)).fetchall()
|
||||
rows = conn.execute(
|
||||
"""
|
||||
SELECT l.*, COALESCE(u.display_name,u.username,u.email,'user ' || l.user_id) AS owner_name
|
||||
FROM labels l
|
||||
LEFT JOIN users u ON u.id=l.user_id
|
||||
WHERE l.profile_id=?
|
||||
ORDER BY l.name COLLATE NOCASE, l.id
|
||||
""",
|
||||
(pid,),
|
||||
).fetchall()
|
||||
return ok({"labels": rows})
|
||||
|
||||
|
||||
@@ -123,9 +135,15 @@ def labels_save():
|
||||
name = str(data.get("name") or "").strip()
|
||||
if not name:
|
||||
return jsonify({"ok": False, "error": "Missing label name"}), 400
|
||||
if not auth.can_write_profile(int(profile["id"]), default_user_id()):
|
||||
return jsonify({"ok": False, "error": "No write access to profile"}), 403
|
||||
now = utcnow()
|
||||
with connect() as conn:
|
||||
conn.execute("INSERT OR IGNORE INTO labels(user_id,profile_id,name,color,created_at,updated_at) VALUES(?,?,?,?,?,?)", (default_user_id(), profile["id"], name, data.get("color") or "#64748b", now, now))
|
||||
existing = conn.execute("SELECT id FROM labels WHERE profile_id=? AND lower(name)=lower(?) ORDER BY id LIMIT 1", (profile["id"], name)).fetchone()
|
||||
if existing:
|
||||
conn.execute("UPDATE labels SET color=?, updated_at=? WHERE id=?", (data.get("color") or "#64748b", now, existing["id"]))
|
||||
else:
|
||||
conn.execute("INSERT INTO labels(user_id,profile_id,name,color,created_at,updated_at) VALUES(?,?,?,?,?,?)", (default_user_id(), profile["id"], name, data.get("color") or "#64748b", now, now))
|
||||
return labels_list()
|
||||
|
||||
|
||||
@@ -134,8 +152,10 @@ def labels_save():
|
||||
def labels_delete(label_id: int):
|
||||
profile = preferences.active_profile()
|
||||
pid = profile["id"] if profile else None
|
||||
if not pid or not auth.can_write_profile(int(pid), default_user_id()):
|
||||
return jsonify({"ok": False, "error": "No write access to profile"}), 403
|
||||
with connect() as conn:
|
||||
conn.execute("DELETE FROM labels WHERE id=? AND user_id=? AND (profile_id=? OR profile_id IS NULL)", (label_id, default_user_id(), pid))
|
||||
conn.execute("DELETE FROM labels WHERE id=? AND profile_id=?", (label_id, pid))
|
||||
return labels_list()
|
||||
|
||||
|
||||
@@ -145,8 +165,17 @@ def ratio_groups_list():
|
||||
profile = preferences.active_profile()
|
||||
pid = profile["id"] if profile else None
|
||||
with connect() as conn:
|
||||
rows = conn.execute("SELECT * FROM ratio_groups WHERE user_id=? AND (profile_id=? OR profile_id IS NULL) ORDER BY name COLLATE NOCASE", (default_user_id(), pid)).fetchall()
|
||||
history = conn.execute("SELECT * FROM ratio_history WHERE user_id=? AND profile_id=? ORDER BY id DESC LIMIT 50", (default_user_id(), pid or 0)).fetchall() if pid else []
|
||||
rows = conn.execute(
|
||||
"""
|
||||
SELECT g.*, COALESCE(u.display_name,u.username,u.email,'user ' || g.user_id) AS owner_name
|
||||
FROM ratio_groups g
|
||||
LEFT JOIN users u ON u.id=g.user_id
|
||||
WHERE g.profile_id=?
|
||||
ORDER BY g.name COLLATE NOCASE, g.id
|
||||
""",
|
||||
(pid or 0,),
|
||||
).fetchall() if pid else []
|
||||
history = conn.execute("SELECT * FROM ratio_history WHERE profile_id=? ORDER BY id DESC LIMIT 50", (pid or 0,)).fetchall() if pid else []
|
||||
return ok({"groups": rows, "history": history})
|
||||
|
||||
|
||||
@@ -160,14 +189,23 @@ def ratio_groups_save():
|
||||
name = str(data.get("name") or "").strip()
|
||||
if not name:
|
||||
return jsonify({"ok": False, "error": "Missing group name"}), 400
|
||||
if not auth.can_write_profile(int(profile["id"]), default_user_id()):
|
||||
return jsonify({"ok": False, "error": "No write access to profile"}), 403
|
||||
now = utcnow()
|
||||
with connect() as conn:
|
||||
conn.execute(
|
||||
"""INSERT INTO ratio_groups(user_id,profile_id,name,min_ratio,max_ratio,seed_time_minutes,min_seed_time_minutes,ignore_private,ignore_active_upload,active_upload_min_bytes,move_path,set_label,action,enabled,created_at,updated_at)
|
||||
VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
ON CONFLICT(user_id,profile_id,name) DO UPDATE SET min_ratio=excluded.min_ratio,max_ratio=excluded.max_ratio,seed_time_minutes=excluded.seed_time_minutes,min_seed_time_minutes=excluded.min_seed_time_minutes,ignore_private=excluded.ignore_private,ignore_active_upload=excluded.ignore_active_upload,active_upload_min_bytes=excluded.active_upload_min_bytes,move_path=excluded.move_path,set_label=excluded.set_label,action=excluded.action,enabled=excluded.enabled,updated_at=excluded.updated_at""",
|
||||
(default_user_id(), profile["id"], name, float(data.get("min_ratio") or 1), float(data.get("max_ratio") or 2), int(data.get("seed_time_minutes") or 0), int(data.get("min_seed_time_minutes") or 0), 1 if data.get("ignore_private", True) else 0, 1 if data.get("ignore_active_upload", True) else 0, int(data.get("active_upload_min_bytes") or 1024), data.get("move_path") or "", data.get("set_label") or "", data.get("action") or "stop", 1 if data.get("enabled", True) else 0, now, now),
|
||||
)
|
||||
existing = conn.execute("SELECT id,user_id FROM ratio_groups WHERE profile_id=? AND lower(name)=lower(?) ORDER BY id LIMIT 1", (profile["id"], name)).fetchone()
|
||||
values = (float(data.get("min_ratio") or 1), float(data.get("max_ratio") or 2), int(data.get("seed_time_minutes") or 0), int(data.get("min_seed_time_minutes") or 0), 1 if data.get("ignore_private", True) else 0, 1 if data.get("ignore_active_upload", True) else 0, int(data.get("active_upload_min_bytes") or 1024), data.get("move_path") or "", data.get("set_label") or "", data.get("action") or "stop", 1 if data.get("enabled", True) else 0, now)
|
||||
if existing:
|
||||
conn.execute(
|
||||
"""UPDATE ratio_groups SET min_ratio=?,max_ratio=?,seed_time_minutes=?,min_seed_time_minutes=?,ignore_private=?,ignore_active_upload=?,active_upload_min_bytes=?,move_path=?,set_label=?,action=?,enabled=?,updated_at=? WHERE id=? AND profile_id=?""",
|
||||
(*values, existing["id"], profile["id"]),
|
||||
)
|
||||
else:
|
||||
conn.execute(
|
||||
"""INSERT INTO ratio_groups(user_id,profile_id,name,min_ratio,max_ratio,seed_time_minutes,min_seed_time_minutes,ignore_private,ignore_active_upload,active_upload_min_bytes,move_path,set_label,action,enabled,created_at,updated_at)
|
||||
VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||
(default_user_id(), profile["id"], name, *values[:-1], now, now),
|
||||
)
|
||||
return ratio_groups_list()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user