fix: 收紧 GUI 安全边界

This commit is contained in:
2026-06-20 13:51:06 +08:00
parent ff73470142
commit a1b791e6ef
10 changed files with 112 additions and 67 deletions
+39 -27
View File
@@ -16,6 +16,7 @@ import traceback
import urllib.error
import urllib.request
from calendar import monthrange
from contextlib import contextmanager
from datetime import datetime, timedelta, timezone
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from typing import Any
@@ -178,11 +179,22 @@ def connect() -> sqlite3.Connection:
return conn
@contextmanager
def db_connect():
with db_lock:
conn = connect()
try:
with conn:
yield conn
finally:
conn.close()
def init_db() -> None:
db_dir = os.path.dirname(DB_PATH)
if db_dir:
os.makedirs(db_dir, exist_ok=True)
with db_lock, connect() as conn:
with db_connect() as conn:
conn.executescript(
"""
CREATE TABLE IF NOT EXISTS job_schedules (
@@ -322,7 +334,7 @@ def row_to_one_time_job(row: sqlite3.Row) -> dict[str, Any]:
def fetch_job(schedule_id: int) -> dict[str, Any]:
with db_lock, connect() as conn:
with db_connect() as conn:
row = conn.execute("SELECT * FROM job_schedules WHERE id=?", (schedule_id,)).fetchone()
if row is None:
raise ApiError(404, "job not found")
@@ -331,13 +343,13 @@ def fetch_job(schedule_id: int) -> dict[str, Any]:
def list_jobs() -> list[dict[str, Any]]:
refresh_running_jobs()
with db_lock, connect() as conn:
with db_connect() as conn:
rows = conn.execute("SELECT * FROM job_schedules ORDER BY id DESC").fetchall()
return [row_to_job(row) for row in rows]
def fetch_one_time_job(job_id: int) -> dict[str, Any]:
with db_lock, connect() as conn:
with db_connect() as conn:
row = conn.execute("SELECT * FROM one_time_jobs WHERE id=?", (job_id,)).fetchone()
if row is None:
raise ApiError(404, "job not found")
@@ -346,7 +358,7 @@ def fetch_one_time_job(job_id: int) -> dict[str, Any]:
def list_one_time_jobs() -> list[dict[str, Any]]:
refresh_one_time_jobs()
with db_lock, connect() as conn:
with db_connect() as conn:
rows = conn.execute("SELECT * FROM one_time_jobs ORDER BY updated_at DESC, id DESC").fetchall()
return [row_to_one_time_job(row) for row in rows]
@@ -383,7 +395,7 @@ def create_job(payload: dict[str, Any]) -> dict[str, Any]:
interval_seconds = legacy_interval_seconds(schedule_kind)
now = iso(utcnow())
with db_lock, connect() as conn:
with db_connect() as conn:
cur = conn.execute(
"""
INSERT INTO job_schedules
@@ -423,7 +435,7 @@ def create_one_time_job(payload: dict[str, Any]) -> dict[str, Any]:
raise ApiError(400, "src and dst are required")
now = iso(utcnow())
with db_lock, connect() as conn:
with db_connect() as conn:
cur = conn.execute(
"""
INSERT INTO one_time_jobs
@@ -452,7 +464,7 @@ def legacy_interval_seconds(schedule_kind: str) -> int:
def delete_job(schedule_id: int) -> None:
with db_lock, connect() as conn:
with db_connect() as conn:
cur = conn.execute("DELETE FROM job_schedules WHERE id=?", (schedule_id,))
if cur.rowcount == 0:
raise ApiError(404, "job not found")
@@ -461,7 +473,7 @@ def delete_job(schedule_id: int) -> None:
def manual_run(schedule_id: int) -> dict[str, Any]:
with scheduler_lock:
refresh_running_jobs()
with db_lock, connect() as conn:
with db_connect() as conn:
row = conn.execute("SELECT * FROM job_schedules WHERE id=?", (schedule_id,)).fetchone()
if row is None:
raise ApiError(404, "job not found")
@@ -472,7 +484,7 @@ def manual_run(schedule_id: int) -> dict[str, Any]:
def stop_job(schedule_id: int) -> dict[str, Any]:
with db_lock, connect() as conn:
with db_connect() as conn:
row = conn.execute("SELECT * FROM job_schedules WHERE id=?", (schedule_id,)).fetchone()
if row is None:
raise ApiError(404, "job not found")
@@ -485,7 +497,7 @@ def stop_job(schedule_id: int) -> dict[str, Any]:
now = iso(now_dt)
next_run_at = compute_row_next_run_at(row, now_dt)
snapshot = json.dumps({"finished": True, "error": "stopped", "jobid": jobid}, separators=(",", ":"))
with db_lock, connect() as conn:
with db_connect() as conn:
conn.execute(
"""
UPDATE job_schedules
@@ -512,7 +524,7 @@ def stop_job(schedule_id: int) -> dict[str, Any]:
def manual_run_one_time_job(job_id: int) -> dict[str, Any]:
with db_lock, connect() as conn:
with db_connect() as conn:
row = conn.execute("SELECT * FROM one_time_jobs WHERE id=?", (job_id,)).fetchone()
if row is None:
raise ApiError(404, "job not found")
@@ -523,7 +535,7 @@ def manual_run_one_time_job(job_id: int) -> dict[str, Any]:
def stop_one_time_job(job_id: int) -> dict[str, Any]:
with db_lock, connect() as conn:
with db_connect() as conn:
row = conn.execute("SELECT * FROM one_time_jobs WHERE id=?", (job_id,)).fetchone()
if row is None:
raise ApiError(404, "job not found")
@@ -534,7 +546,7 @@ def stop_one_time_job(job_id: int) -> dict[str, Any]:
rc_post("job/stop", {"jobid": jobid})
now = iso(utcnow())
snapshot = json.dumps({"finished": True, "error": "stopped", "jobid": jobid}, separators=(",", ":"))
with db_lock, connect() as conn:
with db_connect() as conn:
conn.execute(
"""
UPDATE one_time_jobs
@@ -551,7 +563,7 @@ def stop_one_time_job(job_id: int) -> dict[str, Any]:
def delete_one_time_job(job_id: int) -> None:
with db_lock, connect() as conn:
with db_connect() as conn:
cur = conn.execute("DELETE FROM one_time_jobs WHERE id=?", (job_id,))
if cur.rowcount == 0:
raise ApiError(404, "job not found")
@@ -619,7 +631,7 @@ def start_schedule(row: sqlite3.Row, trigger: str) -> None:
error = str(exc)
next_run_at = compute_row_next_run_at(row, now_dt)
snapshot = json.dumps({"finished": True, "error": error}, separators=(",", ":"))
with db_lock, connect() as conn:
with db_connect() as conn:
conn.execute(
"""
INSERT INTO job_runs
@@ -647,7 +659,7 @@ def start_schedule(row: sqlite3.Row, trigger: str) -> None:
snapshot = json.dumps({"finished": False, "jobid": jobid}, separators=(",", ":"))
next_run_at = compute_row_next_run_at(row, now_dt)
with db_lock, connect() as conn:
with db_connect() as conn:
conn.execute(
"""
INSERT INTO job_runs
@@ -675,7 +687,7 @@ def start_schedule(row: sqlite3.Row, trigger: str) -> None:
def start_one_time_job(job_id: int) -> None:
with db_lock, connect() as conn:
with db_connect() as conn:
row = conn.execute("SELECT * FROM one_time_jobs WHERE id=?", (job_id,)).fetchone()
if row is None:
raise ApiError(404, "job not found")
@@ -699,7 +711,7 @@ def start_one_time_job(job_id: int) -> None:
except Exception as exc:
error = str(exc)
snapshot = json.dumps({"finished": True, "error": error}, separators=(",", ":"))
with db_lock, connect() as conn:
with db_connect() as conn:
conn.execute(
"""
UPDATE one_time_jobs
@@ -717,7 +729,7 @@ def start_one_time_job(job_id: int) -> None:
return
snapshot = json.dumps({"finished": False, "jobid": jobid}, separators=(",", ":"))
with db_lock, connect() as conn:
with db_connect() as conn:
conn.execute(
"""
UPDATE one_time_jobs
@@ -762,7 +774,7 @@ def status_with_stats(kind: str, row: sqlite3.Row, jobid: int) -> dict[str, Any]
def refresh_running_jobs() -> None:
with db_lock, connect() as conn:
with db_connect() as conn:
rows = conn.execute("SELECT * FROM job_schedules WHERE current_jobid IS NOT NULL").fetchall()
for row in rows:
@@ -776,7 +788,7 @@ def refresh_running_jobs() -> None:
def refresh_one_time_jobs() -> None:
with db_lock, connect() as conn:
with db_connect() as conn:
rows = conn.execute("SELECT * FROM one_time_jobs WHERE status='running' AND jobid IS NOT NULL").fetchall()
for row in rows:
@@ -797,7 +809,7 @@ def apply_status(row: sqlite3.Row, status: dict[str, Any]) -> None:
name = status_name(status)
if name == "running":
with db_lock, connect() as conn:
with db_connect() as conn:
conn.execute(
"""
UPDATE job_schedules
@@ -821,7 +833,7 @@ def apply_status(row: sqlite3.Row, status: dict[str, Any]) -> None:
error = status.get("error")
next_run_at = compute_row_next_run_at(row, now_dt)
with db_lock, connect() as conn:
with db_connect() as conn:
conn.execute(
"""
UPDATE job_schedules
@@ -854,7 +866,7 @@ def apply_one_time_status(row: sqlite3.Row, status: dict[str, Any]) -> None:
name = status_name(status)
if name == "running":
with db_lock, connect() as conn:
with db_connect() as conn:
conn.execute(
"""
UPDATE one_time_jobs
@@ -869,7 +881,7 @@ def apply_one_time_status(row: sqlite3.Row, status: dict[str, Any]) -> None:
return
error = status.get("error")
with db_lock, connect() as conn:
with db_connect() as conn:
conn.execute(
"""
UPDATE one_time_jobs
@@ -887,7 +899,7 @@ def apply_one_time_status(row: sqlite3.Row, status: dict[str, Any]) -> None:
def run_due_jobs() -> None:
with scheduler_lock:
now = iso(utcnow())
with db_lock, connect() as conn:
with db_connect() as conn:
rows = conn.execute(
"""
SELECT * FROM job_schedules