tiny_auth_support #6
@@ -117,6 +117,9 @@ def bypass_user_id() -> int:
|
|||||||
def current_user_id() -> int:
|
def current_user_id() -> int:
|
||||||
if not enabled():
|
if not enabled():
|
||||||
return default_user_id()
|
return default_user_id()
|
||||||
|
if not has_request_context():
|
||||||
|
# Note: Background jobs and schedulers do not have Flask request/session state.
|
||||||
|
return 0
|
||||||
if auth_bypassed_request():
|
if auth_bypassed_request():
|
||||||
return bypass_user_id()
|
return bypass_user_id()
|
||||||
api_user_id = getattr(g, "api_user_id", None)
|
api_user_id = getattr(g, "api_user_id", None)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import json
|
|||||||
import psutil
|
import psutil
|
||||||
from flask_socketio import emit, join_room, leave_room, disconnect
|
from flask_socketio import emit, join_room, leave_room, disconnect
|
||||||
from .preferences import active_profile, get_profile
|
from .preferences import active_profile, get_profile
|
||||||
|
from ..db import default_user_id
|
||||||
from .torrent_cache import torrent_cache
|
from .torrent_cache import torrent_cache
|
||||||
from .torrent_summary import cached_summary
|
from .torrent_summary import cached_summary
|
||||||
from . import rtorrent, smart_queue, traffic_history, automation_rules, torrent_stats, auth, speed_peaks, poller_control, download_planner
|
from . import rtorrent, smart_queue, traffic_history, automation_rules, torrent_stats, auth, speed_peaks, poller_control, download_planner
|
||||||
@@ -38,13 +39,15 @@ def _emit_profile(socketio, event: str, payload: dict, profile_id: int) -> None:
|
|||||||
|
|
||||||
def _run_slow_profile_tasks(socketio, profile: dict, profile_id: int) -> None:
|
def _run_slow_profile_tasks(socketio, profile: dict, profile_id: int) -> None:
|
||||||
state = poller_control.state_for(profile_id)
|
state = poller_control.state_for(profile_id)
|
||||||
|
# Note: Background checks keep the profile owner so bypass/admin profiles do not enqueue jobs as the fallback user.
|
||||||
|
profile_user_id = int(profile.get("user_id") or default_user_id())
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
torrent_stats.queue_refresh(socketio, profile, force=False, room=_profile_room(profile_id) if auth.enabled() else None)
|
torrent_stats.queue_refresh(socketio, profile, force=False, room=_profile_room(profile_id) if auth.enabled() else None)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
_emit_profile(socketio, "torrent_stats_update", {"ok": False, "profile_id": profile_id, "error": str(exc)}, profile_id)
|
_emit_profile(socketio, "torrent_stats_update", {"ok": False, "profile_id": profile_id, "error": str(exc)}, profile_id)
|
||||||
try:
|
try:
|
||||||
result = smart_queue.check(profile, force=False)
|
result = smart_queue.check(profile, user_id=profile_user_id, force=False)
|
||||||
if result.get("enabled"):
|
if result.get("enabled"):
|
||||||
_emit_profile(socketio, "smart_queue_update", result, profile_id)
|
_emit_profile(socketio, "smart_queue_update", result, profile_id)
|
||||||
if result.get("stopped") or result.get("started") or result.get("start_requested") or result.get("paused") or result.get("resumed"):
|
if result.get("stopped") or result.get("started") or result.get("start_requested") or result.get("paused") or result.get("resumed"):
|
||||||
@@ -55,7 +58,7 @@ def _run_slow_profile_tasks(socketio, profile: dict, profile_id: int) -> None:
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
_emit_profile(socketio, "smart_queue_update", {"ok": False, "profile_id": profile_id, "error": str(exc)}, profile_id)
|
_emit_profile(socketio, "smart_queue_update", {"ok": False, "profile_id": profile_id, "error": str(exc)}, profile_id)
|
||||||
try:
|
try:
|
||||||
auto_result = automation_rules.check(profile, force=False)
|
auto_result = automation_rules.check(profile, user_id=profile_user_id, force=False)
|
||||||
if auto_result.get("applied"):
|
if auto_result.get("applied"):
|
||||||
_emit_profile(socketio, "automation_update", auto_result, profile_id)
|
_emit_profile(socketio, "automation_update", auto_result, profile_id)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ from . import rtorrent, auth, disk_guard, operation_logs
|
|||||||
from .preferences import get_profile
|
from .preferences import get_profile
|
||||||
from ..config import WORKERS
|
from ..config import WORKERS
|
||||||
from ..db import connect, utcnow, default_user_id
|
from ..db import connect, utcnow, default_user_id
|
||||||
|
from .torrent_cache import torrent_cache
|
||||||
|
from .torrent_summary import cached_summary
|
||||||
|
|
||||||
LIGHT_ACTIONS = {"start", "stop", "pause", "resume", "unpause", "set_label", "set_ratio_group", "reannounce", "set_limits"}
|
LIGHT_ACTIONS = {"start", "stop", "pause", "resume", "unpause", "set_label", "set_ratio_group", "reannounce", "set_limits"}
|
||||||
WATCHDOG_INTERVAL_SECONDS = 30
|
WATCHDOG_INTERVAL_SECONDS = 30
|
||||||
@@ -216,10 +218,11 @@ def _job_event_meta(payload: dict) -> dict:
|
|||||||
return meta
|
return meta
|
||||||
|
|
||||||
|
|
||||||
def _execute(profile: dict, action_name: str, payload: dict):
|
def _execute(profile: dict, action_name: str, payload: dict, user_id: int | None = None):
|
||||||
if action_name == "smart_queue_check":
|
if action_name == "smart_queue_check":
|
||||||
from . import smart_queue
|
from . import smart_queue
|
||||||
return smart_queue.check(profile, user_id=auth.current_user_id() or default_user_id(), force=True)
|
# Note: Worker execution uses the job owner instead of Flask session state.
|
||||||
|
return smart_queue.check(profile, user_id=user_id or default_user_id(), force=True)
|
||||||
if action_name == "add_magnet":
|
if action_name == "add_magnet":
|
||||||
if bool(payload.get("start", True)):
|
if bool(payload.get("start", True)):
|
||||||
disk_guard.assert_can_start_download(profile)
|
disk_guard.assert_can_start_download(profile)
|
||||||
@@ -268,6 +271,22 @@ def _mark_running(job_id: str, attempts: int) -> bool:
|
|||||||
return int(cur.rowcount or 0) == 1
|
return int(cur.rowcount or 0) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def _emit_torrent_refresh(profile: dict, action_name: str) -> None:
|
||||||
|
if action_name not in {"add_magnet", "add_torrent_raw", "remove", "move", "start", "stop", "pause", "resume", "unpause", "set_label", "set_ratio_group", "recheck"}:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
diff = torrent_cache.refresh(profile)
|
||||||
|
profile_id = int(profile["id"])
|
||||||
|
if diff.get("ok"):
|
||||||
|
rows = torrent_cache.snapshot(profile_id)
|
||||||
|
_emit("torrent_patch", {**diff, "summary": cached_summary(profile_id, rows, force=True)})
|
||||||
|
else:
|
||||||
|
_emit("rtorrent_error", diff)
|
||||||
|
except Exception as exc:
|
||||||
|
# Note: A failed live refresh must not change the already completed job result.
|
||||||
|
_emit("rtorrent_error", {"profile_id": int(profile.get("id") or 0), "error": str(exc)})
|
||||||
|
|
||||||
|
|
||||||
def _run(job_id: str):
|
def _run(job_id: str):
|
||||||
if not _claim_runner(job_id):
|
if not _claim_runner(job_id):
|
||||||
return
|
return
|
||||||
@@ -303,7 +322,7 @@ def _run(job_id: str):
|
|||||||
operation_logs.record_job_event(profile["id"], job["action"], "started", payload, job_id=job_id, user_id=int(job.get("user_id") or 0))
|
operation_logs.record_job_event(profile["id"], job["action"], "started", payload, job_id=job_id, user_id=int(job.get("user_id") or 0))
|
||||||
_emit("operation_started", {"job_id": job_id, "action": job["action"], "profile_id": profile["id"], "hashes": payload.get("hashes") or [], "hash_count": len(payload.get("hashes") or []), "bulk": len(payload.get("hashes") or []) > 1, **event_meta})
|
_emit("operation_started", {"job_id": job_id, "action": job["action"], "profile_id": profile["id"], "hashes": payload.get("hashes") or [], "hash_count": len(payload.get("hashes") or []), "bulk": len(payload.get("hashes") or []) > 1, **event_meta})
|
||||||
_emit("job_update", {"id": job_id, "profile_id": profile["id"], "status": "running", "attempts": attempts})
|
_emit("job_update", {"id": job_id, "profile_id": profile["id"], "status": "running", "attempts": attempts})
|
||||||
result = _execute(profile, job["action"], payload)
|
result = _execute(profile, job["action"], payload, user_id=int(job.get("user_id") or 0))
|
||||||
fresh = _job_row(job_id)
|
fresh = _job_row(job_id)
|
||||||
# Note: Emergency cancel and watchdog timeout keep late work from overwriting a terminal state.
|
# Note: Emergency cancel and watchdog timeout keep late work from overwriting a terminal state.
|
||||||
if fresh and fresh["status"] != "running":
|
if fresh and fresh["status"] != "running":
|
||||||
|
|||||||
2
pytorrent/static/js/bootstrap.js
vendored
2
pytorrent/static/js/bootstrap.js
vendored
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user