Skip to content

Commit

Permalink
No longer redirect to ./static/index.html for serving UI
Browse files Browse the repository at this point in the history
  • Loading branch information
jdhollander committed Jun 11, 2024
1 parent 652cff4 commit 0717103
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 102 deletions.
34 changes: 34 additions & 0 deletions e2e/custom.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,37 @@ test.describe('Check customizations', () => {
expect(logoId).toBe('example-logo') // it is included in the svg file
})
})

test.describe('Check redirection and url handling of static assets', () => {
test('Check static/index.html redirects', async ({ page }) => {
const jsonResponsePromise = page.waitForResponse(/json/)
await page.goto(`${URL_DOCUMENTATION}/static/index.html`)

// Check if the page is redirected to /documentation
const url = await page.url()
expect(url).toContain(`${URL_DOCUMENTATION}`)
expect(url).not.toContain('static/index.html')

// Check if the page has requested the json spec, and if so has it succeeded
const jsonResponse = await jsonResponsePromise
expect(jsonResponse.ok()).toBe(true)
})

test('Check root UI without slash loads json spec', async ({ page }) => {
const jsonResponsePromise = page.waitForResponse(/json/)
await page.goto(`${URL_DOCUMENTATION}`)

// Check if the page has requested the json spec, and if so has it succeeded
const jsonResponse = await jsonResponsePromise
expect(jsonResponse.ok()).toBe(true)
})

test('Check root UI with trailing slash loads json spec', async ({ page }) => {
const jsonResponsePromise = page.waitForResponse(/json/)
await page.goto(`${URL_DOCUMENTATION}/`)

// Check if the page has requested the json spec, and if so has it succeeded
const jsonResponse = await jsonResponsePromise
expect(jsonResponse.ok()).toBe(true)
})
})
22 changes: 11 additions & 11 deletions lib/index-html.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
'use strict'

