From e0b81a3e481ff606e4f8c55e669f4bf8df4d73db Mon Sep 17 00:00:00 2001 From: David Tanner Date: Mon, 14 Oct 2024 10:25:11 -0600 Subject: [PATCH] fix: html urls should not be absolute --- lib/index-html.js | 6 ++-- lib/routes.js | 3 +- test/route.test.js | 87 +++++++++++++++++++++++++++++++++++++++------- types/index.d.ts | 25 ++++++++++--- 4 files changed, 99 insertions(+), 22 deletions(-) diff --git a/lib/index-html.js b/lib/index-html.js index 886872b..85cbc17 100644 --- a/lib/index-html.js +++ b/lib/index-html.js @@ -1,8 +1,10 @@ 'use strict' function indexHtml (opts) { - return (hasTrailingSlash) => { - const prefix = hasTrailingSlash ? `.${opts.staticPrefix}` : `${opts.prefix}${opts.staticPrefix}` + const hasLeadingSlash = /^\//.test(opts.prefix) + return (url) => { + const hasTrailingSlash = /\/$/.test(url) + const prefix = hasTrailingSlash ? `.${opts.staticPrefix}` : `${hasLeadingSlash ? '.' : ''}${opts.prefix}${opts.staticPrefix}` return ` diff --git a/lib/routes.js b/lib/routes.js index 3b206d5..f4be95f 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -112,10 +112,9 @@ function fastifySwagger (fastify, opts, done) { schema: { hide: true }, ...hooks, handler: (req, reply) => { - const hasTrailingSlash = /\/$/.test(req.url) reply .header('content-type', 'text/html; charset=utf-8') - .send(indexHtmlContent(hasTrailingSlash)) // trailing slash alters the relative urls generated in the html + .send(indexHtmlContent(req.url)) // trailing slash alters the relative urls generated in the html } }) diff --git a/test/route.test.js b/test/route.test.js index b9be134..d3efbc8 100644 --- a/test/route.test.js +++ b/test/route.test.js @@ -539,7 +539,7 @@ test('should return empty log level of route /documentation', async (t) => { }) test('/documentation should display index html with correct asset urls', async (t) => { - t.plan(4) + t.plan(6) const fastify = Fastify() await fastify.register(fastifySwagger, swaggerOption) await fastify.register(fastifySwaggerUi, { theme: { js: [{ filename: 'theme-js.js' }] } }) @@ -549,10 +549,54 @@ test('/documentation should display index html with correct asset urls', async ( url: '/documentation' }) - t.assert.deepStrictEqual(res.payload.includes('href="/documentation/static/index.css"'), true) - t.assert.deepStrictEqual(res.payload.includes('src="/documentation/static/theme/theme-js.js"'), true) - t.assert.deepStrictEqual(res.payload.includes('href="/documentation/index.css"'), false) - t.assert.deepStrictEqual(res.payload.includes('src="/documentation/theme/theme-js.js"'), false) + t.assert.deepStrictEqual(res.payload.includes('href="./documentation/static/index.css"'), true) + t.assert.deepStrictEqual(res.payload.includes('src="./documentation/static/theme/theme-js.js"'), true) + t.assert.deepStrictEqual(res.payload.includes('href="./documentation/index.css"'), false) + t.assert.deepStrictEqual(res.payload.includes('src="./documentation/theme/theme-js.js"'), false) + + let cssRes = await fastify.inject({ + method: 'GET', + url: '/documentation/static/index.css' + }) + t.assert.equal(cssRes.statusCode, 200) + cssRes = await fastify.inject({ + method: 'GET', + url: './documentation/static/index.css' + }) + t.assert.equal(cssRes.statusCode, 200) +}) + +/** + * This emulates when the server is inside an NGINX application that routes by path + */ +test('/documentation should display index html with correct asset urls when nested', async (t) => { + t.plan(5) + const fastify = Fastify() + await fastify.register( + async () => { + await fastify.register(fastifySwagger, swaggerOption) + await fastify.register(fastifySwaggerUi, { theme: { js: [{ filename: 'theme-js.js' }] } }) + }, + { + prefix: '/swagger-app' + } + ) + + const res = await fastify.inject({ + method: 'GET', + url: '/swagger-app/documentation' + }) + + t.assert.deepStrictEqual(res.payload.includes('href="./documentation/static/index.css"'), true) + t.assert.deepStrictEqual(res.payload.includes('src="./documentation/static/theme/theme-js.js"'), true) + t.assert.deepStrictEqual(res.payload.includes('href="./documentation/index.css"'), false) + t.assert.deepStrictEqual(res.payload.includes('src="./documentation/theme/theme-js.js"'), false) + + const cssRes = await fastify.inject({ + method: 'GET', + url: '/swagger-app/documentation/static/index.css' + }) + t.assert.equal(cssRes.statusCode, 200) }) test('/documentation/ should display index html with correct asset urls', async (t) => { @@ -583,10 +627,27 @@ test('/docs should display index html with correct asset urls when documentation url: '/docs' }) - t.assert.strictEqual(res.payload.includes('href="/docs/static/index.css"'), true) - t.assert.strictEqual(res.payload.includes('src="/docs/static/theme/theme-js.js"'), true) - t.assert.strictEqual(res.payload.includes('href="/docs/index.css"'), false) - t.assert.strictEqual(res.payload.includes('src="/docs/theme/theme-js.js"'), false) + t.assert.strictEqual(res.payload.includes('href="./docs/static/index.css"'), true) + t.assert.strictEqual(res.payload.includes('src="./docs/static/theme/theme-js.js"'), true) + t.assert.strictEqual(res.payload.includes('href="./docs/index.css"'), false) + t.assert.strictEqual(res.payload.includes('src="./docs/theme/theme-js.js"'), false) +}) + +test('/docs should display index html with correct asset urls when documentation prefix is set with no leading slash', async (t) => { + t.plan(4) + const fastify = Fastify() + await fastify.register(fastifySwagger, swaggerOption) + await fastify.register(fastifySwaggerUi, { theme: { js: [{ filename: 'theme-js.js' }] }, routePrefix: 'docs' }) + + const res = await fastify.inject({ + method: 'GET', + url: '/docs' + }) + + t.assert.strictEqual(res.payload.includes('href="docs/static/index.css"'), true) + t.assert.strictEqual(res.payload.includes('src="docs/static/theme/theme-js.js"'), true) + t.assert.strictEqual(res.payload.includes('href="docs/index.css"'), false) + t.assert.strictEqual(res.payload.includes('src="docs/theme/theme-js.js"'), false) }) test('/docs/ should display index html with correct asset urls when documentation prefix is set', async (t) => { @@ -634,10 +695,10 @@ test('/docs should display index html with correct asset urls when documentation url: '/docs' }) - t.assert.strictEqual(res.payload.includes('href="/docs/static/index.css"'), true) - t.assert.strictEqual(res.payload.includes('src="/docs/static/theme/theme-js.js"'), true) - t.assert.strictEqual(res.payload.includes('href="/docs/index.css"'), false) - t.assert.strictEqual(res.payload.includes('src="/docs/theme/theme-js.js"'), false) + t.assert.strictEqual(res.payload.includes('href="./docs/static/index.css"'), true) + t.assert.strictEqual(res.payload.includes('src="./docs/static/theme/theme-js.js"'), true) + t.assert.strictEqual(res.payload.includes('href="./docs/index.css"'), false) + t.assert.strictEqual(res.payload.includes('src="./docs/theme/theme-js.js"'), false) }) test('/docs/ should display index html with correct asset urls when documentation prefix is set', async (t) => { diff --git a/types/index.d.ts b/types/index.d.ts index 9b630a9..a3720ad 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,6 +1,13 @@ /// -import { FastifyPluginCallback, FastifyReply, FastifyRequest, onRequestHookHandler, preHandlerHookHandler } from 'fastify'; +import { + FastifyPluginCallback, + FastifyReply, + FastifyRequest, + onRequestHookHandler, + preHandlerHookHandler, + RegisterOptions, +} from 'fastify'; /** * Swagger-UI Vendor Extensions @@ -31,13 +38,17 @@ declare module 'fastify' { type FastifySwaggerUi = FastifyPluginCallback; declare namespace fastifySwaggerUi { - export interface FastifySwaggerUiOptions { + export interface FastifySwaggerUiOptions extends Omit { baseDir?: string; /** * Overwrite the swagger url end-point * @default /documentation */ routePrefix?: string; + /** + * Make it explicit that this plugin overrides the prefix value + */ + prefix?: never; /** * Swagger UI Config */ @@ -52,6 +63,10 @@ declare namespace fastifySwaggerUi { * route hooks */ uiHooks?: FastifySwaggerUiHooksOptions + /** + * Make it explicit that this plugin overrides the prefix value + */ + hooks?: never; theme?: FastifySwaggerUiTheme @@ -62,7 +77,7 @@ declare namespace fastifySwaggerUi { /** * Use this parameter to set a validator URL - * + * * @default false */ validatorUrl?: string | false @@ -441,7 +456,7 @@ declare namespace fastifySwaggerUi { /** * scope separator for passing scopes, encoded before calling, default * value is a space (encoded value %20). - * + * * @default ' ' */ scopeSeparator?: string; @@ -449,7 +464,7 @@ declare namespace fastifySwaggerUi { /** * string array or scope separator (i.e. space) separated string of * initially selected oauth scopes - * + * * @default [] */ scopes?: string | string[];