Files
Xiao-no-love f0fe50e484 feat: StarLight 第三方登录 + 安全加固 + 循环池 + 热配置
功能:
- StarLight Yggdrasil 第三方登录(后端中转,accessToken 不落地)
- 管理员可视化权限管理(用户列表 + 一键授予/移出)
- 设置页热配置(11 项可编辑,立即生效)
- 循环池(每流独立手动歌单,队列空时随机不重复抽取)
- 登录页统一主站浅色风格
- 全站改名「Starlight专属电台」
- 删除关于页

安全:
- 全局 CSRF 防护(双提交 Cookie)
- 登录限流防 XFF 伪造、错误信息脱敏
- 权限中间件 requireAdminForWrites(读放行/写 admin)
- 密码哈希存储(scrypt/sha256)
- SSRF 防护(封面下载域名白名单)
- 空 catch 加日志、SSE 连接上限

修复:
- 多角色账号 selectedProfile 缺失
- 普通用户越权删歌(V4)
- streams 写操作越权(V9)
- 设置页布局溢出

测试:38 项全绿(api16 / e2e17 / write5)
2026-10-06 16:37:03 +08:00

123 lines
4.8 KiB
Markdown
Raw Permalink 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.
# Starlight专属电台 · v2
把 `audio/` 目录里的歌曲用 FFmpeg 切成 HLS 切片,拼成**多路**永不停播的直播流,并提供 Web 控制面板管理播放列表、导入音源(网易云 / 本地上传)。前后端一体的 Node.js 应用。
## v2 相比 v1 的变化
- **数据库**:JSON(playlist/heat/metadata)→ **SQLite**(better-sqlite3,WAL 模式),支持多路流隔离
- **鉴权**:可选 Bearer Token → **Cookie 会话登录**(面板 + API 全保护,HLS 流不锁)
- **多路流**:单流 → **多路独立直播流**,各自播放列表,`/hls/{streamId}/index.m3u8`
- **导入**:仅网易云下载 → 增加**本地上传导入**(自动匹配作者/封面)
- **删除**:无 → **级联删除**(音频 + 切片缓存 + DB + 播放列表引用)
- **本地化**:CDN 依赖 + 封面图 → **全部本地**(`public/vendor/`、`public/covers/`),系统字体
- **模块化**:1233 行单文件 → `src/` 分模块
## 环境要求
| 依赖 | 版本 |
|------|------|
| Node.js | >= 18 |
| FFmpeg | 含 `ffmpeg` 与 `ffprobe`,在 PATH 中 |
## 安装与启动
```bash
npm install
npm start
```
首次启动若未设密码,控制台会打印随机登录密码:
```
[AUTH] 未设置 PANEL_PASSWORD,已生成随机登录密码: xxxxxx
```
建议固定密码:
```bash
# Windows PowerShell
$env:PANEL_PASSWORD="你的密码"; npm start
# Windows CMD
set PANEL_PASSWORD=你的密码 && npm start
# Linux / macOS
PANEL_PASSWORD=你的密码 npm start
```
访问 `http://127.0.0.1:3000`,用密码登录。
## 目录结构
```
demo/
├── index.js # 入口,委托 src/server.js
├── src/
│ ├── server.js # 主程序:装配路由、启动服务
│ ├── config.js # 配置(环境变量)
│ ├── logger.js # 日志(分级 + 落盘 + 面板可查)
│ ├── db.js # SQLite 初始化 + schema
│ ├── store.js # 数据访问层(全部 SQL 收敛于此)
│ ├── util.js # 工具函数
│ ├── media.js # ffprobe / FFmpeg 切片 / 并发队列
│ ├── metadata.js # 网易云 API / 封面本地化 / 元数据补全
│ ├── livestream.js # LiveStream 类 + 多路流管理 + 缓存
│ ├── scanner.js # audio/ 目录监听与同步
│ ├── auth.js # 会话鉴权
│ ├── sse.js # SSE 推送
│ └── routes/ # API 路由(auth / music / resources / streams / system)
├── public/ # 前端(index.html / login.html / css / js / vendor / covers)
├── scripts/fetch_vendor.py # 一次性下载前端 CDN 资源到本地
├── audio/ # 音频文件
├── data.db # SQLite 数据库(自动生成)
└── hls/ hls_cache/ # 直播流与切片缓存(自动生成)
```
## API 一览
所有 `/api/*`(除 `/api/auth/*`)需登录。
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/auth/login` | 登录(body: `{password}`) |
| POST | `/api/auth/logout` | 登出 |
| GET | `/api/resources` | 资源列表 |
| GET | `/api/music/search?keyword=` | 网易云搜索 |
| POST | `/api/music/detail` | 网易云详情 |
| POST | `/api/music/download` | 网易云下载入库 |
| POST | `/api/music/import` | 本地上传导入(multipart) |
| POST | `/api/music/delete` | 删除音频(级联) |
| POST | `/api/music/delete-batch` | 批量删除 |
| POST | `/api/music/enrich` | 重新匹配元数据 |
| GET | `/api/streams` | 流列表 |
| POST | `/api/streams` | 创建流 |
| DELETE | `/api/streams/:id` | 删除流 |
| POST | `/api/streams/:id/start\|stop` | 启停流 |
| GET | `/api/streams/:id/playlist` | 播放列表 |
| POST | `/api/streams/:id/playlist/add\|remove\|reorder\|clear\|next\|prev\|jump` | 播放列表操作 |
| GET | `/api/system/cache` | 缓存信息 |
| POST | `/api/system/cache/cleanup` | 手动清理缓存 |
| GET | `/api/system/logs?tail=` | 查看日志 |
| GET | `/api/events` | SSE 实时推送 |
## 直播流地址
- 默认流:`http://127.0.0.1:3000/hls/main/index.m3u8`
- 其它流:`http://127.0.0.1:3000/hls/{streamId}/index.m3u8`
可用 VLC、PotPlayer、ffplay 打开。
## 环境变量
| 变量 | 默认 | 说明 |
|------|------|------|
| `PANEL_PASSWORD` | 随机 | 面板登录密码 |
| `PORT` | 3000 | 端口 |
| `HLS_TIME` | 5 | 切片时长(秒) |
| `SLIDE_WINDOW` | 15 | 滑动窗口段数 |
| `MAX_CACHE_MB` | 1000 | 缓存上限 |
| `FFMPEG_CONCURRENCY` | 3 | 转码并发 |
| `SESSION_TTL_HOURS` | 72 | 登录有效期(小时) |
| `LOG_LEVEL` | info | 日志级别 |
## 技术栈
Node.js · Express · better-sqlite3 · Multer · Chokidar · FFmpeg(HLS)· SortableJS · Bootstrap Icons