|
import express from 'express'; |
|
import getAbsoluteSwaggerFsPath from "swagger-ui-dist"; |
|
|
|
const getAbsoluteSwaggerFsPaths = getAbsoluteSwaggerFsPath.getAbsoluteFSPath() |
|
|
|
var favIconHtml = '<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />' + |
|
'<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />'; |
|
var swaggerInit = ''; |
|
|
|
function trimQuery(q) { |
|
return q && q.split('?')[0]; |
|
} |
|
|
|
var htmlTplString = ` |
|
<!-- HTML for static distribution bundle build --> |
|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
|
<meta property="og:type" content="website"> |
|
<meta name="apple-mobile-web-app-capable" content="yes"> |
|
<% robotsMetaString %> |
|
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" > |
|
<% favIconString %> |
|
<style> |
|
html |
|
{ |
|
box-sizing: border-box; |
|
overflow: -moz-scrollbars-vertical; |
|
overflow-y: scroll; |
|
} |
|
*, |
|
*:before, |
|
*:after |
|
{ |
|
box-sizing: inherit; |
|
} |
|
body { |
|
margin:0; |
|
background: #fafafa; |
|
} |
|
.swagger-ui section.models h4 span { |
|
flex: 1; |
|
color: black; |
|
} |
|
|
|
.swagger-ui .model-toggle { |
|
cursor: pointer; |
|
display: inline-block; |
|
font-size: 10px; |
|
margin: auto .3em; |
|
position: relative; |
|
top: 6px; |
|
transform: rotate(90deg); |
|
transform-origin: 50% 50%; |
|
transition: transform .15s ease-in; |
|
background-color: white; |
|
border-radius: 50%; |
|
} |
|
|
|
.dark-mode body { |
|
background-color: #1f1f1f; |
|
color: #ffffff; |
|
} |
|
|
|
.dark-mode .swagger-ui { |
|
color: #3b4151; |
|
background-color: #1f1f1f; |
|
} |
|
|
|
.dark-mode .swagger-ui .topbar { |
|
background-color: #131414; |
|
} |
|
|
|
.dark-mode .swagger-ui .info .title { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .opblock-description-wrapper, .swagger-ui .opblock-external-docs-wrapper, .swagger-ui .opblock-title_normal { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .info li, .dark-mode .swagger-ui .info p, .dark-mode .swagger-ui .info table { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .scheme-container { |
|
background: #131414; |
|
box-shadow: 0 1px 2px 0 rgba(0,0,0,.15); |
|
} |
|
|
|
.dark-mode .swagger-ui .servers-title { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui select { |
|
background: #131414 url("data:image/svg+xml;charset=utf-8,<svg xmlns=\\"http://www.w3.org/2000/svg\\" viewBox=\\"0 0 20 20\\"><path d=\\"M13.418 7.859a.695.695 0 0 1 .978 0 .68.68 0 0 1 0 .969l-3.908 3.83a.697.697 0 0 1-.979 0l-3.908-3.83a.68.68 0 0 1 0-.969.695.695 0 0 1 .978 0L10 11z\\"/></svg>") right 10px center no-repeat; |
|
border: 2px solid #41444e; |
|
box-shadow: 0 1px 2px 0 rgba(0,0,0,.25); |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui a.nostyle, .dark-mode .swagger-ui a.nostyle:visited { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .opblock-tag small { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .opblock .opblock-summary-description { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .opblock-description-wrapper p, .dark-mode .swagger-ui .opblock-external-docs-wrapper p, .dark-mode .swagger-ui .opblock-title_normal p { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .opblock .opblock-section-header { |
|
align-items: center; |
|
background: #131414; |
|
box-shadow: 0 1px 2px rgba(0,0,0,.1); |
|
} |
|
|
|
.dark-mode .swagger-ui .btn { |
|
box-shadow: 0 1px 2px rgba(0,0,0,.1); |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .opblock .opblock-section-header h4 { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .responses-inner h4, .swagger-ui .responses-inner h5 { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui table thead tr td, .dark-mode .swagger-ui table thead tr th { |
|
border-bottom: 1px solid rgba(59,65,81,.2); |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .parameter__name { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .parameters-col_description .markdown p, .dark-mode .swagger-ui .parameters-col_description .renderedMarkdown p { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .parameter__type { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .parameter__extension, .dark-mode .swagger-ui .parameter__in { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui input[disabled], .dark-mode .swagger-ui select[disabled], .dark-mode .swagger-ui textarea[disabled] { |
|
background-color: #131414; |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui input[type="email"], .dark-mode .swagger-ui input[type="file"], .dark-mode .swagger-ui input[type="password"], .dark-mode .swagger-ui input[type="search"], .dark-mode .swagger-ui input[type="text"], .dark-mode .swagger-ui textarea { |
|
background: #131414; |
|
border: 1px solid #d9d9d9; |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui input[type="email"].invalid, .dark-mode .swagger-ui input[type="file"].invalid, .dark-mode .swagger-ui input[type="password"].invalid, .dark-mode .swagger-ui input[type="search"].invalid, .dark-mode .swagger-ui input[type="text"].invalid, .dark-mode .swagger-ui textarea.invalid { |
|
background: #400c0c; |
|
border-color: #f93e3e; |
|
} |
|
|
|
.dark-mode .swagger-ui .errors-wrapper { |
|
background: rgba(249,62,62,.1); |
|
border: 2px solid #f93e3e; |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .response-col_status { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .response-col_description .markdown p, .dark-mode .swagger-ui .response-col_description .renderedMarkdown p { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .response-control-examples__title, .dark-mode .swagger-ui .response-control-media-type__title { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .tab li button.tablinks { |
|
color: #fff; |
|
} |
|
|
|
.dark-mode .swagger-ui .expand-methods svg, .swagger-ui .expand-operation svg { |
|
fill: white; |
|
} |
|
|
|
.dark-mode .swagger-ui svg:not(:root) { |
|
fill: white; |
|
} |
|
|
|
.dark-mode .swagger-ui section.models h4 span { |
|
color: white; |
|
} |
|
|
|
.dark-mode .swagger-ui .model { |
|
color: white; |
|
} |
|
|
|
.dark-mode .swagger-ui .model-title { |
|
color: white; |
|
} |
|
|
|
.dark-mode .swagger-ui label { |
|
color: white; |
|
} |
|
|
|
.dark-mode .swagger-ui .model-toggle { |
|
background-color: white; |
|
} |
|
|
|
.dark-mode .swagger-ui table.model tr.property-row td:first-child { |
|
color: white; |
|
} |
|
|
|
.dark-mode .swagger-ui .model-box-control, .swagger-ui .models-control, .swagger-ui .opblock-summary-control { |
|
color: white; |
|
} |
|
|
|
.dark-mode .swagger-ui section.models .model-container { |
|
background-color: #1a1a1a; |
|
} |
|
|
|
.dark-mode .swagger-ui .info .base-url { |
|
color: white; |
|
} |
|
|
|
.dark-mode .swagger-ui .scheme-container .schemes > .schemes-server-container > label { |
|
color: white; |
|
} |
|
</style> |
|
</head> |
|
|
|
<body> |
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0"> |
|
<defs> |
|
<symbol viewBox="0 0 20 20" id="unlocked"> |
|
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path> |
|
</symbol> |
|
|
|
<symbol viewBox="0 0 20 20" id="locked"> |
|
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/> |
|
</symbol> |
|
|
|
<symbol viewBox="0 0 20 20" id="close"> |
|
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/> |
|
</symbol> |
|
|
|
<symbol viewBox="0 0 20 20" id="large-arrow"> |
|
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/> |
|
</symbol> |
|
|
|
<symbol viewBox="0 0 20 20" id="large-arrow-down"> |
|
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/> |
|
</symbol> |
|
|
|
|
|
<symbol viewBox="0 0 24 24" id="jump-to"> |
|
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/> |
|
</symbol> |
|
|
|
<symbol viewBox="0 0 24 24" id="expand"> |
|
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/> |
|
</symbol> |
|
|
|
</defs> |
|
</svg> |
|
|
|
<div id="swagger-ui"></div> |
|
|
|
<script src="./swagger-ui-bundle.js"> </script> |
|
<script src="./swagger-ui-standalone-preset.js"> </script> |
|
<script src="./swagger-ui-init.js"> </script> |
|
|
|
<% customJs %> |
|
<% customJsStr %> |
|
<% customCssUrl %> |
|
<style> |
|
<% customCss %> |
|
</style> |
|
|
|
<script> |
|
document.addEventListener('DOMContentLoaded', function() { |
|
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); |
|
|
|
function updateTheme() { |
|
if (darkModeMediaQuery.matches) { |
|
document.documentElement.classList.add('dark-mode'); |
|
document.documentElement.classList.remove('light-mode'); |
|
} else { |
|
document.documentElement.classList.remove('dark-mode'); |
|
document.documentElement.classList.add('light-mode'); |
|
} |
|
} |
|
|
|
darkModeMediaQuery.addListener(updateTheme); |
|
updateTheme(); |
|
}); |
|
|
|
</script> |
|
</body> |
|
</html> |
|
` |
|
|
|
var jsTplString = ` |
|
window.onload = function() { |
|
// Build a system |
|
|
|
var url = window.location.search.match(/url=([^&]+)/); |
|
if (url && url.length > 1) { |
|
url = decodeURIComponent(url[1]); |
|
} else { |
|
url = window.location.origin; |
|
} |
|
|
|
<% swaggerOptions %> |
|
url = options.swaggerUrl || url |
|
var urls = options.swaggerUrls |
|
var customOptions = options.customOptions |
|
var spec1 = options.swaggerDoc |
|
var swaggerOptions = { |
|
spec: spec1, |
|
url: url, |
|
urls: urls, |
|
dom_id: '#swagger-ui', |
|
deepLinking: true, |
|
presets: [ |
|
SwaggerUIBundle.presets.apis, |
|
SwaggerUIStandalonePreset |
|
], |
|
|
|
plugins: [ |
|
SwaggerUIBundle.plugins.DownloadUrl |
|
], |
|
layout: "StandaloneLayout" |
|
} |
|
|
|
for (var attrname in customOptions) { |
|
swaggerOptions[attrname] = customOptions[attrname]; |
|
} |
|
var ui = SwaggerUIBundle(swaggerOptions) |
|
|
|
if (customOptions.oauth) { |
|
ui.initOAuth(customOptions.oauth) |
|
} |
|
|
|
if (customOptions.preauthorizeApiKey) { |
|
const key = customOptions.preauthorizeApiKey.authDefinitionKey; |
|
const value = customOptions.preauthorizeApiKey.apiKeyValue; |
|
if (!!key && !!value) { |
|
const pid = setInterval(() => { |
|
const authorized = ui.preauthorizeApiKey(key, value); |
|
if(!!authorized) clearInterval(pid); |
|
}, 500) |
|
|
|
} |
|
} |
|
|
|
if (customOptions.authAction) { |
|
ui.authActions.authorize(customOptions.authAction) |
|
} |
|
|
|
window.ui = ui |
|
} |
|
` |
|
|
|
function toExternalScriptTag(url) { |
|
return `<script src='${url}'></script>` |
|
} |
|
|
|
function toInlineScriptTag(jsCode) { |
|
return `<script>${jsCode}</script>` |
|
} |
|
|
|
function toExternalStylesheetTag(url) { |
|
return `<link href='${url}' rel='stylesheet'>` |
|
} |
|
|
|
function toTags(customCode, toScript) { |
|
if (typeof customCode === 'string') { |
|
return toScript(customCode) |
|
} else if (Array.isArray(customCode)) { |
|
return customCode.map(toScript).join('\n') |
|
} else { |
|
return '' |
|
} |
|
} |
|
|
|
var generateHTML = function (swaggerDoc, opts, options, customCss, customfavIcon, swaggerUrl, customSiteTitle, _htmlTplString, _jsTplString) { |
|
|
|
var isExplorer |
|
var customJs |
|
var customJsStr |
|
var swaggerUrls |
|
var customCssUrl |
|
var customRobots |
|
|
|
if (opts && typeof opts === 'object') { |
|
options = opts.swaggerOptions |
|
customCss = opts.customCss |
|
customJs = opts.customJs |
|
customJsStr = opts.customJsStr |
|
customfavIcon = opts.customfavIcon |
|
customRobots = opts.customRobots |
|
swaggerUrl = opts.swaggerUrl |
|
swaggerUrls = opts.swaggerUrls |
|
isExplorer = opts.explorer || !!swaggerUrls |
|
customSiteTitle = opts.customSiteTitle |
|
customCssUrl = opts.customCssUrl |
|
} else { |
|
|
|
isExplorer = opts |
|
} |
|
|
|
options = options || {} |
|
var explorerString = isExplorer ? '' : '.swagger-ui .topbar .download-url-wrapper { display: none }' |
|
customCss = explorerString + ' ' + customCss || explorerString |
|
customfavIcon = customfavIcon || false |
|
customSiteTitle = customSiteTitle || 'Swagger UI' |
|
_htmlTplString = _htmlTplString || htmlTplString |
|
_jsTplString = _jsTplString || jsTplString |
|
|
|
var robotsMetaString = customRobots ? '<meta name="robots" content="' + customRobots + '" />' : '' |
|
var favIconString = customfavIcon ? '<link rel="icon" href="' + customfavIcon + '" />' : favIconHtml |
|
var htmlWithCustomCss = _htmlTplString.toString().replace('<% customCss %>', customCss) |
|
var htmlWithCustomRobots = htmlWithCustomCss.replace('<% robotsMetaString %>', robotsMetaString) |
|
var htmlWithFavIcon = htmlWithCustomRobots.replace('<% favIconString %>', favIconString) |
|
var htmlWithCustomJsUrl = htmlWithFavIcon.replace('<% customJs %>', toTags(customJs, toExternalScriptTag)) |
|
var htmlWithCustomJs = htmlWithCustomJsUrl.replace('<% customJsStr %>', toTags(customJsStr, toInlineScriptTag)) |
|
var htmlWithCustomCssUrl = htmlWithCustomJs.replace('<% customCssUrl %>', toTags(customCssUrl, toExternalStylesheetTag)) |
|
|
|
var initOptions = { |
|
swaggerDoc: swaggerDoc || undefined, |
|
customOptions: options, |
|
swaggerUrl: swaggerUrl || undefined, |
|
swaggerUrls: swaggerUrls || undefined |
|
} |
|
|
|
swaggerInit = _jsTplString.toString().replace('<% swaggerOptions %>', stringify(initOptions)) |
|
return htmlWithCustomCssUrl.replace('<% title %>', customSiteTitle) |
|
} |
|
|
|
var setup = function (swaggerDoc, opts, options, customCss, customfavIcon, swaggerUrl, customSiteTitle) { |
|
var html = generateHTML(swaggerDoc, opts, options, customCss, customfavIcon, swaggerUrl, customSiteTitle, htmlTplString, jsTplString) |
|
return function (req, res) { |
|
if (req.swaggerDoc) { |
|
var reqHtml = generateHTML(req.swaggerDoc, opts, options, customCss, customfavIcon, swaggerUrl, customSiteTitle, htmlTplString, jsTplString) |
|
res.send(reqHtml) |
|
} else { |
|
res.send(html) |
|
} |
|
} |
|
} |
|
|
|
function swaggerInitFn(req, res, next) { |
|
if (trimQuery(req.url).endsWith('/package.json')) { |
|
res.sendStatus(404) |
|
} else if (trimQuery(req.url).endsWith('/swagger-ui-init.js')) { |
|
res.set('Content-Type', 'application/javascript') |
|
res.send(swaggerInit) |
|
} else { |
|
next() |
|
} |
|
} |
|
|
|
var swaggerInitFunction = function (swaggerDoc, opts) { |
|
var swaggerInitFile = jsTplString.toString().replace('<% swaggerOptions %>', stringify(opts)) |
|
return function (req, res, next) { |
|
if (trimQuery(req.url).endsWith('/package.json')) { |
|
res.sendStatus(404) |
|
} else if (trimQuery(req.url).endsWith('/swagger-ui-init.js')) { |
|
if (req.swaggerDoc) { |
|
opts.swaggerDoc = req.swaggerDoc |
|
swaggerInitFile = jsTplString.toString().replace('<% swaggerOptions %>', stringify(opts)) |
|
} |
|
res.set('Content-Type', 'application/javascript') |
|
res.send(swaggerInitFile) |
|
} else { |
|
next() |
|
} |
|
} |
|
} |
|
|
|
|
|
var swaggerAssetMiddleware = options => { |
|
var opts = options || {} |
|
opts.index = false |
|
return express.static(getAbsoluteSwaggerFsPaths, opts) |
|
} |
|
|
|
var serveFiles = function (swaggerDoc, opts) { |
|
opts = opts || {} |
|
|
|
var initOptions = { |
|
swaggerDoc: swaggerDoc || undefined, |
|
customOptions: opts.swaggerOptions || {}, |
|
swaggerUrl: opts.swaggerUrl || {}, |
|
swaggerUrls: opts.swaggerUrls || undefined |
|
} |
|
var swaggerInitWithOpts = swaggerInitFunction(swaggerDoc, initOptions) |
|
return [swaggerInitWithOpts, swaggerAssetMiddleware()] |
|
} |
|
|
|
var serve = [swaggerInitFn, swaggerAssetMiddleware()] |
|
var serveWithOptions = options => [swaggerInitFn, swaggerAssetMiddleware(options)] |
|
|
|
var stringify = function (obj, prop) { |
|
var placeholder = '____FUNCTIONPLACEHOLDER____' |
|
var fns = [] |
|
var json = JSON.stringify(obj, function (key, value) { |
|
if (typeof value === 'function') { |
|
fns.push(value) |
|
return placeholder |
|
} |
|
return value |
|
}, 2) |
|
json = json.replace(new RegExp('"' + placeholder + '"', 'g'), function (_) { |
|
return fns.shift() |
|
}) |
|
return 'var options = ' + json + ';' |
|
} |
|
|
|
export { |
|
setup, |
|
serve, |
|
serveWithOptions, |
|
generateHTML, |
|
serveFiles |
|
}; |