From d4378afbc9609623b1ddc625efa0b9c32282614e Mon Sep 17 00:00:00 2001
From: RISE
Date: Mon, 27 Apr 2026 01:56:43 +0800
Subject: [PATCH] aa
---
app_web.py | 102 +++++++++++++++++++++++++++----------------
templates/index.html | 51 ++++++++++++++--------
2 files changed, 98 insertions(+), 55 deletions(-)
diff --git a/app_web.py b/app_web.py
index 3dbf02f..403c266 100644
--- a/app_web.py
+++ b/app_web.py
@@ -32,6 +32,25 @@ os.environ.setdefault("TELEGRAM_WEB_UI", "1")
logger = logging.getLogger("uvicorn.error")
+
+def web_url_prefix() -> str:
+ """反代子路径时设置 WEB_URL_PREFIX=/前缀(勿尾斜杠),与 nginx location 一致。"""
+ return (os.getenv("WEB_URL_PREFIX") or "").strip().rstrip("/")
+
+
+def with_url_prefix(path: str) -> str:
+ path = path if path.startswith("/") else f"/{path}"
+ pre = web_url_prefix()
+ return f"{pre}{path}" if pre else path
+
+
+def app_home_url(*, needauth: bool = False) -> str:
+ pre = web_url_prefix()
+ base = f"{pre}/" if pre else "/"
+ if needauth:
+ return f"{base}?needauth=1"
+ return base
+
WEB_CONSOLE_AUTH_KEY = "web_console_authenticated"
@@ -53,7 +72,7 @@ def is_console_authed(request: Request) -> bool:
def redirect_if_console_unauthed(request: Request) -> Optional[RedirectResponse]:
if is_console_authed(request):
return None
- return RedirectResponse(url="/?needauth=1", status_code=303)
+ return RedirectResponse(url=app_home_url(needauth=True), status_code=303)
def json_if_console_unauthed(request: Request) -> Optional[JSONResponse]:
@@ -179,6 +198,7 @@ class WebScraperService:
self.logs: deque[str] = deque(maxlen=1200)
self.continuous_task: Optional[asyncio.Task] = None
self.continuous_started_at: Optional[float] = None
+ self.continuous_start_lock = asyncio.Lock()
def _append(self, text: str) -> None:
ts = time.strftime("%Y-%m-%d %H:%M:%S")
@@ -425,25 +445,26 @@ class WebScraperService:
return await self.start_job(f"补抓媒体({len(channels)}个频道)", runner())
async def start_continuous(self) -> str:
- await self.ensure_ready()
- if self.is_continuous_running():
- return "持续抓取已在运行中"
- if not self.scraper.state.get("channels"):
- raise ValueError("当前没有监控频道,请先添加频道。")
+ async with self.continuous_start_lock:
+ await self.ensure_ready()
+ if self.is_continuous_running():
+ return "持续抓取已在运行中"
+ if not self.scraper.state.get("channels"):
+ raise ValueError("当前没有监控频道,请先添加频道。")
- async def runner():
- try:
- await self._run_and_capture(self.scraper.continuous_scraping())
- except asyncio.CancelledError:
- self._append("持续抓取任务已取消。")
- raise
- except Exception as e:
- self._append(f"持续抓取异常:{e}")
+ async def runner():
+ try:
+ await self._run_and_capture(self.scraper.continuous_scraping())
+ except asyncio.CancelledError:
+ self._append("持续抓取任务已取消。")
+ raise
+ except Exception as e:
+ self._append(f"持续抓取异常:{e}")
- self.continuous_started_at = time.time()
- self.continuous_task = asyncio.create_task(runner())
- self._append("已启动持续抓取(含心跳逻辑)。")
- return "持续抓取已启动"
+ self.continuous_started_at = time.time()
+ self.continuous_task = asyncio.create_task(runner())
+ self._append("已启动持续抓取(含心跳逻辑)。")
+ return "持续抓取已启动"
async def stop_continuous(self) -> str:
if not self.is_continuous_running():
@@ -678,7 +699,7 @@ async def auth_console_login(request: Request):
@app.post("/auth/console/logout")
async def auth_console_logout(request: Request):
request.session.pop(WEB_CONSOLE_AUTH_KEY, None)
- return RedirectResponse(url="/", status_code=303)
+ return RedirectResponse(url=app_home_url(), status_code=303)
@app.get("/api/stats/overview")
@@ -727,6 +748,9 @@ async def index(request: Request):
"account_channels": account_channels,
"console_authed": authed,
"need_auth_banner": request.query_params.get("needauth") == "1",
+ "app_url": with_url_prefix,
+ "url_prefix": web_url_prefix(),
+ "app_home": app_home_url(),
"connected": service.is_connected(),
"job_running": service.is_job_running(),
"job_name": service.job_name,
@@ -773,7 +797,7 @@ async def save_config(request: Request):
else:
updates[k] = str(form.get(k, "")).strip()
write_env_updates(updates)
- return RedirectResponse(url="/", status_code=303)
+ return RedirectResponse(url=app_home_url(), status_code=303)
@app.post("/start")
@@ -785,7 +809,7 @@ async def start_scraper(request: Request):
await service.ensure_ready()
except Exception as e:
service._append(f"初始化失败:{e}")
- return RedirectResponse(url="/", status_code=303)
+ return RedirectResponse(url=app_home_url(), status_code=303)
@app.post("/stop")
@@ -794,7 +818,7 @@ async def stop_scraper(request: Request):
if redir:
return redir
await service.disconnect()
- return RedirectResponse(url="/", status_code=303)
+ return RedirectResponse(url=app_home_url(), status_code=303)
@app.post("/channels/add")
@@ -807,7 +831,7 @@ async def add_channel(request: Request, channel_spec: str = Form(...)):
service._append(msg)
except Exception as e:
service._append(f"添加频道失败:{e}")
- return RedirectResponse(url="/", status_code=303)
+ return RedirectResponse(url=app_home_url(), status_code=303)
@app.post("/channels/add-selected")
@@ -820,14 +844,14 @@ async def add_channels_selected(request: Request):
ids = [str(x).strip() for x in raw_ids if str(x).strip()]
if not ids:
service._append("未选择任何频道,请在列表中勾选后再提交。")
- return RedirectResponse(url="/", status_code=303)
+ return RedirectResponse(url=app_home_url(), status_code=303)
for spec in ids:
try:
msg = await service.add_channel(spec)
service._append(msg)
except Exception as e:
service._append(f"添加失败({spec}):{e}")
- return RedirectResponse(url="/", status_code=303)
+ return RedirectResponse(url=app_home_url(), status_code=303)
@app.post("/channels/remove")
@@ -840,7 +864,7 @@ async def remove_channel(request: Request, channel_spec: str = Form(...)):
service._append(msg)
except Exception as e:
service._append(f"移除频道失败:{e}")
- return RedirectResponse(url="/", status_code=303)
+ return RedirectResponse(url=app_home_url(), status_code=303)
@app.post("/jobs/scrape")
@@ -853,7 +877,7 @@ async def start_scrape(request: Request, selection: str = Form("all")):
service._append(msg)
except Exception as e:
service._append(f"启动抓取失败:{e}")
- return RedirectResponse(url="/", status_code=303)
+ return RedirectResponse(url=app_home_url(), status_code=303)
@app.post("/jobs/export")
@@ -866,7 +890,7 @@ async def start_export(request: Request, selection: str = Form("all")):
service._append(msg)
except Exception as e:
service._append(f"启动导出失败:{e}")
- return RedirectResponse(url="/", status_code=303)
+ return RedirectResponse(url=app_home_url(), status_code=303)
@app.post("/jobs/rescrape")
@@ -879,20 +903,24 @@ async def start_rescrape(request: Request, selection: str = Form("all")):
service._append(msg)
except Exception as e:
service._append(f"启动补抓失败:{e}")
- return RedirectResponse(url="/", status_code=303)
+ return RedirectResponse(url=app_home_url(), status_code=303)
@app.post("/jobs/continuous/start")
-async def start_continuous(request: Request):
+async def start_continuous_route(request: Request):
redir = redirect_if_console_unauthed(request)
if redir:
return redir
- try:
- msg = await service.start_continuous()
- service._append(msg)
- except Exception as e:
- service._append(f"启动持续抓取失败:{e}")
- return RedirectResponse(url="/", status_code=303)
+
+ async def _run_start_continuous() -> None:
+ try:
+ await service.start_continuous()
+ except Exception as e:
+ service._append(f"启动持续抓取失败:{e}")
+
+ asyncio.create_task(_run_start_continuous())
+ service._append("已接收「启动持续抓取」:正在后台连接 Telegram 并启动(请勿重复点击;进度见运行日志)。")
+ return RedirectResponse(url=app_home_url(), status_code=303)
@app.post("/jobs/continuous/stop")
@@ -905,7 +933,7 @@ async def stop_continuous(request: Request):
service._append(msg)
except Exception as e:
service._append(f"停止持续抓取失败:{e}")
- return RedirectResponse(url="/", status_code=303)
+ return RedirectResponse(url=app_home_url(), status_code=303)
@app.get("/status")
diff --git a/templates/index.html b/templates/index.html
index 276f6aa..bd18f3a 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -764,7 +764,7 @@
{% if console_authed %}
-
{% endif %}
@@ -859,16 +859,16 @@
客户端:{{ error }}
{% endif %}
-
-
-
-
@@ -892,14 +892,14 @@
添加:@用户名、-100…、https://t.me/xxx。
不支持仅靠群内显示标题。
-
移除:all;1,2;-100…;@用户名 / t.me(须已在监控中)。
-
@@ -907,15 +907,15 @@
任务(抓取 / 导出 / 补媒体)
范围:all、1,3、-100… 或已在监控的 @ / 链接。
-
-
-
@@ -946,7 +946,7 @@
账号可见频道:勾选后加入监控。标题包含配置项「账号列表隐藏」中任一子串的会话不会出现在此列表(默认隐藏含「远程-到岗-技术招聘」的群,避免选到自有招聘群)。
{% if account_channels %}
-