feat(source): 外部音源迁 src/ 并新增 source-simple 进程内直载

- apis/source(.ivm).ts → src/source(.ivm).ts(音源属服务端职责,不入 SDK)
- 新增 src/source-simple.ts:sources/*.mjs 进程内直载(getMusicUrl 极简契约),
  与 *.js 的 IVM 沙箱(globalThis.lx 契约)按扩展名混排候选链,失败降权冷却
- server 挂载决策:SOURCE_ENABLED 非 0/false 启用,/url 取流分流到外部音源;
  音源模式下登录态不参与取流,自动续期不启动(手动 /login/refresh 仍可用)
- 新增 src/env.ts(.env 最先加载)与 test/source-simple-test.ts(并入 pnpm test)
This commit is contained in:
snowy-yang committed 2026-10-01 16:35:08 +08:00
1 parent 66d06ef48a
commit 96f6a7fe91
13 files changed
+570 -94

No files matched your search

+16 -2
View File
@@ -19,6 +19,7 @@
// 状态码语义:200 成功;422 参数校验失败;404 路由或平台不存在;405 方法不允许;502 上游平台失败;500 内部错误
// 响应信封:成功 { code: 0, data };失败 { code: <HTTP状态码>, msg, detail? }
import './env.ts' // 必须最先导入:在任何读取 process.env 的模块求值前加载 .env
import Fastify from 'fastify'
import cors from '@fastify/cors'
import swagger from '@fastify/swagger'
@@ -27,6 +28,7 @@ import { readFileSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import { dirname, join } from 'node:path'
import musicSdk from '../sdk/index.ts'
import sourceApi from './source.ts'
import { ApiError } from './http/api-error.ts'
import { musicRoutes } from './routes.ts'
import { loginRoutes } from './routes-login.ts'
@@ -50,6 +52,12 @@ const fastify = Fastify({
})
fastify.decorate('musicSdk', musicSdk)
// ---- 音源模式(服务端职责):SOURCE_ENABLED 非 0/false 时,/url 取流重定向到 sources/ 外部音源脚本 ----
// SDK 只包含官方平台接口;是否走外部音源由服务端在此决策,路由层按 sourceApi 是否挂载分流
const sourceEnabled = process.env.SOURCE_ENABLED
const useSource = Boolean(sourceEnabled) && sourceEnabled !== '0' && sourceEnabled !== 'false'
fastify.decorate('sourceApi', useSource ? sourceApi : null)
// ---- 共享 schema(领域模型:校验引用与 OpenAPI components 同源) ----
for (const [name, schema] of Object.entries(COMPONENT_SCHEMAS)) {
fastify.addSchema({ ...schema, $id: name })
@@ -185,12 +193,18 @@ fastify.get('/', {
fastify.register(musicRoutes)
fastify.register(loginRoutes)
// 登录态自动续期(onClose 钩子需在 listen 前注册,故这里而非 startServer 内启动)
startRefreshScheduler(fastify)
// 登录态自动续期(onClose 钩子需在 listen 前注册,故这里而非 startServer 内启动)。
// 音源模式下取流走外部脚本,登录态不参与任何接口,自动续期没有意义;手动 /login/refresh 仍可用
if (!useSource) {
startRefreshScheduler(fastify)
} else {
console.log(`[${new Date().toISOString()}] [登录续期] 音源模式已启用,登录态不参与取流,自动续期未启动`)
}
export const startServer = async(port = PORT) => {
await fastify.listen({ port: Number(port), host: '0.0.0.0' })
console.log(`音乐服务器已启动: http://localhost:${port}`)
console.log(`取流通道: ${useSource ? '外部音源脚本(sources/,SOURCE_ENABLED)' : 'SDK 官方直连接口'}`)
console.log(`接口文档: http://localhost:${port}/docs (Swagger UI)`)
console.log(`ReDoc 文档: http://localhost:${port}/redoc`)
console.log(`OpenAPI: http://localhost:${port}/openapi.json`)