v0.2.0-rc1
Pre-release
Pre-release
Added
- 管理后台:新增完整的 Web 管理后台(前端 SPA + 后端 serverless API),访问
/admin进入。支持首次初始化、密码登录、配置可视化编辑、文件上传、版本更新检查与触发,无需手动编辑仓库文件即可管理站点 /setup初始化页面:首次部署后访问/admin自动进入设置密码流程,密码经 PBKDF2-SHA256(100k 迭代 + 16 字节随机盐)哈希后存储到public/admin.json,设置后/setup自动关闭(先到先得模型,利用 GitHub API 409/422 防并发初始化竞态)- 配置文件全文加密:
public/config.json与public/admin.json写入 GitHub 仓库时使用 AES-GCM 256 加密,密钥由CONFIG_ENCRYPTION_KEY经 PBKDF2(100k 迭代)派生,仓库中只存储v1:base64(salt+iv+ciphertext)密文;开发模式(DEVELOPMENT=true)不加密 CONFIG_ENCRYPTION_KEY环境变量:新增独立配置加密密钥,配置加密与 Cookie 签名均从它派生,与GH_TOKEN完全解耦,GH_TOKEN可独立轮换而不影响已加密配置与已签发 Cookie;生产模式校验密钥强度(至少 32 位、拒绝全相同字符 / 纯顺序字符等弱模式)ADMIN_DISABLED环境变量:设为true或1时禁用管理后台,/admin显示"已禁用"页,除状态探针外所有/api/*返回 403;播放器公开配置改为构建期硬编译,不再依赖管理 API- Cookie 鉴权体系(
server/core/auth.ts):基于 HMAC-SHA256 签名的 7 天有效期 token(payloadB64.sigB64),HttpOnly + Secure + SameSite=Lax;签名密钥从CONFIG_ENCRYPTION_KEY派生;tokenVersion机制——修改密码时自增并持久化,旧 Cookie 立即失效;timingSafeEqual常量时间比较防时序侧信道 - 速率限制(
server/core/rate-limit.ts):登录 / 初始化 / 更新端点独立限流(滑动窗口 + 超限封禁),按key + IP + UserAgent维度,登录成功后重置计数;客户端 IP 优先取CF-Connecting-IP - GitHub 持久化层(
server/core/github.ts):封装 Contents API 的文件读 / 写 / 删,8 秒超时,sha乐观锁防并发覆盖,GitHubWriteError携带 HTTP 状态码 - 版本更新检查与一键触发(
server/core/update-handler.ts+.github/workflows/update-from-upstream.yml):后台关于页检查上游 Release,完整 SemVer 实现(支持预发布版本比较);一键触发update-from-upstream.ymlworkflow 拉取上游 release 到临时分支,验证通过后自动合并回目标分支(保留用户数据:config.json/admin.json/icon.*/music/等均 exclude);支持 GitHub 代理({url}占位符或前缀拼接) - Meting API 连通性测试(
server/core/music-api-tester.ts):保存配置前并发测试所有启用歌单(并发 3),返回每个歌单的状态码 / 曲目数 / 错误信息与汇总结果,8 秒超时 - 本地音乐文件上传(
upload-handler.ts+LocalTrackEditor.vue):支持音频 / 封面 / 歌词 LRC 文件上传到public/music/{trackId}/,50MB 限制,删除曲目时联动删除已上传文件;路径白名单(仅允许public/前缀)+new URL()规范化防路径穿越 - 站点图标上传(
SiteSettingsEditor.vue):支持 SVG / PNG / JPEG / WebP 图标上传到public/icon.{ext},本地开发模式用 DataURL 预览 - 远程歌单可视化编辑(
PlaylistEditor.vue):网易云 / QQ 音乐歌单的增删改 + 启用禁用,内联编辑(Dropdown 选服务器 + BaseInput 输入 ID),删除二次确认 - 统计与分析配置(
AnalyticsSettingsEditor.vue+src/utils/site-integrations.ts):Umami(自建 script URL + Website ID)、Google Analytics 4(Measurement ID)、Google 站点验证(Search Console)、自定义 CSS / 自定义 JS 注入,运行时动态 upsert / remove 元素 - 共享组件库(
src/components/):新增Collapse(grid-template-rows: 0fr→1fr动画)、BaseInput(inheritAttrs+$attrs透传)、BaseTextarea、Toast(Teleport + Transition)、Dropdown(outside-click 封装)、ConfirmModal、ToggleSwitch、SettingRange(自动计算进度变量),管理后台与播放器统一复用 - 路由系统:引入
vue-router,AppShell.vue用<router-view>+<Transition mode="out-in">包裹;/为播放器,/admin懒加载管理后台;路由级淡入淡出 - 全局错误处理:
main.ts添加app.config.errorHandler;新增ErrorBoundary.vue用onErrorCaptured包裹router-view,渲染出错时显示降级 UI + 重试按钮,避免白屏 - 服务器端状态页(
server/core/status-pages.ts):环境变量未就绪 / 管理后台禁用时由后端直接返回完整 HTML(内联深色玻璃拟态样式 +escapeHtml防 XSS + 具体密钥生成命令),前端通过 Content-Type 检测后document.write接管页面 - 浏览器能力检测模块(
src/utils/browser.ts):集中管理 Safari 等浏览器兼容性差异——Fullscreen API(标准 + webkit 前缀 + iOS 不支持检测)、Document PiP、AudioContext(webkit 前缀)、Web Animations、iOS 设备检测(含 iPadOS 13+ 桌面 UA)、Safari 浏览器检测、iOS PWA 安装引导 useFullscreen重写:改用browser.ts的能力检测,iOS Safari 不支持 Fullscreen API 时引导用户使用浏览器菜单全屏,而非静默失败- Meting API 鉴权:
services/music.ts的fetchPlaylist支持apiToken参数(查询字符串token) robots.txt:新增public/robots.txt,Disallow: /admin/和/api/- Cloudflare
_routes.json:限制只有/api/*走 Pages Functions,静态资源不产生 Functions 调用计费 - 动态 preconnect:播放器使用构建期公开配置中的
apiEndpoint动态创建<link rel="preconnect">,移除index.html中写死的域名 - 多平台 API 路由:Vercel(
api/[...path].ts)、Cloudflare(functions/api/[[path]].ts)、Netlify(netlify/functions/api.ts+netlify.tomlredirect)三平台统一入口 - 服务端独立 tsconfig:
tsconfig.server.json用于server/目录类型检查 - 开发体验:
package.json新增dev:admin(wrangler pages dev :8788)与dev:full(并行跑前端 + 后端);vite.config.ts新增/api代理到 :8788,测试纳入server/tests/**/*.test.ts - 服务端单元测试:新增
admin-auth、admin-config-validation、crypto、router-security、update-handler、upload-handler共 6 个测试文件 update-from-upstream.ymlworkflow:fork 仓库一键同步上游最新 release,支持 GitHub 代理,rsync 保留用户数据- 主分支验证与发布自动化:main 分支 workflow 负责测试、类型检查、lint、构建、打 tag;仅稳定版自动创建 GitHub Release(
--generate-notes --latest) agent.md规范增补:§3.1 模板内联事件禁止多语句、§3.3.1 共享组件库约束表、§4.2 路由 / 浮层过渡规范、§4.4 accent 半透明色color-mix派生规范
Changed
- 环境变量模型:生产环境需
GH_TOKEN、GH_REPO、CONFIG_ENCRYPTION_KEY三项;GH_BRANCH可选(默认 main)、GITHUB_PROXY可选;密码通过/setup设置(不再使用ADMIN_PASSWORD),Cookie 签名密钥从CONFIG_ENCRYPTION_KEY派生(不再使用ADMIN_SECRET) - 公开配置构建期硬编译:播放器首屏不再请求后端配置 API,构建时从
public/config.json生成公开配置并写入前端 bundle;远程歌单仍按apiEndpoint与歌单 ID 运行时请求 Meting API config-handler默认配置:config.json不存在时返回默认空配置(不再 404),首次部署开箱即用;读取失败、明文配置或解密失败时管理后台会明确报错,避免静默覆盖已有配置src/config/music.ts清理为纯净默认配置:空 apiEndpoint、空数组,不再含个人数据;删除public/config.json(由后台初始化创建)MusicConfig类型扩展:新增siteIcon?、umami?、googleAnalytics?、googleSiteVerification?、customCss?、customJs?字段,与服务端ConfigPayload与validateConfig校验对齐App.vue重构:启动时读取构建期公开配置 →applySiteBrand(动态设置 title + favicon)+applySiteIntegrations(注入统计 / 自定义 CSS/JS);顶栏新增分享按钮;公开配置驱动站点名称显示main.ts重构:挂载AppShell(而非直接App),注册 router 与全局 errorHandler- CSP 四处统一收紧:
index.html/vercel.json/netlify.toml/wrangler.toml统一为script-src 'self' 'unsafe-inline' https:、connect-src 'self' https:,不再写死api.music.abloom.site、googletagmanager.com、cloud.umami.is等具体域名 - 管理后台 accent 颜色派生:
src/admin/下所有rgba(var(--accent-rgb), α)替换为color-mix(in srgb, var(--accent), transparent X%),确保用户自定义--accent时选中态 / 徽章跟随变化 - CORS 头合规:
router.tsOPTIONS 预检的Access-Control-Allow-Origin从*改为反射请求Origin头,添加Vary: Origin(允许 credentials 时 origin 不能为通配符) wrangler.toml:移除[build]段(构建命令改在 Cloudflare Dashboard 配置);修复Permissions-Policy中camera()→camera=()语法错误vercel.json:SPA rewrite 排除/api/*(source: "/((?!api/).*)")netlify.toml:新增functions(esbuild bundler)与/api/*→/.netlify/functions/api/:splatredirectpnpm-workspace.yaml:allowBuilds新增 esbuild / sharp / workerdpackage.json:版本号0.1.1→0.2.0-rc1;新增依赖vue-router、concurrently、npm-run-all2、wrangler
Fixed
usePreloadPool事件监听器泄漏:PreloadSlot新增cleanup字段,clearSlot与超时回调统一调用cleanup()移除canplay/loadeddata/error监听器useLyricsWindow竞态条件:toggleLyricsWindow添加isToggling守卫,快速重复调用时第二次直接返回;waitForDocumentReady的 interval 改为可清理,teardownWindow会清理它useThemeAccentrAF 未清理:onBeforeUnmount取消颜色过渡动画帧useDrawerSheettimer 泄漏:dismissAnimated的setTimeout与watch(active)的双层requestAnimationFrame存储 ID,onBeforeUnmount清理useFocusTrapsetTimeout 未清理:deactivateTrap的setTimeout存储到focusTimer,卸载时清理useBeatAnalyser卸载后多余 rAF:添加isUnmounted标志,await audioContext.resume()后检查,已卸载则不启动新 rAFupload-handler路径穿越:public/../server/core/router.ts可绕过startsWith('public/')检查,改用new URL()规范化路径后校验,拒绝空字节DashboardView模板类型错误:@notify事件处理器在vue-tsc -b下$event[1]推断为string而非'success' | 'error',改为显式参数类型注解- 模板内联事件多语句编译错误:
@click="a(); b()"会被 Prettier 拆分号导致 Vue 编译器报Unexpected token, expected ",",改用方法引用或包装函数 - 管理后台首次访问闪现 LoginView:
checking初始值从false改为true,首帧显示"正在验证"而非 LoginView - 浮层 fixed 定位被困在父容器内:
ConfirmModal/Toast使用<Teleport to="body">避免被backdrop-filter/transform父容器建立包含块 ADMIN_DISABLED拦截管理 API:禁用管理后台时仅状态探针可访问,播放器公开配置已改为构建期硬编译,不再需要运行时配置接口