fix: harden scheduling and deployment flow

This commit is contained in:
Rixuan Shao
2026-07-11 14:58:05 +08:00
parent c496d90039
commit e3142cabbf
28 changed files with 552 additions and 1750 deletions
+28 -6
View File
@@ -1,9 +1,11 @@
import json
import logging
import os
from datetime import datetime, timedelta, timezone
from pathlib import Path
import urllib.error
import urllib.request
from contextlib import asynccontextmanager
import uvicorn
from fastapi import FastAPI, Request
@@ -12,8 +14,6 @@ from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from starlette.middleware.sessions import SessionMiddleware
logger = logging.getLogger(__name__)
from core.friends import fetch_account_friends
from core.send_state import history_entry_is_strong_confirmed_today, parse_sent_at
from core.tasks import run_browser_tasks, task_run_lock
@@ -50,9 +50,12 @@ from webui.ops import (
run_task_now,
run_unsent_retry_now,
task_run_lock_status,
sync_daily_schedule_from_config,
update_daily_schedule,
)
logger = logging.getLogger(__name__)
BASE_DIR = Path(__file__).resolve().parent
TEMPLATES_DIR = BASE_DIR / "templates"
@@ -190,12 +193,17 @@ def mark_target_unconfirmed(account, target_name, *, reason="manual_reset_possib
def login_desktop_api_url():
settings = get_app_settings(force_reload=True)
return str(settings.get("login_desktop_api_url") or "http://127.0.0.1:18090").rstrip("/")
configured = os.getenv("SPARKFLOW_LOGIN_DESKTOP_API_URL") or settings.get("login_desktop_api_url")
return str(configured or "http://127.0.0.1:18090").rstrip("/")
def login_desktop_public_url(request: Request) -> str:
settings = get_app_settings(force_reload=True)
configured_url = str(settings.get("login_desktop_public_url") or "").strip()
configured_url = str(
os.getenv("SPARKFLOW_LOGIN_DESKTOP_PUBLIC_URL")
or settings.get("login_desktop_public_url")
or ""
).strip()
if configured_url:
return configured_url
@@ -274,13 +282,27 @@ def public_app_settings():
def create_app():
settings = get_app_settings()
app = FastAPI(title="DouYin Spark Flow Admin")
@asynccontextmanager
async def lifespan(_app):
result = sync_daily_schedule_from_config()
if result.returncode != 0:
logger.warning("Failed to synchronize the configured daily schedule: %s", result.stderr)
yield
secure_cookie = str(os.getenv("SPARKFLOW_SESSION_COOKIE_SECURE") or "").strip().lower() in {
"1",
"true",
"yes",
"on",
}
app = FastAPI(title="DouYin Spark Flow Admin", lifespan=lifespan)
app.add_middleware(
SessionMiddleware,
secret_key=settings["session_secret"],
max_age=settings["session_max_age_seconds"],
same_site="lax",
https_only=False,
https_only=secure_cookie,
)
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
DEBUG_ARTIFACTS_DIR.mkdir(parents=True, exist_ok=True)
File diff suppressed because it is too large Load Diff
+40 -1
View File
@@ -13,7 +13,7 @@ from pathlib import Path
from zoneinfo import ZoneInfo
from core.send_state import history_entry_is_strong_confirmed_today, parse_sent_at
from utils.config import get_app_settings, get_config, get_userData, normalize_unique_id, repo_root, save_config
from utils.config import get_app_settings, get_config, get_userData, repo_root, save_config
logger = logging.getLogger(__name__)
@@ -423,6 +423,8 @@ def refresh_proxy():
def restart_proxy():
try:
if running_in_container():
return run_command(["docker", "restart", "mihomo"], timeout=120)
return run_command(compose_command("restart", "proxy"), timeout=120)
except Exception as exc:
logger.error("restart_proxy failed: %s", exc)
@@ -566,6 +568,43 @@ def update_daily_schedule(time_string):
return _empty_result(stderr=str(exc))
def sync_daily_schedule_from_config():
config = get_config(force_reload=True)
window = dict(config.get("dailySendWindow") or {})
if not window.get("enabled"):
return subprocess.CompletedProcess(
args=["sync-daily-schedule"],
returncode=0,
stdout="schedule disabled; existing crontab left unchanged",
stderr="",
)
try:
time_string = _format_window_schedule(window)
current = read_crontab()
updated = replace_douyin_cron_schedule(current, time_string)
if updated == current:
return subprocess.CompletedProcess(
args=["sync-daily-schedule"], returncode=0, stdout="already synchronized", stderr=""
)
if running_in_container() and HOST_CRONTAB_PATH.parent.exists():
HOST_CRONTAB_PATH.write_text(updated, encoding="utf-8")
return subprocess.CompletedProcess(
args=["sync-daily-schedule"], returncode=0, stdout="host spool updated", stderr=""
)
return subprocess.run(
["crontab", "-"],
input=updated,
text=True,
capture_output=True,
check=False,
timeout=10,
)
except Exception as exc:
logger.error("sync_daily_schedule_from_config failed: %s", exc)
return _empty_result(stderr=str(exc))
def current_daily_schedule():
config = get_config(force_reload=True)
window = dict(config.get("dailySendWindow") or {})