This repository has been archived on 2026-10-02. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
NeteaseUrl/_mkpkg.py
T
snowy-yang 8446166120 refactor!: uv 管理环境 + 彻底删除技术债(Flask 与 requests 全部移除)
## uv 环境管理
- pyproject.toml 声明 dependencies + [dependency-groups].dev,uv.lock 精确锁定
- 删除 requirements.txt / requirements-fastapi.txt / requirements-dev.txt
- .python-version = 3.12;Dockerfile 改用 uv sync --frozen(可复现构建)
- 新增 jinja2(首页渲染)

## 删除 Flask 技术债
- 从 main.py 用 ast 精确剥离 56 个 Flask 节点(路由/中间件/错误处理/启动),
  框架无关的服务层落为 services/runtime.py(MusicAPIService、下载/打包 worker、
  任务监控、缓存清扫),零 Flask 依赖
- 删除 main.py(4527 行)与 auth_routes.py(1050 行)
- auth_system.py 移除会话/请求上下文层(login_session/current_user/login_required/
  admin_required/_client_ip 等),保留数据与业务层
- 新增 core/net.py(IP 解析)、web/auth_state.py(登录锁定/2FA 票据/发码限流)
- FastAPI 侧改直接依赖 services.runtime,不再 import main
- web/app.py 去掉 Flask fallback;入口默认根路径模式(API_PREFIX 可选)

## 删除 requests 技术债
- 新增 core/sync_http.py:requests 风格的同步门面(底层 aiohttp 连接池,
  流式读取经有界队列背压),含 Session/headers/(connect,read) 元组超时兼容
- core/utils、auth_system、podcast_api、music_downloader、services 全部切换
- 实测真实下载 5.2MB 音频完整成功

## 质量门禁归零
- ruff:0 错误(B904 全部修复,不再忽略)
- pyright:0 错误(类型收窄修复)
- pytest:140/140(含 golden 契约回归、root 模式 66 项路径覆盖)
- 真实 uvicorn 启动:/health、/、/static、受保护端鉴权全部正确

新增 tests/golden/responses.json:删除 Flask 前捕获的 32 例响应快照,
FastAPI 逐字段复现——删除巨石后行为回归仍有硬证据。
2026-09-26 11:10:17 +08:00

100 lines
4.6 KiB
Python
Raw 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.
# -*- coding: utf-8 -*-
"""重新生成部署包——在主项目目录下运行:python _mkpkg.py [--public]
- 默认:生成 owner 私有部署包(含 data/site.db、data/secret.key、cookie.txt,
供 owner 自己的服务器迁移使用,严禁对外分发)
- --public:生成可对外分发的干净包(F01:剔除全部敏感数据——数据库/密钥/网易云 Cookie/
日志),包内另附 _pkg_ok.txt 标记
F67:源/目标路径基于脚本自身位置推导,不再写死原作者桌面绝对路径。
"""
import glob
import os
import shutil
import sys
SRC = os.path.dirname(os.path.abspath(__file__))
DST = os.path.join(os.path.dirname(SRC), 'NeteaseUrl-部署包')
public = '--public' in sys.argv[1:]
ignore = shutil.ignore_patterns(
'venv*', '__pycache__', 'downloads', 'release*', '*.log', 'music_api.log*',
'_smoke_test_downloader.py', 'backups', '云村小助手-网站开关.bat',
'_audit_work*',
# 第 87 条:.zcode 为 AI 工具工作目录(内部规划/会话文件),不得进任何分发包
'.zcode*',
# Cookie 备份文件(qr_login._backup_cookie 产生,含真实 MUSIC_U):
# 'cookie.*.txt' 匹配 cookie.prewrite/backup/logout.*.txt 但不匹配 cookie.txt 本身
'cookie.*.txt', 'user_cookie.*.txt')
if os.path.exists(DST):
shutil.rmtree(DST)
shutil.copytree(SRC, DST, ignore=ignore)
# 双保险:显式删除目标里可能漏网的敏感文件(万一 ignore 模式被绕过)。
# 含轮转日志 music_api.log.1/2/3——fnmatch 的 *.log 不匹配它们,曾会随包外泄(含 IP/UA)
for _pat in ('cookie.*.txt', 'user_cookie.*.txt', 'music_api.log*'):
for _p in glob.glob(os.path.join(DST, _pat)):
try:
os.remove(_p)
except OSError:
pass
if public:
# F01:公开包必须剔除的敏感/私有内容(P1-1:补齐后产生的审计文档与内部引导文件——
# 修复台账含全部 117 项漏洞细节,新会话引导含内部路径与运维细节,均不得对外)
for rel in ('data', 'cookie.txt', 'user_cookie.txt', 'music_api.log',
'交接文档.md', '待修问题清单_三轮汇总.md',
'修复验收台账_F01-F117.md', '新会话引导.md',
'待修问题清单_第四轮深挖.md',
# 第 87 条:第五轮深挖清单同样含全部漏洞细节,公开包必须剔除
'待修问题清单_第五轮深挖.md',
'_pkg_ok.txt',):
p = os.path.join(DST, rel)
try:
if os.path.isdir(p):
shutil.rmtree(p)
elif os.path.exists(p):
os.remove(p)
except OSError:
pass
# 公开包同样剔除轮转日志与 Cookie 备份(双保险与上面同集)
for _pat in ('music_api.log*', 'cookie.*.txt', 'user_cookie.*.txt'):
for _p in glob.glob(os.path.join(DST, _pat)):
try:
os.remove(_p)
except OSError:
pass
marker = 'public-clean package (no secrets)'
else:
# 第 100 条(运维审计):私有包严禁携带《README_公开包说明.txt》——它宣称
# "不含任何敏感数据/由 --public 生成",而私有包含 cookie.txt/site.db/secret.key
# 与交接文档(明文管理员凭据),接手运维按该 README 认知会整包外发造成泄露。
# 私有包写入显式警告文件替代。
_p = os.path.join(DST, 'README_公开包说明.txt')
if os.path.exists(_p):
try:
os.remove(_p)
except OSError:
pass
with open(os.path.join(DST, 'README_私有包警告.txt'), 'w', encoding='utf-8') as _f:
_f.write(
'⚠️ 私有部署包——严禁外传、严禁上传网盘/代码仓库/发给任何人。\n'
'本包包含全部敏感数据:cookie.txt(网易云 SVIP 登录态)、data/site.db(用户库)、\n'
'data/secret.key(会话与音频签名根)、交接文档.md(含管理员凭据)。\n'
'泄露后果与应急流程见交接文档 §4/§5.7 A2/C1/C2/E4。\n'
'(本文件由 _mkpkg.py 私有模式自动生成;对外分发包由 --public 生成并剔除全部敏感物。)\n')
marker = None
n = 0
sz = 0
for r, _, fs in os.walk(DST):
for f in fs:
n += 1
sz += os.path.getsize(os.path.join(r, f))
with open(os.path.join(SRC, '_pkg_ok.txt'), 'w', encoding='utf-8') as f:
f.write(marker if marker else f'{n} files, {sz // 1024} KB')
shutil.copy2(os.path.join(SRC, '_pkg_ok.txt'), os.path.join(DST, '_pkg_ok.txt'))
print('ok:', 'public' if public else 'private', '-', n, 'files')