function indexHtml (opts) {
return `<!-- HTML for static distribution bundle build -->
return (url) => `<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>${opts.theme?.title || 'Swagger UI'}</title>
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
<link rel="stylesheet" type="text/css" href="./index.css" />
${opts.theme && opts.theme.css ? opts.theme.css.map(css => `<link rel="stylesheet" type="text/css" href="./theme/${css.filename}" />\n`).join('') : ''}
<link rel="stylesheet" type="text/css" href="${url}${opts.staticPrefix}/swagger-ui.css" />
<link rel="stylesheet" type="text/css" href="${url}${opts.staticPrefix}/index.css" />
${opts.theme && opts.theme.css ? opts.theme.css.map(css => `<link rel="stylesheet" type="text/css" href="${url}${opts.staticPrefix}/theme/${css.filename}" />\n`).join('') : ''}
${opts.theme && opts.theme.favicon
? opts.theme.favicon.map(favicon => `<link rel="${favicon.rel}" type="${favicon.type}" href="./theme/${favicon.filename}" sizes="${favicon.sizes}" />\n`).join('')
? opts.theme.favicon.map(favicon => `<link rel="${favicon.rel}" type="${favicon.type}" href="${url}${opts.staticPrefix}/theme/${favicon.filename}" sizes="${favicon.sizes}" />\n`).join('')
: `
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
<link rel="icon" type="image/png" href="${url}${opts.staticPrefix}/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="${url}${opts.staticPrefix}/favicon-16x16.png" sizes="16x16" />
`}
</head>
<body>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
<script src="./swagger-initializer.js" charset="UTF-8"> </script>
${opts.theme && opts.theme.js ? opts.theme.js.map(js => `<script src="./theme/${js.filename}" charset="UTF-8"> </script>\n`).join('') : ''}
<script src="${url}${opts.staticPrefix}/swagger-ui-bundle.js" charset="UTF-8"> </script>
<script src="${url}${opts.staticPrefix}/swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
<script src="${url}${opts.staticPrefix}/swagger-initializer.js" charset="UTF-8"> </script>
${opts.theme && opts.theme.js ? opts.theme.js.map(js => `<script src="${url}${opts.staticPrefix}/theme/${js.filename}" charset="UTF-8"> </script>\n`).join('') : ''}
</body>
</html>
`
Expand Down
39 changes: 13 additions & 26 deletions lib/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,6 @@ const indexHtml = require('./index-html')
// URI prefix to separate static assets for swagger UI
const staticPrefix = '/static'

function getRedirectPathForTheRootRoute (url) {
let redirectPath

if (url.length !== 0 && url[url.length - 1] === '/') {
redirectPath = `.${staticPrefix}/index.html`
} else {
const urlPathParts = url.split('/')
redirectPath = `./${urlPathParts[urlPathParts.length - 1]}${staticPrefix}/index.html`
}

return redirectPath
}

function fastifySwagger (fastify, opts, done) {
let staticCSP = false
if (opts.staticCSP === true) {
Expand Down Expand Up @@ -66,16 +53,6 @@ function fastifySwagger (fastify, opts, done) {
}
}

fastify.route({
url: '/',
method: 'GET',
schema: { hide: true },
...hooks,
handler: (req, reply) => {
reply.redirect(getRedirectPathForTheRootRoute(req.raw.url))
}
})

if (opts.theme) {
const themePrefix = `${staticPrefix}/theme`
if (opts.theme.css) {
Expand Down Expand Up @@ -127,17 +104,27 @@ function fastifySwagger (fastify, opts, done) {
}
}

const indexHtmlContent = indexHtml(opts)
const indexHtmlContent = indexHtml({ ...opts, staticPrefix })

fastify.route({
url: `${staticPrefix}/index.html`,
url: '/',
method: 'GET',
schema: { hide: true },
...hooks,
handler: (req, reply) => {
reply
.header('content-type', 'text/html; charset=utf-8')
.send(indexHtmlContent)
.send(indexHtmlContent(req.url.replace(/\/$/, ''))) // remove trailing slash, as staticPrefix has a leading slash
}
})

fastify.route({
url: `${staticPrefix}/index.html`,
method: 'GET',
schema: { hide: true },
...hooks,
handler: (req, reply) => {
reply.redirect(req.url.replace(/\/static\/index\.html$/, '/'))
}
})

Expand Down
10 changes: 6 additions & 4 deletions lib/swagger-initializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ function swaggerInitializer (opts) {
});
}
function resolveUrl(url) {
const anchor = document.createElement('a')
anchor.href = url
var currentHref = window.location.href;
currentHref = currentHref.endsWith('/') ? currentHref : currentHref + '/';
var anchor = document.createElement('a');
anchor.href = currentHref + url;
return anchor.href
}
Expand All @@ -47,8 +49,8 @@ function swaggerInitializer (opts) {
layout: "StandaloneLayout",
validatorUrl: ${serialize(opts.validatorUrl || null)},
}, config, {
url: resolveUrl('./json').replace('static/json', 'json'),
oauth2RedirectUrl: resolveUrl('./oauth2-redirect.html')
url: resolveUrl('./json'),
oauth2RedirectUrl: resolveUrl('./static/oauth2-redirect.html')
});
const ui = SwaggerUIBundle(resConfig)
Expand Down
12 changes: 6 additions & 6 deletions test/csp.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ test('staticCSP = undefined', async (t) => {

const res = await fastify.inject({
method: 'GET',
url: '/documentation/static/index.html'
url: '/documentation'
})
t.equal(res.statusCode, 200)
t.equal(typeof res.headers['content-security-policy'], 'undefined')
Expand All @@ -57,7 +57,7 @@ test('staticCSP = true', async (t) => {
{
const res = await fastify.inject({
method: 'GET',
url: '/documentation/static/index.html'
url: '/documentation'
})
t.equal(res.statusCode, 200)
t.equal(res.headers['content-security-policy'], `default-src 'self'; base-uri 'self'; font-src 'self' https: data:; frame-ancestors 'self'; img-src 'self' data: validator.swagger.io; object-src 'none'; script-src 'self' ${csp.script.join(' ')}; script-src-attr 'none'; style-src 'self' https: ${csp.style.join(' ')}; upgrade-insecure-requests;`)
Expand Down Expand Up @@ -93,7 +93,7 @@ test('staticCSP = "default-src \'self\';"', async (t) => {
{
const res = await fastify.inject({
method: 'GET',
url: '/documentation/static/index.html'
url: '/documentation'
})
t.equal(res.statusCode, 200)
t.equal(res.headers['content-security-policy'], "default-src 'self';")
Expand Down Expand Up @@ -132,7 +132,7 @@ test('staticCSP = object', async (t) => {
{
const res = await fastify.inject({
method: 'GET',
url: '/documentation/static/index.html'
url: '/documentation'
})
t.equal(res.statusCode, 200)
t.equal(res.headers['content-security-policy'], "default-src 'self'; script-src 'self';")
Expand Down Expand Up @@ -172,7 +172,7 @@ test('transformStaticCSP = function', async (t) => {
{
const res = await fastify.inject({
method: 'GET',
url: '/documentation/static/index.html'
url: '/documentation'
})
t.equal(res.statusCode, 200)
t.equal(res.headers['content-security-policy'], "default-src 'self'; script-src 'self';")
Expand Down Expand Up @@ -212,7 +212,7 @@ test('transformStaticCSP = function, with @fastify/helmet', async (t) => {
{
const res = await fastify.inject({
method: 'GET',
url: '/documentation/static/index.html'
url: '/documentation'
})
t.equal(res.statusCode, 200)
t.equal(res.headers['content-security-policy'], "default-src 'self'; script-src 'self';")
Expand Down
Loading

0 comments on commit 0717103

Please sign in to comment.