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 链接
This commit is contained in:
@@ -11,7 +11,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import shutil
|
||||
import tempfile
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
@@ -26,7 +26,7 @@ from ..config import get_settings
|
||||
from ..dao.pdf_job_dao import PdfJobDAO
|
||||
from ..dao.uploaded_file_dao import UploadedFileDAO
|
||||
from ..models.pdf_job import PdfJob
|
||||
from ..schemas.pdf import PdfJobOut, PdfJobListResponse, PdfSubmitResponse
|
||||
from ..schemas.pdf import PdfJobOut, PdfJobListResponse
|
||||
from . import pdf_converter
|
||||
from .upload_service import UploadService
|
||||
|
||||
@@ -172,7 +172,6 @@ class PdfService:
|
||||
|
||||
# 落产物 UploadedFile(复用 commit_entity 的原子改名 + 入库)
|
||||
from ..models.uploaded_file import UploadedFile
|
||||
import hashlib
|
||||
size = part_path.stat().st_size
|
||||
sha256 = self._hash_file(part_path)
|
||||
entity = UploadedFile(
|
||||
@@ -277,8 +276,7 @@ class PdfService:
|
||||
if job.output_file_id is not None:
|
||||
self._safe_delete_file(job.output_file_id)
|
||||
# 删 PdfJob 行
|
||||
self.job_dao.db.delete(job)
|
||||
self.job_dao.db.commit()
|
||||
self.job_dao.delete(job)
|
||||
logger.info("管理员硬删 PDF 任务 job=%s", job_id)
|
||||
return True
|
||||
|
||||
@@ -293,7 +291,6 @@ class PdfService:
|
||||
except Exception as exc: # pragma: no cover
|
||||
logger.warning("删除文件失败 file_id=%s path=%s: %s", file_id, path, exc)
|
||||
try:
|
||||
self.file_dao.db.delete(row)
|
||||
self.file_dao.db.commit()
|
||||
self.file_dao.delete(file_id)
|
||||
except Exception as exc: # pragma: no cover
|
||||
logger.warning("删除文件 DB 行失败 file_id=%s: %s", file_id, exc)
|
||||
|
||||
@@ -27,8 +27,9 @@ class ZikaiSFTPServer(asyncssh.SFTPServer):
|
||||
super().__init__(chan, chroot=str(upload_root).encode())
|
||||
try:
|
||||
self._username = chan.get_extra_info("username") or "unknown"
|
||||
except Exception: # pragma: no cover
|
||||
except Exception as exc: # pragma: no cover
|
||||
self._username = "unknown"
|
||||
logger.debug("读取 SFTP 会话用户名失败: %s", exc)
|
||||
logger.info("SFTP 会话开始 user=%s chroot=%s", self._username, upload_root)
|
||||
|
||||
def exit(self) -> None:
|
||||
@@ -45,8 +46,8 @@ def _tunnel_dao():
|
||||
def _close_tunnel_dao(dao) -> None:
|
||||
try:
|
||||
dao.db.close()
|
||||
except Exception: # pragma: no cover
|
||||
pass
|
||||
except Exception as exc: # pragma: no cover
|
||||
logger.debug("关闭隧道 DAO 会话失败: %s", exc)
|
||||
|
||||
|
||||
class ZikaiSSHServer(asyncssh.SSHServer):
|
||||
@@ -101,7 +102,8 @@ class ZikaiSSHServer(asyncssh.SSHServer):
|
||||
try:
|
||||
# asyncssh 命中返回 dict(可能为空),未命中返回 None
|
||||
result = self._authorized_keys.validate(key, client_host=addr, client_addr=addr)
|
||||
except Exception:
|
||||
except Exception as exc: # pragma: no cover
|
||||
logger.warning("公钥校验异常 user=%s: %s", username, exc)
|
||||
result = None
|
||||
ok = result is not None
|
||||
if ok:
|
||||
|
||||
@@ -52,11 +52,6 @@ class TunnelService:
|
||||
def get_active(self, user_name: str) -> TunnelSession | None:
|
||||
return self.dao.get_active_by_user(user_name)
|
||||
|
||||
def is_port_allowed(self, user_name: str, tunnel_port: int) -> bool:
|
||||
"""校验该 user 是否被允许绑定该隧道端口(防 user 乱绑端口)。"""
|
||||
user = self.settings.find_user(user_name)
|
||||
return user is not None and user.tunnel_port == tunnel_port
|
||||
|
||||
def reap_orphans(self) -> int:
|
||||
"""兜底清理:关闭所有 active 会话(进程重启时 DB 里残留的孤儿记录)。
|
||||
|
||||
|
||||
@@ -92,8 +92,9 @@ class WhiteboardHub:
|
||||
# 尽力关闭 websocket(可能已关闭)
|
||||
try:
|
||||
await conn.websocket.close()
|
||||
except Exception: # pragma: no cover
|
||||
pass
|
||||
except Exception as exc: # pragma: no cover
|
||||
logger.debug("关闭 websocket 时出错 board=%s client=%s: %s",
|
||||
conn.board_id, conn.client_id, exc)
|
||||
logger.info("连接移除 board=%s client=%s(剩余 %d 人)",
|
||||
conn.board_id, conn.client_id, self.connection_count(conn.board_id))
|
||||
|
||||
@@ -161,8 +162,9 @@ class WhiteboardHub:
|
||||
for conn in conns:
|
||||
try:
|
||||
await conn.websocket.close()
|
||||
except Exception: # pragma: no cover
|
||||
pass
|
||||
except Exception as exc: # pragma: no cover
|
||||
logger.debug("关闭 websocket 时出错 board=%s client=%s: %s",
|
||||
conn.board_id, conn.client_id, exc)
|
||||
logger.info("关闭白板 board=%s,踢出 %d 个连接", board_id, len(conns))
|
||||
|
||||
|
||||
@@ -179,10 +181,3 @@ def get_hub() -> WhiteboardHub:
|
||||
if _hub is None:
|
||||
_hub = WhiteboardHub()
|
||||
return _hub
|
||||
|
||||
|
||||
def reset_hub() -> None:
|
||||
"""测试用:重置单例。"""
|
||||
global _hub
|
||||
with _hub_lock:
|
||||
_hub = None
|
||||
|
||||
Reference in New Issue
Block a user