Skip to content

v0.2.0-rc1

Pre-release
Pre-release

Choose a tag to compare

@abloom25 abloom25 released this 03 Jul 05:17

Added

  • 管理后台:新增完整的 Web 管理后台(前端 SPA + 后端 serverless API),访问 /admin 进入。支持首次初始化、密码登录、配置可视化编辑、文件上传、版本更新检查与触发,无需手动编辑仓库文件即可管理站点
  • /setup 初始化页面:首次部署后访问 /admin 自动进入设置密码流程,密码经 PBKDF2-SHA256(100k 迭代 + 16 字节随机盐)哈希后存储到 public/admin.json,设置后 /setup 自动关闭(先到先得模型,利用 GitHub API 409/422 防并发初始化竞态)
  • 配置文件全文加密:public/config.jsonpublic/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 环境变量:设为 true1 时禁用管理后台,/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.yml workflow 拉取上游 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 透传)、BaseTextareaToast(Teleport + Transition)、Dropdown(outside-click 封装)、ConfirmModalToggleSwitchSettingRange(自动计算进度变量),管理后台与播放器统一复用
  • 路由系统:引入 vue-router,AppShell.vue<router-view> + <Transition mode="out-in"> 包裹;/ 为播放器,/admin 懒加载管理后台;路由级淡入淡出
  • 全局错误处理:main.ts 添加 app.config.errorHandler;新增 ErrorBoundary.vueonErrorCaptured 包裹 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.tsfetchPlaylist 支持 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.toml redirect)三平台统一入口
  • 服务端独立 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-authadmin-config-validationcryptorouter-securityupdate-handlerupload-handler 共 6 个测试文件
  • update-from-upstream.yml workflow: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_TOKENGH_REPOCONFIG_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? 字段,与服务端 ConfigPayloadvalidateConfig 校验对齐
  • 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.sitegoogletagmanager.comcloud.umami.is 等具体域名
  • 管理后台 accent 颜色派生:src/admin/ 下所有 rgba(var(--accent-rgb), α) 替换为 color-mix(in srgb, var(--accent), transparent X%),确保用户自定义 --accent 时选中态 / 徽章跟随变化
  • CORS 头合规:router.ts OPTIONS 预检的 Access-Control-Allow-Origin* 改为反射请求 Origin 头,添加 Vary: Origin(允许 credentials 时 origin 不能为通配符)
  • wrangler.toml:移除 [build] 段(构建命令改在 Cloudflare Dashboard 配置);修复 Permissions-Policycamera()camera=() 语法错误
  • vercel.json:SPA rewrite 排除 /api/*(source: "/((?!api/).*)")
  • netlify.toml:新增 functions(esbuild bundler)与 /api/*/.netlify/functions/api/:splat redirect
  • pnpm-workspace.yaml:allowBuilds 新增 esbuild / sharp / workerd
  • package.json:版本号 0.1.10.2.0-rc1;新增依赖 vue-routerconcurrentlynpm-run-all2wrangler

Fixed

  • usePreloadPool 事件监听器泄漏:PreloadSlot 新增 cleanup 字段,clearSlot 与超时回调统一调用 cleanup() 移除 canplay / loadeddata / error 监听器
  • useLyricsWindow 竞态条件:toggleLyricsWindow 添加 isToggling 守卫,快速重复调用时第二次直接返回;waitForDocumentReady 的 interval 改为可清理,teardownWindow 会清理它
  • useThemeAccent rAF 未清理:onBeforeUnmount 取消颜色过渡动画帧
  • useDrawerSheet timer 泄漏:dismissAnimatedsetTimeoutwatch(active) 的双层 requestAnimationFrame 存储 ID,onBeforeUnmount 清理
  • useFocusTrap setTimeout 未清理:deactivateTrapsetTimeout 存储到 focusTimer,卸载时清理
  • useBeatAnalyser 卸载后多余 rAF:添加 isUnmounted 标志,await audioContext.resume() 后检查,已卸载则不启动新 rAF
  • upload-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:禁用管理后台时仅状态探针可访问,播放器公开配置已改为构建期硬编译,不再需要运行时配置接口