From 10de5582c7b3c45bb578f38a3ce45618739ff003 Mon Sep 17 00:00:00 2001 From: snowy-yang <102860110+snowy-yang@users.noreply.github.com> Date: Wed, 16 Sep 2026 20:52:38 +0800 Subject: [PATCH] =?UTF-8?q?fix(docs):=20=E8=B1=81=E5=85=8D=20/docs/json=20?= =?UTF-8?q?=E4=BF=A1=E5=B0=81=E5=8C=85=E8=A3=B9=EF=BC=8C=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=20Swagger=20UI=20=E6=97=A0=E6=B3=95=E5=8A=A0=E8=BD=BD=E8=A7=84?= =?UTF-8?q?=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server.js | 13 ++++++++----- src/smoke-test.js | 9 +++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/server.js b/src/server.js index e130d39..80f76af 100644 --- a/src/server.js +++ b/src/server.js @@ -31,6 +31,7 @@ import { redocHtml } from './openapi/ui.js' // 默认端口 16688:本机 TCP 动态端口范围是 1024~15000(出站连接会随机占用低端端口),避开该区间 const PORT = process.env.PORT || 16688 +const DOCS_PREFIX = '/docs' const API_TITLE = 'MusicServer 聚合音乐 API' const API_DESCRIPTION = '多平台音乐聚合接口:搜索、播放地址、歌词、封面、榜单、歌单。' + '支持 kw(酷我)/ kg(酷狗)/ tx(QQ音乐)/ wy(网易云)/ mg(咪咕)平台。' @@ -74,10 +75,13 @@ fastify.addHook('onResponse', async(request, reply) => { console.log(`[${new Date().toISOString()}] ${request.method} ${request.url} → ${reply.statusCode} (${Math.round(reply.elapsedTime)}ms)`) }) -// 成功响应统一包成 { code: 0, data };文档页/raw 路由与非 200 响应不包 +// 成功响应统一包成 { code: 0, data };文档页/raw 路由与非 200 响应不包。 +// @fastify/swagger-ui 的规范端点 {DOCS_PREFIX}/json 由插件内部注册、无法加 raw 标记, +// 必须豁免:Swagger UI 强制从该地址加载规范,包了信封顶层就没有 openapi 字段,页面直接报错 fastify.addHook('preSerialization', async(request, reply, payload) => { if (reply.statusCode !== 200) return payload if (request.routeOptions.config?.raw) return payload + if (request.routeOptions.url === `${DOCS_PREFIX}/json`) return payload if (payload === null || typeof payload === 'object') return { code: 0, data: payload } return payload }) @@ -141,10 +145,9 @@ fastify.register(swagger, { }, }) -fastify.register(swaggerUi, { - routePrefix: '/docs', - url: '/openapi.json', -}) +// 注意:插件初始化器会把规范地址强制为 {DOCS_PREFIX}/json(uiConfig 的 url 会被覆盖), +// 真正对外文档化的规范地址是下方带 raw 标记的 /openapi.json +fastify.register(swaggerUi, { routePrefix: DOCS_PREFIX }) fastify.get('/openapi.json', { config: { raw: true }, diff --git a/src/smoke-test.js b/src/smoke-test.js index 51a4421..6a16292 100644 --- a/src/smoke-test.js +++ b/src/smoke-test.js @@ -305,6 +305,15 @@ await test('OpenAPI 规范 /openapi.json', async() => { return `${Object.keys(spec.paths).length} 个路径` }) +await test('Swagger UI 规范端点 /docs/json(不可包信封)', async() => { + const r = await getRaw('/docs/json') + if (r.status !== 200) throw new Error(`期望 200,实际 ${r.status}`) + const spec = JSON.parse(r.body) + // Swagger UI 强制从该地址加载规范:若被全局信封钩子包成 { code, data },顶层无 openapi 字段,文档页直接报错 + if (!spec.openapi?.startsWith('3.')) throw new Error(`顶层缺少 openapi 字段(疑似被信封包裹): ${r.body.slice(0, 80)}`) + return spec.openapi +}) + // 汇总 const passed = results.filter(r => r.ok).length const failed = results.filter(r => !r.ok)