Files
zTools2/app/services/tunnel_service.py
zikai 9af28f41b4 refactor: 清理死代码/提前失败/日志/高内聚低耦合
死代码移除:
- whiteboard_hub.py: 移除未引用的 reset_hub 单例重置函数
- tunnel_service.py: 移除未引用的 is_port_allowed (逻辑已在 sftp_server 内联)
- tunnel_session_dao.py: 移除未引用的 get_active_by_port
- pdf_job_dao.py: 移除未用 datetime 导入
- pdf_converter.py: 移除未用 shutil 导入
- pdf_service.py: 移除未用 PdfSubmitResponse 导入 + _do_convert 内未用 hashlib 导入
- upload_html.py: 移除未用 escape 导入 (JS 侧自有 escapeHtml)
- pdf_controller.py: 移除 _resolve_cookie 内未用 cfg 局部变量

提前失败/分层修复:
- database.py init_db_schema: 建表后用 inspector 校验既有表列与模型一致,
  缺列即抛 RuntimeError (fail-fast on schema drift), 避免运行期才暴露
- whiteboard_dao.get_or_create: 仅 IntegrityError 才回滚重读, 其他异常向上抛
  (原 except Exception 会掩盖 schema/连接等真实故障)
- pdf_service.admin_delete/_safe_delete_file: 改用 PdfJobDAO.delete /
  UploadedFileDAO.delete, 不再直接操作 job_dao.db / file_dao.db (修复分层契约:
  DAO 头注释声明 service 不直接操作 session)
- PdfJobDAO 新增 delete(job) 方法

日志补全 (8 处 silent catch):
- whiteboard_hub.py disconnect/close_board 关闭 ws: logger.debug
- whiteboard_controller _safe_send/_safe_close: logger.debug
- sftp_server _close_tunnel_dao/读用户名: logger.debug
- sftp_server validate_public_key: logger.warning (auth 路径, 避免静默失败)

文档:
- 新增 docs/routes.md, docs/configuration.md, docs/error-handling.md
- README.md 精简为简介/结构/外部依赖/apache2 配置/Ubuntu 安装/docs 链接
2026-07-28 11:34:35 +08:00

66 lines
2.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""反向隧道业务逻辑:会话注册 / 注销 / 查询。
SSH 服务收到 remote port-forward 请求时调 register 记一条 active 会话;
user 断开时调 close 标记 ended_atHTTP 路由调 get_active 查隧道端口做反代。
"""
from __future__ import annotations
import logging
from ..config import get_settings
from ..dao.tunnel_session_dao import TunnelSessionDAO
from ..models.tunnel_session import TunnelSession
logger = logging.getLogger("zikai.tunnel")
class TunnelService:
def __init__(self, dao: TunnelSessionDAO) -> None:
self.dao = dao
self.settings = get_settings().tunnel
def register(
self, user_name: str, user_ip: str, tunnel_port: int, local_port: int,
) -> TunnelSession:
"""登记一条活跃隧道会话。
若该 user 已有活跃会话先关闭旧的(同一 user 同一时刻只保留一条)。
"""
self.dao.close_active_by_user(user_name)
session = TunnelSession(
user_name=user_name,
user_ip=user_ip,
local_port=local_port,
tunnel_port=tunnel_port,
status="active",
)
saved = self.dao.create(session)
logger.info(
"隧道建立 user=%s ip=%s tunnel_port=%d local_port=%d",
user_name, user_ip, tunnel_port, local_port,
)
return saved
def close(self, user_name: str) -> int:
"""关闭该 user 的活跃会话SSH 断开时调用),返回关闭条数。"""
n = self.dao.close_active_by_user(user_name)
if n:
logger.info("隧道关闭 user=%s 条数=%d", user_name, n)
return n
def get_active(self, user_name: str) -> TunnelSession | None:
return self.dao.get_active_by_user(user_name)
def reap_orphans(self) -> int:
"""兜底清理:关闭所有 active 会话(进程重启时 DB 里残留的孤儿记录)。
由后台 reaper 在启动后调用一次。SSH 实际断开时已有 close() 处理,
这里只兜底进程异常退出后 DB 与实际状态不一致的情况。
"""
active = self.dao.list_active()
for session in active:
self.dao.close(session)
logger.info("reaper 清理孤儿隧道 id=%d user=%s", session.id, session.user_name)
return len(active)