# VoiceHub - 校园广播站点歌系统 这是一个使用Nuxt 4全栈框架开发的现代化校园广播站点歌管理系统。系统提供完整的点歌、投票、排期管理、通知推送、数据分析、权限控制和数据库管理功能,支持多角色权限管理和灵活的系统配置。
[交流群](https://qm.qq.com/cgi-bin/qm/qr?k=5DV4vGlqn82YaNi7a3xW4zjmS8ZUr6cz&jump_from=webapi&authKey=axAl02PMsIVVAwrXij0YUUrOrUTeLpqLipu5XcTvyBUOzeWaOnicBB+fmBwNJs5S) | [使用学校收集表](https://laoshuikaixue.feishu.cn/share/base/form/shrcniUKakpNYP6KH7qrU20qq5e) | [项目宣传片](https://www.bilibili.com/video/BV1B9ArzMEkA) | [赞助支持](#sponsor)
## 项目截图 image image image image image ## 主要功能 ### 🎵 核心功能 - **智能点歌系统**:用户可以点歌或给已有歌曲投票,支持网易云音乐、QQ音乐和哔哩哔哩搜索,可选择期望播出时段 - **多平台登录支持**: - **OAuth 账户系统**:支持通过 GitHub、Casdoor 等 OAuth 提供商快速创建和登录账户 - **直接创建账户**:用户通过 OAuth 认证后可创建新账户,但仍需设置本地用户名和密码 - **账户绑定**:已有账户的用户可将 OAuth 身份绑定到现有账户,实现多平台统一登录 - **WebAuthn 支持**:支持 HarmonyOS Passkey、Windows Hello、生物识别和硬件安全密钥(如 YubiKey)登录 - **双因素认证(2FA)**:支持 TOTP 和邮箱验证,增强账户安全性 - **网易云音乐登录**:支持扫码登录,登录后可搜索个人歌单、收藏及播客电台内容 - **一键添加到歌单**:登录后支持将排期中的网易云音乐歌曲一键添加到个人歌单 - **从歌单投稿**:支持从个人歌单中直接投稿歌曲到系统 - **从最近播放投稿**:支持从最近播放记录中投稿歌曲 - **播客电台投稿**:支持搜索和投稿播客电台内容 - **投稿限额管理**:灵活配置用户投稿限制,支持按时间段、用户角色设置不同的投稿额度,有效控制系统负载 - **歌曲去重功能**:智能识别重复歌曲,优化歌曲库管理,避免重复播放 - **歌曲管理**:按热度排序,避免重复播放,动态URL防止链接过期,支持黑名单管理 - **音乐播放器**:内置音乐播放器,支持进度控制和音质实时切换 - **音质切换**:支持多种音质选择(标准、HQ、无损、Hi-Res等),动态获取最新播放链接 - **音乐下载功能**:支持管理员下载歌曲到本地,提供多种音质选择和批量下载 - **歌曲重播功能**:支持用户对已播放过的歌曲发起重播申请,支持查看申请记录和撤回申请 ### 👥 用户管理 - **用户管理**:管理员添加用户,支持按年级班级分类 - **账户创建方式**: - 管理员直接添加账户 - 用户通过 OAuth 快速创建账户 - 用户通过传统用户名/密码注册 - **权限控制**:多级权限管理,支持普通用户、管理员、超级管理员 - **账户安全**: - bcrypt 密码加密 - 双因素认证(TOTP、邮箱验证) - WebAuthn 支持(HarmonyOS Passkey、生物识别、硬件密钥) - 账户锁定和风险控制 - **身份关联**:支持将多个 OAuth 身份绑定到同一账户,实现统一登录 - **黑名单管理**:支持歌曲和艺术家黑名单,自动过滤不当内容 ### 📅 排期管理 - **排期管理**:管理员可以通过拖拽界面进行歌曲排期和顺序管理 - **排期草稿**:支持保存排期草稿功能,允许管理员分步完成排期安排 - 草稿状态不影响公开展示,可随时修改和完善 - 支持草稿发布为正式排期,确保排期质量 - **播出时段**:灵活配置播出时段,**支持多时段管理** - **排期复制**:支持将某日期的排期完整复制到另一日期,原排期保留不变 - **打印排期**:支持自定义纸张大小、内容选择、编写备注和PDF导出的打印功能 - **学期管理**:管理员可设置当前学期,自动关联点歌记录 - **公开展示**:公开展示歌曲播放排期,按日期分组展示 ### 🔔 通知系统 - **实时通知**:歌曲被选中、投票和系统通知 - **通知设置**:用户可自定义通知偏好,支持独立页面设置 - **批量通知**:管理员可向特定用户群体发送通知 - **社交账号绑定**:支持绑定MeoW等账号,同步推送通知到外部平台 - **验证码验证**:安全的验证码验证机制,支持动态样式反馈 ### 💾 数据管理 - **数据库备份**:完整的数据库备份和恢复功能 - **数据库重置**:支持安全的数据库重置操作,可选择性保留用户数据或完全重置 - **文件导入导出**:支持备份文件的上传、下载和管理 - **数据库自检**:自动数据库验证和修复机制,确保系统稳定性 ### 🎨 用户体验 - **现代UI**:响应式设计,深色主题,流畅的动画效果 - **玻璃态设计**:现代化的视觉效果和交互体验 - **交互反馈**:hover效果,点击反馈,状态变化动画 - **移动端优化**:适配支持移动设备访问,触摸友好的交互设计 ## 技术栈 ### 前端技术 - **Nuxt 4**:Vue.js全栈框架,提供SSR和SPA支持 - **Vue 3**:响应式前端框架,使用Composition API - **TypeScript**:类型安全的JavaScript,提供完整的类型定义 - **UNO CSS**:实用优先的CSS框架,响应式设计 - **Vue Router**:前端路由管理 ### 后端技术 - **Nuxt Server API**:服务端API路由,支持中间件和认证 - **Drizzle ORM**:现代化数据库ORM,提供类型安全的数据库操作和高性能查询 - **Neon Database**:Serverless PostgreSQL数据库,支持自动启停和无缝扩展 - **PostgreSQL**:关系型数据库,支持复杂查询和事务处理 - **Redis**:可选的分布式短期状态服务,仅用于验证码、限流和临时安全状态 - **JWT**:标准JWT认证机制,支持24小时token有效期 - **bcrypt**:密码加密,安全的哈希算法 - **Multer**:文件上传处理,支持多种存储方式 ## 系统架构 系统采用了现代化的 Serverless 全栈架构: - **前端**:使用 Nuxt 4 + Vue 3 组合式API构建响应式用户界面 - **后端**:使用 Nuxt Server API 构建 RESTful API 服务 - **数据库**:使用 Drizzle ORM + Neon Database,提供类型安全和高性能的数据库操作 - **认证**:标准 JWT 认证系统 - **数据读取**:PostgreSQL 是唯一业务数据源,歌曲、排期和用户状态不使用 Redis 缓存 - **部署**:支持 Vercel、Netlify、EdgeOne 等 Serverless 平台一键部署,并提供 Docker、Linux 一键脚本及飞牛 FnOS (fpk安装包) 等多种部署方式 ## 部署指南 ### 一键部署 本项目可以一键部署到Vercel/Netlify/EdgeOne平台: [![Deploy to Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Flaoshuikaixue%2FVoiceHub&env=DATABASE_URL,JWT_SECRET,NODE_ENV&envDefaults=%7B%22NODE_ENV%22%3A%22production%22%7D&envDescription=%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E8%AF%B4%E6%98%8E&envLink=https%3A%2F%2Fgithub.com%2Flaoshuikaixue%2FVoiceHub%23%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E8%AF%B4%E6%98%8E) [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/laoshuikaixue/VoiceHub) [![Deploy to EdgeOne Pages](https://cdnstatic.tencentcs.com/edgeone/pages/deploy.svg)](https://edgeone.ai/pages/new?repository-url=https://github.com/laoshuikaixue/VoiceHub&env=DATABASE_URL,JWT_SECRET&env-description=%E9%9C%80%E8%A6%81%E9%85%8D%E7%BD%AE%E6%95%B0%E6%8D%AE%E5%BA%93%E5%9C%B0%E5%9D%80%E3%80%81JWT%E5%AF%86%E9%92%A5) 在部署过程中,需要输入必要的环境变量: 1. `DATABASE_URL`:PostgreSQL数据库连接地址 2. `JWT_SECRET`:JWT令牌签名密钥 ### Linux 服务器部署 本项目提供了针对 Ubuntu/Debian 服务器的一键部署脚本,支持自动安装 Node.js 22、配置环境变量、安装依赖和构建项目。 **一键命令:** ```bash bash <(curl -sL https://raw.githubusercontent.com/laoshuikaixue/VoiceHub/main/sh/main.sh) ``` 如果你需要 gh-proxy 加速,使用以下命令: ```bash bash <(curl -sL https://gh-proxy.com/https://raw.githubusercontent.com/laoshuikaixue/VoiceHub/main/sh/main.sh) ``` ### Docker 部署 VoiceHub 支持通过 Docker 进行容器化部署,提供了多种部署方式。 #### 方式一:使用 Docker Compose(推荐) 这是最简单的部署方式,会自动创建应用和数据库容器。 ##### 使用预构建镜像 查看 [docker-compose](/docker-compose) 并选择适合的配置文件 ##### 本地构建镜像 1. 克隆项目 ```bash git clone https://github.com/laoshuikaixue/VoiceHub.git cd VoiceHub ``` 2. 修改 docker-compose.yml 中的环境变量 ```yaml environment: - DATABASE_URL=postgresql://user:password@postgres:5432/voicehub # 可能需要 ?sslmode=disable - JWT_SECRET=your-jwt-secret-here # 请修改为强随机字符串 - NODE_ENV=production ``` 3. 启动服务 ```bash docker-compose up -d ``` 4. 访问应用 打开浏览器访问 http://localhost:3000 默认管理员账号: - 用户名:admin - 密码:admin123 #### 方式二:使用预构建镜像 如果你已有 PostgreSQL 数据库,可以直接使用预构建的镜像。 使用 GitHub 镜像源: ```bash docker run -d \ -p 3000:3000 \ -e DATABASE_URL="postgresql://username:password@host:port/database?sslmode=require" \ # 可能需要替换成 ?sslmode=disable -e JWT_SECRET="your-very-secure-jwt-secret-key" \ -e NODE_ENV=production \ --name voicehub \ ghcr.io/laoshuikaixue/voicehub:latest ``` 使用南京大学镜像源: ```bash docker run -d \ -p 3000:3000 \ -e DATABASE_URL="postgresql://username:password@host:port/database?sslmode=require" \ # 可能需要替换成 ?sslmode=disable -e JWT_SECRET="your-very-secure-jwt-secret-key" \ -e NODE_ENV=production \ --name voicehub \ ghcr.nju.edu.cn/laoshuikaixue/voicehub:latest ``` #### 方式三:本地构建镜像 如果需要自定义构建,可以本地构建镜像。 ```bash git clone https://github.com/laoshuikaixue/VoiceHub.git cd VoiceHub # 构建镜像(不使用缓存,确保完全重新构建) docker build --no-cache -t voicehub . # 运行容器 docker run -d \ -p 3000:3000 \ -e DATABASE_URL="postgresql://username:password@host:port/database?sslmode=require" \ # 可能需要替换成 ?sslmode=disable -e JWT_SECRET="your-very-secure-jwt-secret-key" \ -e NODE_ENV=production \ --name voicehub \ voicehub ``` ### Podman 部署 Podman 是一个与 Docker 兼容的容器引擎,无需守护进程,支持 rootless 模式(无需 root 权限)。VoiceHub 的 Docker 配置文件可以直接用于 Podman。 #### 使用 Podman Compose 部署 ```bash podman compose -f docker-compose.yml up -d ``` > **说明**:`podman compose` 完全兼容 `docker-compose.yml` 文件,无需修改配置。 #### rootless 模式 Podman 默认以当前用户身份运行,无需 `sudo`,安全性更高。但容易遇到文件权限问题(特别是挂载卷时)。 ### 飞牛 (FnOS) 部署 VoiceHub 现已支持飞牛 OS (FnOS) 的 `.fpk` 安装包。 - 从 [GitHub Actions](https://github.com/laoshuikaixue/VoiceHub/actions/workflows/build-fpk.yml) 获取最新版本 ### Nix / NixOS VoiceHub 提供了一个 Nix flake,用于构建、开发和在 NixOS 上部署。 #### 前提条件 - [Nix](https://nixos.org/download)(带 flake 支持) - PostgreSQL 数据库 #### NixOS 部署 将 VoiceHub 添加为 flake input: ```nix { inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; voicehub.url = "github:laoshuikaixue/VoiceHub"; }; outputs = { self, nixpkgs, voicehub, ... }: { nixosConfigurations.my-server = nixpkgs.lib.nixosSystem { specialArgs = { inherit voicehub; }; modules = [ voicehub.nixosModules.default ./configuration.nix ]; }; }; } ``` > [!TIP] > 启用 Binary Cache 可大幅加快构建速度,详见下方[使用 Binary Cache 加速构建](#使用-binary-cache-加速构建)。 然后在 NixOS 配置中使用模块,根据数据库管理方式选择对应场景。 ```nix # 场景 A:自动配置本地 PostgreSQL # environmentFile 只需提供 JWT_SECRET,DATABASE_URL 由模块自动构造 { pkgs, inputs, config, ... }: { imports = [ inputs.voicehub.nixosModules.default ]; services.voicehub = { enable = true; database.createLocally = true; environmentFile = config.sops.templates."voicehub-env".path; runDeployScript = true; }; sops.templates."voicehub-env" = { content = '' JWT_SECRET=${config.sops.placeholder."voicehub/jwt-secret"} ''; }; } ``` ```nix # 场景 B:手动管理数据库(Neon / Docker / 远程 PG) # environmentFile 需同时提供 DATABASE_URL 和 JWT_SECRET { pkgs, inputs, config, ... }: { imports = [ inputs.voicehub.nixosModules.default ]; services.voicehub = { enable = true; environmentFile = config.sops.templates."voicehub-env".path; runDeployScript = true; }; sops.templates."voicehub-env" = { content = '' DATABASE_URL=${config.sops.placeholder."voicehub/database-url"} JWT_SECRET=${config.sops.placeholder."voicehub/jwt-secret"} ''; }; } ``` 环境文件 (`sops.templates."voicehub-env".content`) 格式参考: ```env DATABASE_URL=postgresql://voicehub:secret@localhost:5432/voicehub JWT_SECRET=your-very-secure-jwt-secret-key NUXT_PUBLIC_HOST=https://voicehub.example.com ``` 推荐使用 [sops-nix](https://github.com/Mic92/sops-nix) 管理 secrets,避免明文存储在 Nix store 中。 模块会自动设置 `DynamicUser`、`ProtectSystem=strict`、`NoNewPrivileges` 等安全加固。 防火墙默认不开放端口,在配置中启用以允许外部访问: ```nix services.voicehub.openFirewall = true; ``` 应用配置并部署: ```bash sudo nixos-rebuild switch --flake .#my-server ``` 查看服务状态和日志: ```bash systemctl status voicehub journalctl -u voicehub -f ``` 默认监听 `0.0.0.0:3000`,可通过 `services.voicehub.host` 和 `services.voicehub.port` 修改。 #### 使用 Binary Cache 加速构建 VoiceHub CI 会将构建产物推送到 [Cachix](https://cachix.org) binary cache, 下游用户可直接下载预构建的 `pnpmDeps` 和 `voicehub` 包,跳过本地构建。 在你的 flake 中添加 `nixConfig` 以启用: ```nix { nixConfig = { extra-substituters = [ "https://voicehub.cachix.org" ]; extra-trusted-public-keys = [ "voicehub.cachix.org-1:CKw4/RvZy5c0WVpyo5ZyLbJgdpHZ/+epofIwGOeIOhU=" ]; }; inputs = { voicehub.url = "github:laoshuikaixue/VoiceHub"; }; } ``` > [!IMPORTANT] > 请勿通过 `follows` 覆盖 VoiceHub 的 `nixpkgs` input。缓存中的产物使用 > VoiceHub 自带的 nixpkgs 构建,替换后 hash 不同,无法命中缓存。 #### 其他功能 ##### 开发环境 进入开发 shell(自动提供 Node.js、pnpm、PostgreSQL 客户端): ```bash nix develop ``` 然后在 shell 内: ```bash cp .env.example .env # 配置 DATABASE_URL + JWT_SECRET pnpm install pnpm run dev # 启动开发服务器 (port 3000) ``` ##### 构建 ```bash nix build # 产出 result/bin/voicehub ``` 构建产物可以直接运行(需要 `DATABASE_URL` 等环境变量): ```bash DATABASE_URL="postgresql://..." JWT_SECRET="..." ./result/bin/voicehub ``` 或使用附带的环境文件: ```bash nix run .#default --impure ``` > `nix run` 需要设置 `DATABASE_URL` 环境变量,否则会启动失败。 ##### 更新 pnpm 依赖哈希 当 `pnpm-lock.yaml` 更新后,需要同步 `flake.nix` 中的 `pnpmDeps` 哈希。仓库已配置 GitHub Actions,会在 `pnpm-lock.yaml` 或 `flake.nix` 变更时自动计算新哈希并提交回触发分支。 如果需要在本地手动更新,可以先将 `flake.nix` 中 `pnpmDeps.hash` 临时改为空字符串,然后运行: ```bash nix build .#voicehub ``` Nix 会因固定输出哈希不匹配而失败,并输出 `got: sha256-...`,将该值写回 `pnpmDeps.hash` 即可。也可以使用 impure 构建辅助命令(需要网络和已安装的 pnpm): ```bash nix run .#build # 在项目目录中执行,生成 .output 目录 ``` --- ### 本地开发部署 #### 前提条件 - Node.js 22.11+ - PostgreSQL 数据库(推荐使用 Neon) - Redis 数据库(可选;多实例或 Serverless 部署建议配置) #### 快速开始 1. 克隆项目 ```bash git clone https://github.com/laoshuikaixue/VoiceHub.git cd VoiceHub ``` 2. 安装依赖 ```bash pnpm install --frozen-lockfile ``` 3. 配置环境变量 复制 `.env.example` 文件并重命名为 `.env`: ```bash cp .env.example .env ``` 编辑 `.env` 文件,配置必需的环境变量: ```env # 数据库连接地址(必填) DATABASE_URL="postgresql://username:password@host:port/database?sslmode=require" # 可能需要替换成 ?sslmode=disable # JWT 认证密钥(必填) JWT_SECRET="your-very-secure-jwt-secret-key" # 应用运行环境(可选) NODE_ENV=development ``` 4. 初始化数据库 首次运行需要初始化数据库结构: ```bash # 生成数据库迁移文件 pnpm run db:generate # 执行数据库迁移 pnpm run db:migrate ``` 或使用一键部署命令(推荐): ```bash pnpm run deploy ``` 5. 创建管理员账户 系统会在首次部署时自动创建管理员账户。如需手动创建: ```bash pnpm run create-admin ``` 默认管理员账户: - 用户名:admin - 密码:admin123 6. 启动开发服务器 ```bash pnpm run dev ``` 应用将在 http://localhost:3000 启动。 ### 生产环境部署 1. 构建生产版本 ```bash pnpm run build ``` 2. 启动生产服务器 ```bash pnpm run start ``` ### 数据库管理命令 ```bash # 新的数据库初始化 pnpm run init-help # 生成迁移文件 pnpm run db:generate # 执行数据库迁移 pnpm run db:migrate # 推送模式变更到数据库(开发环境) pnpm run db:push # 启动 Drizzle Studio(数据库管理界面) pnpm run db:studio # 清空数据库并重新创建管理员 pnpm run clear-db # 安全迁移(带备份) pnpm run safe-migrate ``` ### 升级与迁移 有关如何升级现有部署和迁移数据,请参阅 [升级指南](UPGRADE.md)。 ## 系统配置 ### 站点配置管理 VoiceHub 提供了完整的站点配置管理功能,支持通过管理后台动态配置系统参数: #### 基本信息配置 - **站点标题**:自定义系统显示名称 - **站点描述**:系统功能描述和介绍 - **站点Logo**:支持上传自定义Logo图片 #### 播放时段管理 - **多时段支持**:支持配置多个播放时段(如午间、晚间) - **时段名称**:自定义时段显示名称 - **开始/结束时间**:精确到分钟的时间控制 - **时段排序**:支持拖拽调整时段显示顺序 #### 通知系统配置 - **通知开关**:控制系统通知功能的启用状态 - **通知类型**:配置不同类型通知的发送规则 - **通知模板**:自定义通知消息的格式和内容 ### 数据库备份与恢复 系统提供了完整的数据备份和恢复解决方案: #### 备份功能 - **完整备份**:包含所有数据表的完整系统备份 - **用户数据备份**:仅备份用户相关数据(用户、歌曲、投票等) - **增量备份**:支持基于时间的增量备份策略 #### 恢复功能 - **合并模式**:将备份数据与现有数据合并,保留现有数据 - **替换模式**:完全替换现有数据(谨慎使用) - **数据验证**:恢复前自动验证备份文件完整性 ### 权限与角色管理 VoiceHub 实现了细粒度的权限控制系统: #### 角色类型 - **超级管理员 (SUPER_ADMIN)**:拥有所有系统权限,包括用户管理、系统配置、数据库管理等 - **管理员 (ADMIN)**:拥有日常管理权限,如用户管理、排期管理、歌曲管理、系统配置等 - **歌曲管理员 (SONG_ADMIN)**:专门负责歌曲相关管理,包括排期管理、歌曲管理、打印排期等 - **普通用户 (USER)**:基本的点歌、投票和查看权限 #### 权限分类 - **内容管理权限**:排期管理、歌曲管理、打印排期等 - **用户管理权限**:创建、编辑、删除用户账户 - **系统管理权限**:通知管理、播放时间管理、学期管理、黑名单管理、站点配置、数据库管理等 #### 权限继承与分配 - **SUPER_ADMIN**:拥有所有权限 - **ADMIN**:拥有除数据库管理外的所有权限 - **SONG_ADMIN**:拥有内容管理相关权限(排期、歌曲、打印) - **USER**:仅拥有基本的点歌和查看权限 #### 权限验证 - 前端基于角色动态显示界面元素和菜单 - 后端API进行严格的权限验证 - 支持页面级和功能级的权限控制 ## 环境变量说明 | 变量名 | 必填 | 说明 | 示例值 | | ---------------------- | ---- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | DATABASE_URL | 是 | PostgreSQL数据库连接字符串 | `postgresql://username:password@host:port/database?sslmode=require` | | JWT_SECRET | 是 | JWT令牌签名密钥,建议使用强随机字符串 | `your-very-secure-jwt-secret-key` | | TRUSTED_CLIENT_IP_HEADERS | 否 | 受信 CDN/反向代理真实 IP 头,多个值用逗号分隔;未配置时使用 TCP 连接地址 | `eo-connecting-ip` | | NODE_ENV | 否 | 运行环境,development或production | `production` | | REDIS_URL | 否 | Redis短期状态服务连接字符串,用于验证码、限流和临时锁定 | `redis://default:password@host:port` | | REDIS_KEY_PREFIX | 否 | Redis键命名空间,多环境共用Redis时应分别设置 | `voicehub:v2:` | | NITRO_PRESET | 否 | Nitro预设 | `vercel` | | NUXT_PUBLIC_HOST | 否 | 显式配置站点外部地址后启用内部 API 的 CORS 来源校验;未配置时仅对回环 Host(反向代理常见场景)跳过,云平台公开 Host 仍校验 | `https://your-app.com` | | NUXT_PUBLIC_SEO_CONFIG | 否 | 用于自定义 PWA/SEO 配置的 JSON 字符串 | `{"title":"VoiceHub校园广播站点歌系统","shortName":"校园广播","description":"校园广播站点歌系统 - 让你的声音被听见","logo":"/images/logo.png"}` | ## OAuth 配置 系统支持通过 OAuth 提供商(如 GitHub、Casdoor、Google 等)快速创建账户和登录: OAuth 运行时配置统一保存在管理员后台数据库中;环境变量仅用于兼容旧部署和后台一键导入,导入后以后台保存值为准。 1. **在管理员后台配置**: - 导航到系统设置 > OAuth 配置 - 配置基础设置: - **OAuth 重定向 URI**:`https://yourdomain.com/api/auth/[provider]/callback` - **OAuth State 密钥**:强随机字符串,用于 state 参数加密 - 启用需要的 OAuth 提供商并填写相应凭证: - GitHub:Client ID / Secret - Casdoor:Server URL / Client ID / Secret / Organization Name - Google:Client ID / Secret - 聚合登陆:AppID / AppKey / 接口地址,并可同时启用 QQ、微信、支付宝等平台 - 第三方 OAuth2:完整的 OAuth 端点和字段映射 2. **OAuth 提供商配置**: 在 OAuth 提供商的开发者控制台配置重定向 URI,确保与后台配置一致 3. **账户创建流程**: - 用户点击 OAuth 登录按钮 - 完成 OAuth 认证后,若身份未关联,用户可选择: - 创建新账户:设置用户名和密码,直接创建新账户 - 绑定现有账户:输入现有用户名和密码进行绑定 - 成功后自动登录 4. **安全特性**: - 所有密码使用 bcrypt 加密 - OAuth 状态参数使用 AES 加密校验 - 绑定令牌有 10 分钟有效期 - 支持账户锁定和风险控制 ## 项目结构 ``` VoiceHub/ ├── .github/ # GitHub 配置目录 │ └── workflows/ # GitHub Actions 工作流 │ ├── build-fpk.yml # FnOS FPK 安装包构建 │ ├── docker-build.yml # Docker 镜像构建 │ ├── docker-postgres.yml # PostgreSQL Docker 镜像构建 │ ├── nix.yml # Nix 构建校验 │ └── update-nix-pnpm-hash.yml # 自动同步 pnpmDeps 哈希 ├── app/ # Nuxt 4 应用主目录 │ ├── app.vue # 应用入口文件 │ ├── assets/ # 静态资源目录 │ │ └── css/ # CSS样式文件 │ │ ├── components.css # 组件样式 │ │ ├── lyric-player.module.css # 歌词播放器样式 │ │ ├── main.css # 主样式文件(含主题引入) │ │ ├── markdown.css # Markdown样式 │ │ ├── mobile-admin.css # 移动端管理样式 │ │ ├── print-fix.css # 打印样式修复 │ │ ├── sf-pro-icons.css # SF Pro图标字体 │ │ ├── theme-protection.css # 主题保护样式(浏览器兼容性) │ │ ├── transitions.css # 过渡动画样式 │ │ ├── variables.css # 全局基础样式与媒体查询 │ │ ├── year-review.css # 年度回顾样式 │ │ └── themes/ # 主题目录 │ │ ├── dark.css # 深色主题设计变量 │ │ ├── light.css # 亮色主题设计变量 │ │ └── ModernLight.css # 现代浅色主题设计变量 │ ├── components/ # Vue组件目录 │ │ ├── Account/ # 账号管理组件 │ │ │ └── SocialBindings.vue # 社交账号绑定(邮箱/MeoW) │ │ ├── Admin/ # 管理员功能组件 │ │ │ ├── ApiKeyManager.vue # API密钥管理 │ │ │ ├── BackupAutoSettings.vue # 自动备份设置 │ │ │ ├── BackupManager.vue # 数据库备份管理 │ │ │ ├── BatchUpdateModal.vue # 批量更新模态框 │ │ │ ├── BlacklistManager.vue # 黑名单管理 │ │ │ ├── CardCodesManager.vue # 点歌券管理 │ │ │ ├── DataAnalysisPanel.vue # 数据分析面板 │ │ │ ├── DatabaseManager.vue # 数据库管理 │ │ │ ├── EmailTemplateManager.vue # 邮件模板管理 │ │ │ ├── MusicSourceController.vue # 音源控制管理 │ │ │ ├── NotificationHistory.vue # 通知发送历史与用户已读明细 │ │ │ ├── NotificationSender.vue # 通知发送管理 │ │ │ ├── OAuthConfigManager.vue # OAuth 配置管理 │ │ │ ├── OAuthBindingsModal.vue # OAuth 绑定详情弹窗 │ │ │ ├── OverviewDashboard.vue # 管理概览仪表板 │ │ │ ├── PlayTimeManager.vue # 播放时间管理 │ │ │ ├── ProviderConfigSection.vue # OAuth 提供商配置组件 │ │ │ ├── RequestTimeManager.vue # 投稿开放时段 & 重复投稿限制管理 │ │ │ ├── ScheduleForm.vue # 排期表单 │ │ │ ├── ScheduleItemPrint.vue # 排期项目打印 │ │ │ ├── ScheduleManager.vue # 排期管理 │ │ │ ├── SchedulePlaylistFilterModal.vue # 排期歌单过滤器 │ │ │ ├── SchedulePrinter.vue # 排期打印功能 │ │ │ ├── ScheduleTablePrint.vue # 排期表格打印功能 │ │ │ ├── SemesterManager.vue # 学期管理 │ │ │ ├── Sidebar.vue # 管理后台侧边栏 │ │ │ ├── SiteConfigManager.vue # 站点配置管理 │ │ │ ├── SmtpManager.vue # SMTP邮件服务管理 │ │ │ ├── SongDownloadDialog.vue # 歌曲下载弹窗 │ │ │ ├── SongManagement.vue # 歌曲管理 │ │ │ ├── SubmissionRemarkDialog.vue # 投稿备注弹窗 │ │ │ ├── UserManager.vue # 用户管理 │ │ │ ├── UserApprovalModal.vue # 用户注册审核弹窗 │ │ │ ├── GradeClassManager.vue # 年级班级配置管理 │ │ │ ├── UserSongsModal.vue # 用户歌曲查看弹窗 │ │ │ └── VotersModal.vue # 投票人员查看弹窗 │ │ ├── AMLL/ # Apple Music-Like Lyrics组件 │ │ │ └── LyricPlayer.vue # AMLL歌词播放器 │ │ ├── Auth/ # 认证相关组件 │ │ │ ├── Providers/ # 第三方登录提供商组件 │ │ │ │ ├── Casdoor/ # Casdoor登录组件 │ │ │ │ │ └── Icon.vue # Casdoor图标 │ │ │ │ ├── GitHub/ # GitHub登录组件 │ │ │ │ │ └── Icon.vue # GitHub图标 │ │ │ │ └── Google/ # Google登录组件 │ │ │ │ └── Icon.vue # Google图标 │ │ │ ├── ChangePasswordForm.vue # 修改密码表单 │ │ │ ├── LoginForm.vue # 登录表单 │ │ │ ├── OAuthBindingCard.vue # OAuth绑定卡片 │ │ │ ├── CaptchaInput.vue # 图形验证码输入组件 │ │ │ ├── TurnstileWidget.vue # Cloudflare Turnstile验证组件 │ │ │ ├── OAuthButtons.vue # OAuth登录按钮组 │ │ │ ├── OAuthQuickLogin.vue # 微信/QQ内置浏览器快速登录按钮 │ │ │ ├── OAuthBindReminderModal.vue # 微信/QQ内置浏览器账号密码登录绑定引导弹窗 │ │ │ ├── TwoFactorSetup.vue # 双重认证设置组件 │ │ │ └── TwoFactorVerify.vue # 双重认证验证组件 │ │ ├── Common/ # 通用组件 │ │ │ └── UserSearchModal.vue # 用户搜索弹窗 │ │ ├── Notifications/ # 通知系统组件 │ │ │ └── NotificationSettings.vue # 通知设置 │ │ ├── Player/ # 播放器相关组件 │ │ │ └── PlayerLyric/ # 播放器歌词子组件 │ │ │ ├── AMLyric.vue # Apple Music风格歌词 │ │ │ └── DefaultLyric.vue # 默认风格歌词 │ │ ├── Songs/ # 歌曲相关组件 │ │ │ ├── AlbumDetailsModal.vue # 网易云音乐专辑详情弹窗 │ │ │ ├── BilibiliEpisodesModal.vue # Bilibili剧集选择弹窗 │ │ │ ├── DuplicateSongModal.vue # 重复歌曲处理对话框 │ │ │ ├── ImportSongsModal.vue # 导入歌曲弹窗 │ │ │ ├── NeteaseLoginModal.vue # 网易云音乐登录弹窗 │ │ │ ├── NeteaseUploadDialog.vue # 网易云云盘上传弹窗 │ │ │ ├── PlaylistSelectionModal.vue # 歌单选择弹窗 │ │ │ ├── PodcastEpisodesModal.vue # 播客节目弹窗 │ │ │ ├── QQMusicLoginModal.vue # QQ音乐登录弹窗 │ │ │ ├── RecentSongsModal.vue # 最近播放弹窗 │ │ │ ├── RequestForm.vue # 点歌表单 │ │ │ ├── ScheduleList.vue # 排期列表展示 │ │ │ └── SongList.vue # 歌曲列表 │ │ ├── UI/ # 通用UI组件 │ │ │ ├── AudioPlayer/ # 音频播放器组件模块 │ │ │ │ ├── AudioElement.vue # 音频元素组件 │ │ │ │ ├── PlayerControls.vue # 播放器控制组件 │ │ │ │ ├── PlayerInfo.vue # 播放器信息组件 │ │ │ │ └── VolumeControl.vue # 播放器音量控制组件 │ │ │ ├── Common/ # 通用UI组件 │ │ │ │ ├── AppSpinner.vue # 通用加载转圈组件 │ │ │ │ ├── CollapsibleSection.vue # 可折叠区域组件 │ │ │ │ ├── CustomSelect.vue # 自定义选择器 │ │ │ │ ├── DataTable.vue # 通用数据表格组件 │ │ │ │ ├── ErrorBoundary.vue # 错误边界组件 │ │ │ │ ├── InputField.vue # 通用输入框组件 │ │ │ │ ├── LoadingState.vue # 加载状态组件 │ │ │ │ ├── MethodCard.vue # 可展开卡片组件(带开关) │ │ │ │ ├── Pagination.vue # 翻页组件 │ │ │ │ ├── PasswordField.vue # 密码输入框组件 │ │ │ │ ├── Popover.vue # 弹出框组件 │ │ │ │ ├── SearchFilter.vue # 搜索过滤组件 │ │ │ │ └── StatCard.vue # 统计卡片组件 │ │ │ ├── AppleMusicLyrics.vue # 类Apple Music风格歌词显示组件 │ │ │ ├── AudioPlayer.vue # 主音频播放器组件 │ │ │ ├── BilibiliIframeModal.vue # Bilibili视频预览弹窗 │ │ │ ├── ConfirmDialog.vue # 确认对话框 │ │ │ ├── Icon.vue # 图标组件 │ │ │ ├── ImportantNotificationModal.vue # 重要通知全屏弹窗 │ │ │ ├── LyricsModal.vue # 全屏歌词模态框组件 │ │ │ ├── MarqueeText.vue # 滚动文本显示组件 │ │ │ ├── Notification.vue # 单个通知组件 │ │ │ ├── NotificationContainer.vue # 通知容器组件 │ │ │ ├── PageTransition.vue # 页面过渡动画 │ │ │ ├── ProgressBar.vue # 进度条组件 │ │ │ ├── AppLoadingScreen.vue # 启动加载屏幕组件 │ │ │ ├── SongComments.vue # 网易云与QQ音乐评论组件 │ │ │ └── WarpCanvas.vue # 动态画布背景组件 │ │ ├── year-review/ # 年度回顾组件 │ │ │ ├── IntroSlide.vue # 开场页 │ │ │ ├── FirstSongSlide.vue # 第一首歌页 │ │ │ ├── ArtistSlide.vue # 艺术家页 │ │ │ ├── StatsSlide.vue # 统计页 │ │ │ ├── MiscSlide.vue # 杂项页 │ │ │ ├── OutroSlide.vue # 结尾页 │ │ │ └── ShareCard.vue # 分享卡片 │ │ └── SiteFooter.vue # 站点页脚 │ ├── composables/ # Vue 3 组合式API │ │ ├── useAdmin.ts # 管理员功能hooks │ │ ├── useAudioPlayer.ts # 音频播放器hooks │ │ ├── useAudioPlayerControl.ts # 音频播放器控制hooks │ │ ├── useAudioPlayerEnhanced.ts # 增强音频播放器hooks │ │ ├── useAudioPlayerSync.ts # 音频播放器同步hooks │ │ ├── useAudioQuality.ts # 音质管理hooks │ │ ├── useAudioVisualizer.ts # 音频可视化hooks │ │ ├── useAuth.ts # 认证功能hooks │ │ ├── useBackgroundRenderer.ts # 背景渲染hooks │ │ ├── useBilibiliPreview.ts # Bilibili视频预览hooks │ │ ├── useChkszSource.ts # ChKSz音源hooks │ │ ├── useErrorHandler.ts # 错误处理hooks │ │ ├── useImportantNotification.ts # 重要通知全局状态与已读处理 │ │ ├── useLocaleText.ts # i18n 文案访问与服务端错误码本地化hooks │ │ ├── useLyricManager.ts # 歌词管理hooks │ │ ├── useLyricPlayer.ts # 类Apple Music风格歌词播放器hooks │ │ ├── useLyrics.ts # 歌词功能hooks │ │ ├── useLyricSettings.ts # 歌词设置hooks │ │ ├── useMediaSession.ts # 媒体会话API hooks │ │ ├── useMusicSources.ts # 音乐源管理hooks │ │ ├── useMusicWebSocket.ts # 音乐WebSocket hooks │ │ ├── useNotifications.ts # 通知功能hooks │ │ ├── useOAuthBindReminder.ts # 微信/QQ绑定引导浏览器存储状态hooks │ │ ├── usePlatformConfig.ts # 平台管理配置hooks │ │ ├── usePermissions.ts # 权限管理hooks │ │ ├── usePasswordStrength.ts # 密码强度检测hooks │ │ ├── useProgress.ts # 进度管理hooks │ │ ├── useProgressEvents.ts # 进度事件hooks │ │ ├── useRequestDedup.ts # 请求去重hooks │ │ ├── useSafeLocale.ts # 安全 i18n 文本包装hooks │ │ ├── useScrollMemory.ts # 页面滚动位置记忆hooks │ │ ├── useSemesters.ts # 学期管理hooks │ │ ├── useSiteConfig.js # 站点配置hooks │ │ ├── useSongPlayer.ts # 歌曲播放器hooks │ │ ├── useSongs.ts # 歌曲管理hooks │ │ ├── useSyncedTime.ts # 服务器时间对时hooks │ │ ├── useTheme.ts # 主题管理(深色/浅色/现代浅色切换) │ │ ├── useThemeImage.ts # 主题图片获取 │ │ ├── useToast.ts # Toast提示hooks │ │ └── useUserFilters.ts # 用户过滤器hooks │ ├── drizzle/ # 数据库相关 │ │ ├── db.ts # 数据库连接 │ │ ├── schema.ts # 数据库模型 │ │ └── migrations/ # 数据库迁移文件 │ │ ├── *.sql # Drizzle 迁移脚本 │ │ └── meta/ # Drizzle 迁移快照 │ ├── layouts/ # 布局组件 │ │ └── default.vue # 默认布局模板 │ ├── middleware/ # 中间件 │ │ └── auth.global.ts # 全局认证中间件 │ ├── pages/ # 页面组件(Nuxt 4路由) │ │ ├── account/ # 账户管理页面 │ │ │ └── index.vue # 账户中心(绑定管理) │ │ ├── auth/ # 认证相关页面 │ │ │ └── error.vue # 认证错误页面 │ │ ├── change-password.vue # 修改密码页面 │ │ ├── dashboard.vue # 用户仪表盘 │ │ ├── forgot-password.vue # 找回密码页面 │ │ ├── index.vue # 首页 │ │ ├── login.vue # 登录页面 │ │ ├── notification-settings.vue # 通知设置页面 │ │ ├── reset-password.vue # 重置密码页面 │ │ └── year-review.vue # 年度回顾页面 │ ├── plugins/ # Nuxt插件 │ │ ├── auth.client.ts # 客户端认证插件 │ │ ├── auth.server.ts # 服务端认证插件 │ │ ├── locale.ts # 语言初始化与SSR同步插件 │ │ └── time-sync.client.ts # 客户端服务器时间对时插件 │ ├── public/ # 静态文件目录 │ │ ├── images/ # 图片资源 │ │ │ └── beian.png # 备案图标 │ │ ├── themes/ # 主题图片(按主题分目录,仅 SVG 随主题切换) │ │ │ ├── ClassicDark/ # 经典深色主题图片 │ │ │ │ ├── logo.svg # SVG格式Logo │ │ │ │ ├── search.svg # 搜索图标 │ │ │ │ └── thumbs-up.svg # 点赞图标 │ │ │ ├── ClassicLight/ # 经典亮色主题图片 │ │ │ │ ├── logo.svg # SVG格式Logo │ │ │ │ ├── search.svg # 搜索图标 │ │ │ │ └── thumbs-up.svg # 点赞图标 │ │ │ └── ModernLight/ # 现代浅色主题图片 │ │ │ ├── logo.svg # SVG格式Logo │ │ │ ├── search.svg # 搜索图标 │ │ │ └── thumbs-up.svg # 点赞图标 │ │ ├── favicon.ico # 网站图标 │ │ └── robots.txt # 搜索引擎爬虫配置 │ ├── workers/ # Web Worker │ │ └── audioEncoderWorker.js # 音频编码 Web Worker │ └── utils/ # 工具函数 │ ├── core/ # 核心工具 │ │ └── security.ts # 安全相关工具 │ ├── data/ # 生成的数据表 │ │ └── cjkT2sMap.ts # 繁→简单字映射表 │ ├── locale/ # 国际化语言资源 │ │ ├── en-US.ts # 英文语言包 │ │ ├── index.ts # 语言状态、切换及回退逻辑 │ │ └── zh-CN.ts # 简体中文语言包 │ ├── lyric/ # 歌词处理工具 │ │ ├── exclude.ts # 歌词排除规则 │ │ ├── lyricFormat.ts # 歌词格式化 │ │ ├── lyricLanguage.ts # 歌词语言识别(CJK 混合上下文) │ │ ├── lyricMatchQuality.ts # 歌词版本一致性检测 │ │ ├── lyricParser.ts # 歌词解析器 │ │ ├── lyricStripper.ts # 歌词清理 │ │ ├── parseLrc.ts # LRC格式解析 │ │ └── qrc-parser.ts # QRC格式解析 │ ├── bilibiliSource.ts # 哔哩哔哩音源 │ ├── debounce.ts # 防抖工具 │ ├── grade-class-input.ts # 年级班级批量输入解析 │ ├── gradeClassWeights.js # 年级排序权重 │ ├── lyricAdapter.ts # 歌词适配器 │ ├── markdown.js # Markdown工具 │ ├── musicSources.ts # 音乐源配置 │ ├── musicUrl.ts # 音乐URL处理 │ ├── platforms.ts # 平台元数据共享(白名单/显示名/图标) │ ├── blacklist.ts # 歌曲类型黑名单候选值共享(语种/曲风) │ ├── sentryUpstreamMusicErrors.ts # Sentry 上游音源错误过滤 │ ├── song-name-normalize.ts # 歌曲名称归一化匹配 │ ├── neteaseApi.ts # 网易云音乐API │ ├── qqUserLibrary.ts # QQ音乐用户资料库(歌单/最近播放) │ ├── oauth-register.ts # OAuth注册工具 │ ├── email-verification.ts # 注册邮箱验证码 │ ├── embedded-browser.ts # 微信/QQ内置浏览器UA检测 │ ├── password-policy.ts # 统一密码策略 │ ├── oauth.ts # OAuth工具 │ ├── autoSchedule.ts # 自动排期算法 │ ├── timeUtils.ts # 时间工具 │ ├── user-archive.ts # 账号归档判定与筛选参数解析 │ ├── webauthn.js # WebAuthn浏览器兼容工具 │ └── url.ts # URL处理工具 ├── server/ # 服务端代码 │ ├── api/ # API路由 │ │ ├── admin/ # 管理员API │ │ │ ├── api-keys/ # API密钥管理API │ │ │ │ ├── [id].delete.ts # 删除API密钥 │ │ │ │ ├── [id].get.ts # 获取API密钥详情 │ │ │ │ ├── [id].put.ts # 更新API密钥 │ │ │ │ ├── index.get.ts # 获取API密钥列表 │ │ │ │ ├── index.post.ts # 创建API密钥 │ │ │ │ ├── logs.get.ts # API使用日志 │ │ │ │ └── permissions.ts # API密钥权限定义 │ │ │ ├── backup/ # 备份管理API │ │ │ │ ├── delete/ # 删除备份子目录 │ │ │ │ │ └── [filename].delete.ts │ │ │ │ ├── download/ # 下载备份子目录 │ │ │ │ │ └── [filename].get.ts │ │ │ │ ├── auto-config.get.ts # 获取自动备份配置 │ │ │ │ ├── auto-config.put.ts # 更新自动备份配置 │ │ │ │ ├── clear.post.ts # 清空备份历史 │ │ │ │ ├── download.get.ts # 下载备份 │ │ │ │ ├── export.post.ts # 创建备份 │ │ │ │ ├── history.get.ts # 获取备份历史 │ │ │ │ ├── history-clear.post.ts # 清空备份历史记录 │ │ │ │ ├── list.get.ts # 获取备份列表 │ │ │ │ ├── restore-chunk.post.ts # 恢复备份分片 │ │ │ │ ├── restore.post.ts # 恢复备份 │ │ │ │ ├── test-email.post.ts # 测试邮件发送 │ │ │ │ ├── test-s3.post.ts # 测试 S3 连接 │ │ │ │ ├── test-telegram.post.ts # 测试 Telegram Bot │ │ │ │ ├── test-webdav.post.ts # 测试 WebDAV 连接 │ │ │ │ └── upload.post.ts # 上传备份文件 │ │ │ ├── blacklist/ # 黑名单管理API │ │ │ │ ├── [id].delete.ts # 删除黑名单项 │ │ │ │ ├── [id].patch.ts # 更新黑名单项 │ │ │ │ ├── index.get.ts # 获取黑名单列表 │ │ │ │ └── index.post.ts # 添加黑名单项 │ │ │ ├── grade-class/ # 年级班级配置API │ │ │ │ ├── [id].delete.ts # 删除配置项 │ │ │ │ ├── by-grade.delete.ts # 按年级删除配置 │ │ │ │ ├── initialize.post.ts # 从现有用户提取初始化 │ │ │ │ ├── index.get.ts # 获取配置列表 │ │ │ │ └── index.post.ts # 新增配置项 │ │ │ ├── card-codes/ # 点歌券管理API │ │ │ │ ├── [id].put.ts # 更新单张点歌券 │ │ │ │ ├── create.post.ts # 创建点歌券 │ │ │ │ ├── delete.post.ts # 删除点歌券 │ │ │ │ ├── export.get.ts # 导出点歌券 │ │ │ │ ├── index.get.ts # 获取点歌券列表 │ │ │ │ ├── redeem-logs.get.ts # 获取点歌券日志 │ │ │ │ └── update.post.ts # 批量更新点歌券 │ │ │ ├── database/ # 数据库管理API │ │ │ │ ├── cleanup.post.ts # 数据库清理 │ │ │ │ ├── performance.get.ts # 数据库性能监控 │ │ │ │ ├── pool-status.get.ts # 连接池状态 │ │ │ │ ├── reset.post.ts # 重置数据库 │ │ │ │ └── status.get.ts # 数据库状态 │ │ │ ├── db-status.get.ts # 数据库状态检查 │ │ │ ├── email-templates/ # 邮件模板管理API │ │ │ │ ├── index.delete.ts # 删除邮件模板 │ │ │ │ ├── index.get.ts # 获取邮件模板列表 │ │ │ │ ├── index.post.ts # 创建/更新邮件模板 │ │ │ │ └── preview.post.ts # 预览邮件模板 │ │ │ ├── fix-sequence.post.ts # 修复数据库序列 │ │ │ ├── notifications/ # 管理员通知API │ │ │ │ ├── history/ # 通知批次明细API │ │ │ │ │ ├── [batchId].delete.ts # 删除通知批次 │ │ │ │ │ ├── [batchId].get.ts # 查询批次用户已读明细 │ │ │ │ │ └── [batchId].put.ts # 修改通知批次 │ │ │ │ ├── history.get.ts # 查询按发送批次归类的通知历史 │ │ │ │ └── send.post.ts # 发送通知 │ │ │ ├── play-times/ # 播放时间管理API │ │ │ │ ├── [id].ts # 播放时间操作 │ │ │ │ ├── index.post.ts # 创建播放时间 │ │ │ │ └── index.ts # 播放时间列表 │ │ │ ├── replay-requests/ # 重播申请管理API │ │ │ │ ├── index.get.ts # 获取重播申请列表 │ │ │ │ └── reject.post.ts # 拒绝重播申请 │ │ │ ├── request-times/ # 点歌时间管理API │ │ │ │ ├── [id].ts # 点歌时间操作 │ │ │ │ ├── index.post.ts # 创建点歌时间 │ │ │ │ └── index.ts # 点歌时间列表 │ │ │ ├── schedule/ # 排期管理API │ │ │ │ ├── bulk-publish.post.ts # 批量发布排期 │ │ │ │ ├── copy.post.ts # 复制排期到指定日期 │ │ │ │ ├── draft.post.ts # 保存排期草稿 │ │ │ │ ├── full.get.ts # 获取完整排期数据(包含草稿) │ │ │ │ ├── move-date.post.ts # 排期日期迁移 │ │ │ │ ├── publish.post.ts # 发布排期草稿 │ │ │ │ ├── remove-all-date.post.ts # 清空某日全部排期 │ │ │ │ ├── remove.post.ts # 移除排期 │ │ │ │ ├── sequence.post.ts # 更新排期顺序 │ │ │ │ ├── song-pool.delete.ts # 从备选池移除歌曲 │ │ │ │ ├── song-pool.get.ts # 获取备选池列表 │ │ │ │ └── song-pool.post.ts # 歌曲加入备选池 │ │ │ ├── schedule.post.ts # 创建排期 │ │ │ ├── semesters/ # 学期管理API │ │ │ │ ├── [id].delete.ts # 删除学期 │ │ │ │ ├── [id].put.ts # 更新学期 │ │ │ │ ├── index.get.ts # 获取学期列表 │ │ │ │ ├── index.post.ts # 创建学期 │ │ │ │ └── set-active.post.ts # 设置活跃学期 │ │ │ ├── smtp/ # SMTP邮件服务API │ │ │ │ ├── reload.post.ts # 重新加载SMTP配置 │ │ │ │ ├── test-connection.post.ts # 测试SMTP连接 │ │ │ │ └── test-email.post.ts # 发送测试邮件 │ │ │ ├── songs/ # 管理员歌曲管理API │ │ │ │ ├── delete.post.ts # 删除歌曲 │ │ │ │ ├── duration.post.ts # 更新歌曲时长 │ │ │ │ ├── mark-played.post.ts # 标记歌曲已播放 │ │ │ │ └── reject.post.ts # 驳回歌曲 │ │ │ ├── stats.get.ts # 统计数据 │ │ │ ├── activities.get.ts # 活动管理API │ │ │ ├── stats/ # 详细统计API │ │ │ │ ├── active-users.get.ts # 活跃用户统计 │ │ │ │ ├── realtime.get.ts # 实时统计 │ │ │ │ ├── semester-comparison.get.ts # 学期对比统计 │ │ │ │ ├── top-songs.get.ts # 热门歌曲统计 │ │ │ │ ├── trends.get.ts # 趋势分析 │ │ │ │ └── user-engagement.get.ts # 用户参与度统计 │ │ │ ├── system-settings/ # 系统设置API │ │ │ │ ├── env-oauth-import.post.ts # 导入环境变量OAuth配置 │ │ │ │ ├── env-oauth.get.ts # 获取环境变量OAuth配置 │ │ │ │ ├── index.post.ts # 更新系统设置 │ │ │ │ ├── index.ts # 获取系统设置 │ │ │ │ └── secretMask.ts # 密钥脱敏工具 │ │ │ └── users/ # 用户管理API │ │ │ ├── [id]/ # 用户详情操作子目录 │ │ │ │ ├── reset-password.post.ts # 重置用户密码 │ │ │ │ ├── songs.get.ts # 获取用户点歌记录 │ │ │ │ ├── status-logs.get.ts # 获取用户状态变更日志 │ │ │ │ └── status.put.ts # 更新用户状态 │ │ │ ├── [id].approval.post.ts # 注册审核 │ │ │ ├── [id].delete.ts # 删除用户 │ │ │ ├── [id].put.ts # 更新用户 │ │ │ ├── [id].get.ts # 用户详情 │ │ │ ├── batch-grade-update.post.ts # 批量年级更新 │ │ │ ├── batch-status.put.ts # 批量状态更新 │ │ │ ├── batch-update.post.ts # 批量更新用户 │ │ │ ├── batch.post.ts # 批量操作用户 │ │ │ ├── index.get.ts # 获取用户列表 │ │ │ ├── index.post.ts # 创建用户 │ │ │ ├── index.ts # 用户管理 │ │ │ ├── options.ts # 用户管理选项 │ │ │ └── status-logs.get.ts # 用户状态日志 │ │ ├── api-enhanced/ # 网易云音乐API │ │ │ └── netease/ # 网易云增强接口代理 │ │ │ └── [...path].ts # 转发网易云API请求 │ │ ├── auth/ # 认证API │ │ │ ├── captcha.get.ts # 图形验证码 │ │ │ ├── oauth-register-options.get.ts # OAuth注册选项 │ │ │ ├── grade-class-options.get.ts # 年级班级选项 │ │ │ ├── email-code.post.ts # 注册邮箱验证码发送 │ │ │ ├── 2fa/ # 2FA验证API │ │ │ │ ├── send-email.post.ts # 发送2FA验证邮件 │ │ │ │ └── verify.post.ts # 验证2FA代码 │ │ │ ├── webauthn/ # WebAuthn 相关 API │ │ │ │ ├── login/ # 登录验证 │ │ │ │ │ ├── options.post.ts # 获取登录 Challenge │ │ │ │ │ └── verify.post.ts # 验证登录签名 │ │ │ │ ├── register/ # 设备注册 │ │ │ │ │ ├── options.get.ts # 获取注册 Challenge │ │ │ │ │ └── verify.post.ts # 验证注册签名 │ │ │ │ └── rename.post.ts # 重命名设备 │ │ │ ├── [provider]/ # OAuth提供商路由 │ │ │ │ ├── callback.get.ts # OAuth回调处理 │ │ │ │ └── index.get.ts # OAuth授权跳转 │ │ │ ├── bind.post.ts # 绑定社交账号 │ │ │ ├── change-password.post.ts # 修改密码 │ │ │ ├── forgot-password.post.ts # 找回密码 │ │ │ ├── identities.get.ts # 获取已绑定身份列表 │ │ │ ├── login.post.ts # 用户登录 │ │ │ ├── logout.post.ts # 用户登出 │ │ │ ├── oauth-register.post.ts # OAuth用户注册 │ │ │ ├── register.post.ts # 用户名密码注册 │ │ │ ├── reset-password.post.ts # 重置密码 │ │ │ ├── set-initial-password.post.ts # 设置初始密码 │ │ │ ├── unbind.post.ts # 解绑社交账号 │ │ │ └── verify.get.ts # 验证Token并获取用户信息 │ │ ├── bilibili/ # Bilibili相关API │ │ │ ├── playurl.get.ts # 获取播放链接 │ │ │ └── search.get.ts # Bilibili视频搜索 │ │ ├── blacklist/ # 黑名单API │ │ │ └── check.post.ts # 检查黑名单 │ │ ├── card-codes/ # 点歌券API │ │ │ └── validate.post.ts # 验证点歌券可用性 │ │ ├── meow/ # MeoW账号绑定API │ │ │ ├── bind.post.ts # 绑定MeoW账号 │ │ │ └── unbind.post.ts # 解绑MeoW账号 │ │ ├── music/ # 音乐相关API │ │ │ ├── resolve-url.post.ts # 音乐播放链接统一解析 │ │ │ ├── state.post.ts # 音乐状态管理 │ │ │ └── websocket.ts # 音乐WebSocket连接 │ │ ├── native-api/ # 原生音乐API │ │ │ ├── comment/ # 评论API │ │ │ │ └── tx.get.ts # QQ音乐评论 │ │ │ ├── lyric/ # 歌词API │ │ │ │ ├── mg.get.ts # 咪咕音乐歌词 │ │ │ │ └── tx.get.ts # 腾讯音乐歌词 │ │ │ ├── migu/ # 咪咕音乐API │ │ │ │ └── playurl.get.ts # 获取咪咕音乐播放链接 │ │ │ ├── qq/ # QQ音乐账号API │ │ │ │ ├── avatar.get.ts # 获取QQ音乐头像 │ │ │ │ ├── check-cookie.post.ts # 校验QQ音乐登录Cookie有效性 │ │ │ │ ├── check-login.post.ts # 检查扫码登录情况 │ │ │ │ ├── check-wx-login.post.ts # 检查微信扫码登录状态 │ │ │ │ ├── login-qr.get.ts # 获取QQ登录二维码 │ │ │ │ └── login-qr-wx.get.ts # 获取微信登录二维码 │ │ │ │ ├── playlist-songs.post.ts # 获取QQ音乐歌单内歌曲 │ │ │ │ └── playlists.post.ts # 获取用户创建与收藏的歌单 │ │ │ └── search/ # 搜索API │ │ │ ├── mg.get.ts # 咪咕音乐搜索 │ │ │ ├── tx.get.ts # 腾讯音乐搜索 │ │ │ └── wy.get.ts # 网易云音乐搜索 │ │ ├── platform-config/ # 平台管理公开API │ │ │ └── index.get.ts # 获取平台启用与排序配置 │ │ ├── notifications/ # 通知系统API │ │ │ ├── [id]/ # 通知操作子目录 │ │ │ │ └── read.post.ts # 标记通知已读 │ │ │ ├── [id].delete.ts # 删除通知 │ │ │ ├── clear-all.delete.ts # 清空所有通知 │ │ │ ├── important.get.ts # 获取最早一条未读重要通知 │ │ │ ├── index.ts # 通知列表 │ │ │ ├── meow/ # MeoW通知API │ │ │ │ ├── send-verification.post.ts # 发送验证码 │ │ │ │ └── test.post.ts # 测试通知 │ │ │ ├── read-all.post.ts # 标记所有已读 │ │ │ ├── settings.post.ts # 更新通知设置 │ │ │ └── settings.ts # 获取通知设置 │ │ ├── open/ # 开放API(无需认证) │ │ │ ├── card-codes/ # 点歌券开放API │ │ │ │ └── delete.post.ts # 删除点歌券(兼容不支持 DELETE body 的代理) │ │ │ ├── card-codes.delete.ts # 删除点歌券 │ │ │ ├── card-codes.get.ts # 获取点歌券列表 │ │ │ ├── card-codes.patch.ts # 更新点歌券 │ │ │ ├── card-codes.post.ts # 创建点歌券 │ │ │ ├── songs/ # 歌曲相关开放API │ │ │ │ ├── mark-played.post.ts # 标记歌曲已播放(供外部调用) │ │ │ │ └── request.post.ts # 使用个人集成令牌投稿歌曲 │ │ │ ├── backup/ # 自动备份开放API │ │ │ │ ├── auto.post.ts # 触发自动备份(需 API Key) │ │ │ │ └── status/ # 备份状态查询 │ │ │ │ └── [id].get.ts # 查询备份任务状态 │ │ │ ├── schedules.get.ts # 获取公开排期 │ │ │ └── songs.get.ts # 获取公开歌曲列表 │ │ ├── play-times/ # 播放时间API │ │ │ └── index.ts # 播放时间管理 │ │ ├── request-times/ # 点歌时间API │ │ │ └── index.ts # 点歌时间管理 │ │ ├── progress/ # 进度条API │ │ │ ├── events.ts # 进度事件 │ │ │ └── id.ts # 进度ID管理 │ │ ├── proxy/ # 代理服务API │ │ │ └── image.get.ts # 图片代理(解决HTTP/HTTPS混合内容及跨域问题) │ │ ├── semesters/ # 学期API │ │ │ ├── current.get.ts # 获取当前学期 │ │ │ └── options.get.ts # 获取学期选项 │ │ ├── site-config.get.ts # 站点配置API │ │ ├── songs/ # 歌曲相关API │ │ │ ├── [id]/ # 歌曲详情操作 │ │ │ │ ├── update.put.ts # 更新歌曲信息 │ │ │ │ └── voters.get.ts # 获取投票人员 │ │ │ ├── collaborators/ # 联合投稿管理 │ │ │ │ └── reply.post.ts # 处理联合投稿邀请 │ │ │ ├── add.post.ts # 添加歌曲 │ │ │ ├── count.get.ts # 歌曲统计 │ │ │ ├── import.post.ts # 导入歌曲 │ │ │ ├── index.get.ts # 歌曲列表 │ │ │ ├── public.get.ts # 公开歌曲列表 │ │ │ ├── check-restriction.post.ts # 检查重复投稿限制 │ │ │ ├── request.post.ts # 点歌请求 │ │ │ ├── replay.post.ts # 提交重播申请 │ │ │ ├── replay.delete.ts # 撤回重播申请 │ │ │ ├── submission-status.get.ts # 投稿状态 │ │ │ ├── vote.post.ts # 投票 │ │ │ └── withdraw.post.ts # 撤回歌曲 │ │ ├── sys/ # 系统辅助API │ │ │ └── time.get.ts # 获取校准后的服务器时间 │ │ ├── system/ # 系统API │ │ │ ├── instance.get.ts # 实例信息 │ │ │ ├── location.get.ts # 获取系统位置信息 │ │ │ ├── reconnect.post.ts # 重连数据库 │ │ │ └── status.get.ts # 系统状态 │ │ ├── user/ # 用户相关API │ │ │ ├── 2fa/ # 2FA管理API │ │ │ │ ├── disable.post.ts # 关闭双重认证 │ │ │ │ ├── enable.post.ts # 开启双重认证 │ │ │ │ └── generate.post.ts # 生成双重认证密钥 │ │ │ ├── api-keys/ # 个人集成令牌API │ │ │ │ ├── [id].delete.ts # 删除个人集成令牌 │ │ │ │ ├── [id]/logs.get.ts # 获取个人集成令牌调用日志 │ │ │ │ ├── index.get.ts # 获取个人集成令牌列表 │ │ │ │ └── index.post.ts # 创建个人集成令牌 │ │ │ ├── email/ # 用户邮箱管理 │ │ │ │ ├── bind.post.ts # 绑定邮箱 │ │ │ │ ├── resend-verification.post.ts # 重发验证邮件 │ │ │ │ ├── send-code.post.ts # 发送验证码 │ │ │ │ ├── unbind.post.ts # 解绑邮箱 │ │ │ │ └── verify-code.post.ts # 验证邮箱验证码 │ │ │ ├── avatar.post.ts # 设置 OAuth 头像来源 │ │ │ ├── sessions.delete.ts # 撤销登录会话 │ │ │ ├── sessions.get.ts # 获取登录会话列表 │ │ │ └── year-review.get.ts # 获取年度回顾数据 │ │ └── users/ # 用户API │ │ ├── social-accounts/ # 社交账号管理 │ │ │ ├── meow.delete.ts # 删除MeoW绑定 │ │ │ └── meow.post.ts # MeoW账号操作 │ │ ├── search.get.ts # 搜索用户 │ │ └── social-accounts.get.ts # 获取社交账号 │ ├── card-codes/ # 点歌券相关 │ │ └── statuses.ts # 点歌券状态枚举定义 │ ├── config/ # 服务端配置 │ │ └── constants.ts # 风控阈值与时间窗口常量 │ ├── error.ts # 全局错误处理 │ ├── middleware/ # 服务端中间件 │ │ ├── 00.request-id.ts # 请求ID注入中间件 │ │ ├── api-auth.ts # API认证中间件 │ │ ├── api-cors.ts # API跨域中间件 │ │ └── auth.ts # 认证中间件 │ ├── plugins/ # 服务端插件 │ │ ├── 00.sentry.ts # Sentry错误追踪插件 │ │ ├── 01.pre-warm-ssr.ts # SSR预热插件 │ │ ├── error-handler.ts # 错误处理插件 │ │ ├── redis-lifecycle.ts # Redis短期状态连接生命周期 │ │ └── statistics-code.ts # 站点统计代码注入插件 │ ├── services/ # 业务服务层 │ │ ├── apiLogService.ts # API日志服务 │ │ ├── autoBackupService.ts # 自动备份服务 │ │ ├── cardCodeDeleteService.ts # 点歌券删除服务 │ │ ├── cardCodeLifecycleService.ts # 点歌券生命周期服务 │ │ ├── durationValidationService.ts # 歌曲时长校验与补齐服务 │ │ ├── meowNotificationService.ts # MeoW通知服务 │ │ ├── notificationService.ts # 通知服务 │ │ ├── oauthConfigService.ts # OAuth提供商配置与状态服务 │ │ ├── passwordSecurityService.ts # 密码操作审计与限流服务 │ │ ├── securityService.ts # 安全服务 │ │ ├── songRequestService.ts # 点歌投稿服务 │ │ ├── smtpService.ts # SMTP邮件服务 │ │ └── userService.ts # 用户服务 │ ├── utils/ # 服务端工具函数 │ │ ├── admin-password-policy.ts # 管理员重置密码基础校验策略 │ │ ├── apiError.ts # 统一错误码抛出助手 createApiError │ │ ├── apiKeyUtils.ts # API Key生成、哈希与校验 │ │ ├── auth.ts # 认证工具函数 │ │ ├── auth-route-policy.ts # 强制改密期间的接口访问策略 │ │ ├── bilibiliWbi.ts # Bilibili WBI签名工具 │ │ ├── captcha.ts # 图形验证码生成工具 │ │ ├── captchaStore.ts # 分布式短期状态与验证码哈希存储 │ │ ├── card-code-delete-handler.ts # 点歌券删除开放API处理器 │ │ ├── database-health.ts # 数据库健康检查 │ │ ├── database-manager.ts # 数据库管理工具 │ │ ├── geo.ts # 地理位置工具 │ │ ├── grade-class-core.ts # 年级班级选项纯函数 │ │ ├── grade-class-options.ts # 年级班级选项提取工具 │ │ ├── initial-password-policy.ts # 初始密码设置状态策略 │ │ ├── important-notification-policy.ts # 重要通知发送与展示策略 │ │ ├── notification-history-policy.ts # 通知批次引用、筛选与分页策略 │ │ ├── instance-id.ts # 实例ID管理工具 │ │ ├── ip-utils.ts # IP地址工具 │ │ ├── jwt-enhanced.ts # JWT工具 │ │ ├── log-manager.ts # 日志管理工具 │ │ ├── native_common.ts # 原生API通用工具 │ │ ├── native_tx.ts # 腾讯音乐原生API │ │ ├── native_wy.ts # 网易云音乐原生API │ │ ├── oauth-providers.ts # OAuth提供商类型与纯函数工具 │ │ ├── oauth-strategies.ts # OAuth策略配置 │ │ ├── oauth-token.ts # OAuth令牌工具 │ │ ├── oauth-identity.ts # OAuth身份绑定与头像同步工具 │ │ ├── oauth.ts # OAuth通用工具 │ │ ├── permissions.js # 权限系统配置 │ │ ├── qqComment.ts # QQ音乐评论数据归一化 │ │ ├── qq_music_sdk.ts # QQ音乐SDK调用封装 │ │ ├── rateLimiter.ts # 请求速率限制工具 │ │ ├── register-validation.ts # 注册校验纯函数 │ │ ├── registration-notify.ts # 注册结果通知 │ │ ├── redis.ts # 可选Redis连接与命名空间工具 │ │ ├── request-utils.ts # 请求处理通用工具 │ │ ├── requireSongAdmin.ts # 歌曲管理员权限校验工具 │ │ ├── song-duration-policy.ts # 歌曲时长归一化与补齐/清空决策 │ │ ├── song-name-normalize.ts # 歌曲名称标准化匹配工具 │ │ ├── song-type-resolver.ts # 歌曲类型(语种/曲风)解析工具 │ │ ├── songDurationFetcher.ts # 外部平台歌曲时长获取工具 │ │ ├── restoreScheduleSongPool.ts # 排期备选池恢复工具 │ │ ├── s3Client.ts # S3 兼容存储客户端(AWS Signature V4) │ │ ├── scheduleReplayBinding.ts # 排期发布时履行并绑定重播申请 │ │ ├── scheduleSongPool.ts # 排期备选池统计工具 │ │ ├── serverTime.ts # 服务器时间工具 │ │ ├── sequence-sync.ts # 自增序列同步工具 │ │ ├── siteUtils.ts # 站点工具函数 │ │ ├── studentMask.ts # 学生隐私工具 │ │ ├── submissionLimit.ts # 投稿限额工具 │ │ ├── submission-restriction-policy.ts # 重复投稿限制模式判定 │ │ ├── system-settings-defaults.ts # 系统设置默认值 │ │ ├── system-settings-helper.ts # 系统设置读取与强制改密判断工具 │ │ ├── theme-config.ts # 主题配置校验与解析工具 │ │ ├── telemetry.ts # 遥测与错误追踪工具 │ │ ├── user.ts # 用户相关工具函数 │ │ ├── user-archive.ts # 账号归档判定转发导出(权威实现在 app/utils) │ │ ├── user-avatar.ts # OAuth 头像来源解析工具 │ │ ├── webauthn-config.ts # WebAuthn配置工具 │ │ └── webauthn-token.ts # WebAuthn令牌工具 │ └── tsconfig.json # 服务端TypeScript配置 ├── scripts/ # 构建、部署与数据库维护脚本 │ ├── build.js # 输出环境变量解析结果并执行 Nuxt 构建 │ ├── check-deploy.js # 部署前检查 │ ├── clear-database.js # 清空数据库 │ ├── create-admin.js # 创建管理员账户 │ ├── db-sync.js # 数据库同步 │ ├── deploy.js # 一键部署脚本 │ ├── drizzle/ # Drizzle 迁移辅助脚本 │ │ └── migrations/ │ ├── netlify-build.js # Netlify 构建脚本 │ ├── postinstall.js # 安装后脚本 │ ├── redis-scan-legacy.js # 旧Redis业务缓存键dry-run扫描工具 │ ├── reset-database.js # 重置数据库 │ └── safe-migrate.js # 安全迁移(带备份) ├── tests/ # 自动化测试 │ └── server/ # 服务端策略与安全测试 │ ├── auth-route-policy.test.ts # 强制改密路由策略测试 │ ├── cors-origin-policy.test.ts # CORS 来源协议匹配测试 │ ├── important-notification-policy.test.ts # 重要通知策略测试 │ ├── initial-password-policy.test.ts # 初始密码状态策略测试 │ ├── notification-history-policy.test.ts # 通知批次引用、筛选与分页策略测试 │ ├── oauth-state-cookie.test.ts # OAuth state Cookie 安全测试 │ ├── password-policy.test.ts # 密码策略测试 │ ├── qq-comment-normalize.test.ts # QQ音乐评论归一化测试 │ ├── song-duration-policy.test.ts # 歌曲时长归一化与补齐决策测试 │ ├── submission-restriction-policy.test.ts # 重复投稿限制模式判定测试 │ ├── token-version-policy.test.ts # 令牌版本策略测试 │ ├── user-archive.test.ts # 账号归档筛选参数解析测试 │ └── user-avatar.test.ts # OAuth 头像来源解析测试 ├── types/ # TypeScript类型定义 │ ├── global.d.ts # 全局类型定义 │ └── index.ts # 通用类型定义 ├── .env.example # 环境变量示例文件 ├── .gitignore # Git忽略文件配置 ├── .vercelignore # Vercel部署忽略文件 ├── docker-compose/ # Docker Compose配置目录 ├── docker-compose.yml # Docker编排文件 ├── Dockerfile # Docker构建文件 ├── Dockerfile-postgres # PostgreSQL Docker构建文件 ├── drizzle.config.ts # Drizzle配置文件 ├── edgeone.json # EdgeOne Pages 部署配置 ├── eslint.config.mjs # ESLint 配置 ├── flake.lock # Nix flake锁定文件 ├── flake.nix # Nix构建与NixOS模块配置 ├── fnos/ # 飞牛 OS 安装包相关配置 ├── LICENSE # 开源许可证文件 ├── netlify.toml # Netlify部署配置 ├── nuxt.config.ts # Nuxt 4主配置文件 ├── package.json # Node.js项目配置和依赖 ├── pnpm-lock.yaml # pnpm 依赖锁定文件 ├── pnpm-workspace.yaml # pnpm 依赖构建许可配置 ├── README.md # 项目说明文档 ├── sh/ # 一键部署脚本目录 ├── tsconfig.json # TypeScript配置文件 ├── UPGRADE.md # 升级指南 └── vercel.json # Vercel部署配置 ``` ### 目录说明 #### 核心目录 (app/) - **`app/components/`**: Vue组件库,按功能模块组织 - **`Account/`**: 账号管理组件(社交账号绑定等) - **`Admin/`**: 管理后台组件(排期、用户、数据分析等) - **`AMLL/`**: Apple Music-Like Lyrics歌词播放器组件 - **`Auth/`**: 认证相关组件(登录、OAuth绑定等) - **`Common/`**: 通用业务组件 - **`Notifications/`**: 通知系统组件 - **`Player/`**: 播放器相关组件 - **`Songs/`**: 歌曲相关组件(点歌、导入、歌单等) - **`UI/`**: 通用UI组件(播放器、对话框、进度条等) - **`year-review/`**: 年度回顾功能组件 - **`app/pages/`**: 页面组件,Nuxt 4 自动路由 - **`app/composables/`**: Vue 3组合式API,业务逻辑复用 - **`useTheme.ts`**: 主题管理 composable,支持深色/浅色主题切换与 localStorage 持久化 - **`app/drizzle/`**: Drizzle ORM配置、数据库连接和迁移文件 #### 配置目录 (app/) - **`app/assets/css/`**: 样式文件,支持CSS变量和主题 - **`app/plugins/`**: Nuxt插件,扩展框架功能 - **`app/middleware/`**: 中间件,处理路由和认证 - **`app/utils/`**: 客户端工具函数 - **`core/`**: 核心工具(安全等) - **`lyric/`**: 歌词处理工具集 #### 服务端目录 (server/) - **`server/api/`**: 服务端API,RESTful接口设计 - **`admin/`**: 管理员专用API(用户、排期、统计等) - **`auth/`**: 认证相关API - **`songs/`**: 歌曲管理API - **`notifications/`**: 通知系统API - **`open/`**: 公共API(无需认证) - **`server/config/`**: 服务端配置(常量、环境配置等) - **`server/middleware/`**: 服务端中间件(认证、日志等) - **`server/plugins/`**: 服务端插件(错误处理等) - **`server/services/`**: 业务逻辑服务层 - **`server/utils/`**: 服务端工具函数 #### 静态资源 - **`public/`**: 静态文件 - **`public/images/`**: 备案图标等与主题无关的图片 - **`public/assets/`**: 不随主题切换的公共资源(如 `logo.png`) - **`public/themes/{ClassicDark,ClassicLight,ModernLight}/`**: 随主题切换的 SVG 图片资源(Logo、搜索图标、点赞图标) ### 主题系统 VoiceHub 采用 CSS 变量驱动的主题架构,支持深色与亮色两种主题模式,包含经典与现代两种设计风格。主题文件按功能模块组织在 `app/assets/css/themes/` 目录下: #### 目录结构 ``` app/assets/css/themes/ ├── ClassicDark.css # 深色主题设计变量(:root[data-theme="ClassicDark"]) ├── ClassicLight.css # 亮色主题设计变量(:root[data-theme="ClassicLight"]) └── ModernLight.css # 现代浅色主题设计变量(:root[data-theme="ModernLight"]) ``` #### 架构说明 - **CSS 变量分离**:每个主题在独立的 CSS 文件中定义 `:root[data-theme="主题名"]` 选择器,包含所有设计变量(颜色、背景、文字、边框等) - **引入方式**:`app/assets/css/main.css` 通过 `@import` 引入主题文件,各主题按需引入 - **切换机制**:通过 `useTheme()` composable 和 `theme.client.ts` 插件实现主题切换与 localStorage 持久化 #### 主题图片管理 主题相关的图片资源:**SVG 图片**(随主题切换,存放在 `public/themes/{ClassicDark,ClassicLight,ModernLight}/`) - **SVG 主题图片存放位置**:`public/themes/ClassicLight/`(亮色主题)、`public/themes/ClassicDark/`(深色主题)和 `public/themes/ModernLight/`(现代浅色主题) - **管理方式**:SVG 图片通过 `useThemeImage()` composable 统一获取,PNG 图片直接使用静态路径 - **使用示例**: ```vue ``` - **同步规则**:新增主题时需在 `app/assets/css/themes/` 下创建对应的 CSS 文件,**同时**在 `public/themes/` 下创建对应的图片目录并放入 SVG 文件。如果只新增 CSS 主题而未同步图片,组件将无法加载主题图片。 #### 自定义主题 如需新增自定义主题: 1. 在 `app/assets/css/themes/` 下创建新主题,如 `ocean.css`,使用 `:root[data-theme="ocean"]` 作为根选择器定义所有设计变量 3. 在 `app/assets/css/main.css` 中添加对应的 `@import` 语句 4. 在 `app/composables/useTheme.ts` 中将新主题 ID 加入 `Theme` 类型和 `THEMES` 数组 5. 在 `public/themes/` 下创建对应主题的 SVG 图片文件夹(如 `public/themes/ocean/`),**放入与 `ClassicDark/` 和 `ClassicLight/` 相同的 SVG 图片**,否则使用该主题时组件无法加载主题图片。 ## 使用说明 ### 普通用户 1. 访问主页,查看当前排期 2. 注册/登录账号 3. 在仪表盘中点歌或给喜欢的歌曲投票 - 支持搜索网易云音乐、QQ音乐和哔哩哔哩平台 - 可以试听歌曲并选择音质 - 支持给已有歌曲投票 - **网易云音乐登录功能**: - 扫码登录网易云音乐账号 - 登录后可一键添加当前排期歌曲到个人歌单 - 支持从个人歌单中直接投稿歌曲 - 支持从最近播放记录中投稿歌曲 - 可搜索并投稿播客和电台内容 4. 使用内置播放器播放歌曲 - 支持多种音质切换(标准、HQ、无损、Hi-Res等) - 实时切换音质并保持播放进度 5. 查看通知中心获取歌曲状态更新 ### 管理员 1. 使用管理员账号登录(默认账号:admin,密码:admin123) 2. 进入管理后台,选择相应功能标签 3. **排期管理**:可以看到左侧"待排歌曲"和右侧"播放顺序" - 通过拖拽将歌曲从左侧添加到右侧的排期列表 - 可以在右侧拖拽调整歌曲播放顺序 - 支持播出时段管理,可设置不同时段的播放安排 - **草稿功能**:支持保存排期草稿,允许管理员先保存未完成的排期安排 - 点击"保存草稿"按钮保存当前排期为草稿状态 - 草稿不会影响公开展示的排期,可以随时修改 - 点击"保存并发布"按钮将草稿发布为正式排期 - 点击"保存顺序"按钮保存排期 4. **打印排期**:专业的排期打印和导出功能 - 选择纸张大小(A4、A3、Letter、Legal)和页面方向 - 自定义显示内容:歌曲封面、歌名、歌手、投稿人、热度等 - 快捷日期选择:今天、明天、本周、下周 - 智能分组显示:按日期分组,有多个播出时段时自动按时间排序 - 实时预览:所见即所得的打印预览 - PDF导出:支持导出高质量PDF文件,自动处理跨域图片 5. **歌曲管理**:查看和管理所有歌曲 - 支持播放歌曲并实时切换音质 - 动态获取最新的音乐播放链接 - 提供歌曲下载功能,支持批量下载管理 - 批量更新歌曲信息和状态 6. **数据分析**:查看系统使用统计和数据分析 - 实时统计数据:用户活跃度、歌曲热度、投票趋势 - 学期对比分析:不同学期的数据对比 - 用户参与度分析:用户行为和参与度统计 - 趋势分析:系统使用趋势和预测 7. **数据库管理**:数据库备份恢复和维护 - 创建和下载数据库备份 - 上传和恢复备份文件 - 序列重置:修复数据库序列问题 - 数据库状态检查和完整性验证 8. **学期管理**:设置和管理学期信息 - 创建新学期(如"2024-2025学年上学期") - 设置当前活跃学期 - 点歌记录自动关联到当前学期 9. **用户管理**:添加、编辑和删除用户 - 单个添加:填写用户信息(包括姓名、账号、年级、班级) - 批量导入:通过EXCEL文件批量添加用户 - 可以重置用户密码 10. **黑名单管理**:管理歌曲和关键词黑名单 - 添加具体歌曲或关键词到黑名单 - 自动过滤包含黑名单内容的点歌请求 - 支持启用/禁用黑名单项 11. **系统设置**:配置系统参数和功能开关 - 站点信息配置:标题、Logo、描述等 - 投稿限额设置:每日/每周投稿限制 - 播出时段管理:配置不同的播出时间段 - 功能开关:启用/禁用特定功能 12. **通知管理**:向用户发送系统通知 - 支持按全体用户、年级、班级或多班级发送 - 实时显示发送进度和结果 - 通知历史记录和管理 ## 数据库管理 ### Drizzle ORM + Neon Database 项目使用 Drizzle ORM 作为数据库 ORM,配合 Neon Database 提供现代化的数据库解决方案。 #### 核心文件结构 - **`drizzle.config.ts`** - Drizzle ORM 主配置文件 - **`app/drizzle/db.ts`** - 数据库连接和客户端配置 - **`app/drizzle/schema.ts`** - 数据库表结构定义(TypeScript 类型安全) - **`app/drizzle/migrations/`** - 数据库迁移脚本目录 ### 数据库初始化 首次部署时,系统会自动初始化数据库结构。 #### 自动初始化(推荐) 使用部署脚本自动完成数据库初始化: ```bash pnpm run deploy ``` 该命令会: 1. 检查环境变量配置 2. 安装依赖 3. 执行数据库迁移 4. 创建默认管理员账户 5. 构建应用 #### 手动初始化 如需手动管理数据库: 1. 生成迁移文件 ```bash pnpm run db:generate ``` 2. 执行数据库迁移 ```bash pnpm run db:migrate ``` 3. 推送模式变更到数据库(开发环境) ```bash pnpm run db:push ``` 4. 启动 Drizzle Studio(数据库管理界面) ```bash pnpm run db:studio ``` 访问 https://local.drizzle.studio 查看和管理数据库 5. 清空数据库并创建管理员 ```bash pnpm run clear-db ``` ### 数据库备份与恢复 #### 通过管理后台 1. 创建备份 - 登录管理后台 - 进入"数据库管理"页面 - 点击"创建备份"按钮 - 选择备份类型(完整备份/用户数据备份) 2. 下载备份 - 备份完成后在列表中找到备份文件 - 点击"下载"按钮保存到本地 3. 恢复备份 - 点击"上传备份"按钮 - 选择备份文件 - 选择恢复模式: - 增量恢复:合并备份数据与现有数据 - 完全恢复:替换所有现有数据(谨慎使用) - 确认并执行恢复 #### 使用 PostgreSQL 命令行 ```bash # 备份数据库 pg_dump -h localhost -U username -d database_name > backup.sql # 恢复数据库 psql -h localhost -U username -d database_name < backup.sql ``` ### 备份文件格式 - **完整备份**:包含所有数据表的 JSON 格式文件 - **用户备份**:仅包含用户相关数据 - **元数据**:包含创建时间、创建者、表信息等 ## 开发指南 ### 组合式API 项目使用了Vue 3的组合式API,主要包括: - `useAuth`: 处理用户认证、登录、注册和权限控制 - `useSongs`: 处理歌曲相关操作,包括获取歌曲列表、点歌和投票 - `useAdmin`: 处理管理员操作,包括排期管理和标记播放 - `useNotifications`: 处理通知系统,包括获取、标记已读和设置 - `useAudioQuality`: 处理音质管理,包括音质设置和持久化 - `useSemesters`: 处理学期管理,包括创建学期和设置活跃学期 ### 添加新功能 1. 在 `server/api` 中添加新的API端点 2. 在 `app/composables` 中添加相应的组合式函数 3. 在 `app/components` 中创建UI组件 4. 在 `app/pages` 中整合组件和功能 ### 数据库模型修改 如需修改数据库模型: 1. 编辑`app/drizzle/schema.ts`文件中的表结构定义 2. 生成新的迁移文件:`pnpm run db:generate` 3. 应用迁移到数据库:`pnpm run db:migrate` 4. 确保同时更新 `types/index.ts` 中的TypeScript类型定义 5. 使用Drizzle Studio查看数据库:`pnpm run db:studio` ### 国际化 (i18n) VoiceHub 内置一套无第三方依赖的手写国际化方案,支持 `zh-CN`(简体中文)与 `en-US`(English)两种语言。 #### 架构概览 - **词典文件**:`app/utils/locale/zh-CN.ts`、`app/utils/locale/en-US.ts`。两者结构必须完全一致(键对齐),值可为字符串、带 `{0}`/`{1}` 占位符的模板或格式化函数。 - **运行时**:`app/utils/locale/index.ts` 提供 `useLocale()`、`setLocale()`、`loadLocaleMessages()` 等。 - 中文(`FALLBACK_LOCALE`)作为**兜底与合并基底静态内置**;其余语言在被激活时才**动态按需加载**,默认语言用户不会下载多余语言包。 - `mergeLocaleFallback` 保证非兜底语言缺失某键时自动回退中文,不会出现空文本。 - 当前语言用 `useState('voicehub-locale')` 存储,**SSR 下按请求隔离**,避免跨请求语言串扰。 - **初始化插件**:`app/plugins/locale.ts`(服务端 + 客户端通用)。仅当用户手动选择过语言(`manual` 偏好)时信任 `cookie`,否则每次进入按 `Accept-Language` / 浏览器语言**跟随系统语言**,系统语言变化后自动切换;手动选择长期保留。渲染前 `await` 目标语言词典以消除首屏闪烁与水合不匹配,并驱动 ``。 #### 组件中使用 ```js // 取带兜底的响应式文案分区 const { pages, songs } = useLocale() const locale = computed(() => pages.value?.forgotPassword || {}) // 键查找 + {0}/{count} 占位符替换(复用共享助手,勿自行实现) const { t } = useLocaleText(locale) t('title') ``` - 文案访问统一复用 `~/composables/useLocaleText.ts`(`t`/`msg`/`nested`/`format`)与 `~/composables/useSafeLocale.ts`,**禁止**在组件内重复实现 `callLocale`、`getNestedMessage` 等私有取值函数。 - 新增文案键时,**必须同时在 `zh-CN.ts` 和 `en-US.ts` 添加**,保持键结构完全一致。 #### 服务端错误码本地化 服务端错误消息与语言解耦,通过「稳定错误码 + 客户端词典」实现本地化: 1. 服务端抛错使用 `createApiError`(`server/utils/apiError.ts`)而非裸 `createError`: ```ts import { createApiError } from '~~/server/utils/apiError' // createApiError(statusCode, code, message, data?) throw createApiError(429, 'AUTH_RATE_LIMITED_MINUTES', '操作过于频繁,请等待 ${waitMinutes} 分钟后再试', { params: [waitMinutes] }) ``` - `code` 建议取自 `server/config/constants.ts` 的 `SERVER_ERROR_CODES`;同时写入 `statusMessage` 与 `data.code`。 - `message` 为默认兜底文案(词典未命中时展示),动态值用 `data.params` 承载。 2. 客户端统一用 `useServerErrors().localize(err, fallback)` 展示错误:按 `err.data.code` 命中 `serverErrors` 词典,用 `data.params` 替换 `{0}`/`{1}`,未命中再回退服务端 `message`。 3. 新增错误码需在**三处同步**:`SERVER_ERROR_CODES`、`zh-CN.ts` 的 `serverErrors`、`en-US.ts` 的 `serverErrors`(键完全对齐)。 ### OAuth 平台扩展指南 VoiceHub 采用配置化与策略模式(Strategy Pattern)相结合的灵活 OAuth 扩展机制,所有 OAuth 提供商及认证设置现均已迁移至管理员后台界面。你可以直接在后台动态配置,无需修改环境变量和重启服务。 对于想要通过代码深度定制 OAuth 行为(如自定义用户信息解析逻辑等)的开发者,可参考以下机制: #### 扩展步骤 ##### 1. 定义 OAuth 策略 在 `server/utils/oauth-strategies.ts` 文件中,实现 `OAuthStrategy` 接口。该接口定义了 OAuth 流程中的三个核心方法: ```typescript export interface OAuthStrategy { /** * 获取授权跳转 URL * @param redirectUri 回调地址(通常是 /api/auth/[provider]/callback) * @param state 包含安全校验信息的加密字符串 */ getAuthorizeUrl(redirectUri: string, state: string): string /** * 使用 code 换取 access_token * @param code 授权码 * @param redirectUri 回调地址 */ exchangeToken(code: string, redirectUri: string): Promise /** * 获取用户信息 * @param accessToken 访问令牌 */ getUserInfo(accessToken: string): Promise } ``` **示例:接入 Google 登录** ```typescript // server/utils/oauth-strategies.ts const googleStrategy: OAuthStrategy = { getAuthorizeUrl(redirectUri, state) { const clientId = process.env.GOOGLE_CLIENT_ID // Google 授权端点 return `https://accounts.google.com/o/oauth2/v2/auth?client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=email%20profile&state=${encodeURIComponent(state)}` }, async exchangeToken(code, redirectUri) { // ... 实现 Google 的 Token 交换逻辑 // 通常是发送 POST 请求到 https://oauth2.googleapis.com/token }, async getUserInfo(accessToken) { // ... 实现获取 Google 用户信息的逻辑 // 通常是发送 GET 请求到 https://www.googleapis.com/oauth2/v3/userinfo // 需返回统一的 OAuthUserInfo 格式: // { id, username, email, name, avatar } } } ``` ##### 2. 注册策略 在同一个文件 (`server/utils/oauth-strategies.ts`) 的 `strategies` 对象中注册你的新策略: ```typescript const strategies: Record = { github: githubStrategy, casdoor: casdoorStrategy, google: googleStrategy // <--- 注册新平台 // ... 其他平台 } ``` ##### 3. 完成! 现在,你可以在管理员后台的 **站点配置 -> OAuth 第三方登录配置** 中直接填写该平台的信息并启用它。系统会自动处理路由分发、State 校验、CSRF 保护和用户绑定逻辑。 #### Casdoor 配置说明 项目已内置对 [Casdoor](https://casdoor.org/) 的支持。Casdoor 是一个开源的 UI 优先的身份认证管理系统 (IAM),支持 OAuth 2.0、OIDC 等多种协议。 要启用 Casdoor 登录,只需进入管理员后台的 **站点配置 -> OAuth 第三方登录配置**,开启 Casdoor 选项,并填入以下信息: - **Casdoor 服务器 URL** (如 `https://your-casdoor-domain.com`) - **Casdoor Client ID** - **Casdoor Client Secret** - **Casdoor 组织名称** 配置保存后,系统会立即启用 Casdoor 登录策略。 #### 前端图标配置 当添加了新的服务端 OAuth 策略后,如果需要在前端登录页面显示对应的图标按钮,请按照以下步骤操作: 1. 在 `app/components/Auth/Providers` 目录下创建一个以 Provider 名称(首字母大写)命名的文件夹,例如 `Google`。 2. 在该文件夹内创建一个 `Icon.vue` 组件,放入对应的 SVG 图标代码。 - 建议 SVG 大小设置为 `w-5 h-5` 以保持样式统一。 3. 系统会自动检测并加载该图标,无需额外配置。 例如:`app/components/Auth/Providers/Google/Icon.vue` **注意:** 对于 Casdoor,请创建 `app/components/Auth/Providers/Casdoor/Icon.vue`,并填入 Casdoor 的图标代码。 #### OAuth 工具函数 为了统一前端 OAuth 提供商的名称显示,系统提供了 `getProviderDisplayName` 工具函数。 **位置**: `app/utils/oauth.ts` **使用方法**: ```typescript import { getProviderDisplayName } from '~/utils/oauth' // 获取显示名称 const displayName = getProviderDisplayName('github') // 返回 "GitHub" const displayName2 = getProviderDisplayName('casdoor') // 返回 "Casdoor" const displayName3 = getProviderDisplayName('google') // 返回 "Google" (默认首字母大写) ``` **扩展**: 当添加新的 OAuth 提供商时,可以在 `app/utils/oauth.ts` 的 `map` 对象中添加对应的映射关系,以实现自定义显示名称。 #### 添加绑定卡片 为了在账号管理页面显示新添加的 OAuth 提供商绑定选项,你需要修改 `app/components/Auth/OAuthBindingCard.vue` 文件。 **1. 添加计算属性** 在 `