index.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. 'use strict'
  2. var express = require('express')
  3. var getAbsoluteSwaggerFsPath = require('swagger-ui-dist/absolute-path')
  4. var favIconHtml = '<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />' +
  5. '<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />'
  6. var swaggerInit = ''
  7. function trimQuery(q) {
  8. return q && q.split('?')[0]
  9. }
  10. var htmlTplString = `
  11. <!-- HTML for static distribution bundle build -->
  12. <!DOCTYPE html>
  13. <html lang="en">
  14. <head>
  15. <meta charset="UTF-8">
  16. <% robotsMetaString %>
  17. <title><% title %></title>
  18. <link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
  19. <% favIconString %>
  20. <style>
  21. html
  22. {
  23. box-sizing: border-box;
  24. overflow: -moz-scrollbars-vertical;
  25. overflow-y: scroll;
  26. }
  27. *,
  28. *:before,
  29. *:after
  30. {
  31. box-sizing: inherit;
  32. }
  33. body {
  34. margin:0;
  35. background: #fafafa;
  36. }
  37. </style>
  38. </head>
  39. <body>
  40. <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
  41. <defs>
  42. <symbol viewBox="0 0 20 20" id="unlocked">
  43. <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>
  44. </symbol>
  45. <symbol viewBox="0 0 20 20" id="locked">
  46. <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"/>
  47. </symbol>
  48. <symbol viewBox="0 0 20 20" id="close">
  49. <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"/>
  50. </symbol>
  51. <symbol viewBox="0 0 20 20" id="large-arrow">
  52. <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"/>
  53. </symbol>
  54. <symbol viewBox="0 0 20 20" id="large-arrow-down">
  55. <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"/>
  56. </symbol>
  57. <symbol viewBox="0 0 24 24" id="jump-to">
  58. <path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
  59. </symbol>
  60. <symbol viewBox="0 0 24 24" id="expand">
  61. <path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
  62. </symbol>
  63. </defs>
  64. </svg>
  65. <div id="swagger-ui"></div>
  66. <script src="./swagger-ui-bundle.js"> </script>
  67. <script src="./swagger-ui-standalone-preset.js"> </script>
  68. <script src="./swagger-ui-init.js"> </script>
  69. <% customJs %>
  70. <% customJsStr %>
  71. <% customCssUrl %>
  72. <style>
  73. <% customCss %>
  74. </style>
  75. </body>
  76. </html>
  77. `
  78. var jsTplString = `
  79. window.onload = function() {
  80. // Build a system
  81. var url = window.location.search.match(/url=([^&]+)/);
  82. if (url && url.length > 1) {
  83. url = decodeURIComponent(url[1]);
  84. } else {
  85. url = window.location.origin;
  86. }
  87. <% swaggerOptions %>
  88. url = options.swaggerUrl || url
  89. var urls = options.swaggerUrls
  90. var customOptions = options.customOptions
  91. var spec1 = options.swaggerDoc
  92. var swaggerOptions = {
  93. spec: spec1,
  94. url: url,
  95. urls: urls,
  96. dom_id: '#swagger-ui',
  97. deepLinking: true,
  98. presets: [
  99. SwaggerUIBundle.presets.apis,
  100. SwaggerUIStandalonePreset
  101. ],
  102. plugins: [
  103. SwaggerUIBundle.plugins.DownloadUrl
  104. ],
  105. layout: "StandaloneLayout"
  106. }
  107. for (var attrname in customOptions) {
  108. swaggerOptions[attrname] = customOptions[attrname];
  109. }
  110. var ui = SwaggerUIBundle(swaggerOptions)
  111. if (customOptions.oauth) {
  112. ui.initOAuth(customOptions.oauth)
  113. }
  114. if (customOptions.preauthorizeApiKey) {
  115. const key = customOptions.preauthorizeApiKey.authDefinitionKey;
  116. const value = customOptions.preauthorizeApiKey.apiKeyValue;
  117. if (!!key && !!value) {
  118. const pid = setInterval(() => {
  119. const authorized = ui.preauthorizeApiKey(key, value);
  120. if(!!authorized) clearInterval(pid);
  121. }, 500)
  122. }
  123. }
  124. if (customOptions.authAction) {
  125. ui.authActions.authorize(customOptions.authAction)
  126. }
  127. window.ui = ui
  128. }
  129. `
  130. function toExternalScriptTag(url) {
  131. return `<script src='${url}'></script>`
  132. }
  133. function toInlineScriptTag(jsCode) {
  134. return `<script>${jsCode}</script>`
  135. }
  136. function toExternalStylesheetTag(url) {
  137. return `<link href='${url}' rel='stylesheet'>`
  138. }
  139. function toTags(customCode, toScript) {
  140. if (typeof customCode === 'string') {
  141. return toScript(customCode)
  142. } else if (Array.isArray(customCode)) {
  143. return customCode.map(toScript).join('\n')
  144. } else {
  145. return ''
  146. }
  147. }
  148. var generateHTML = function (swaggerDoc, opts, options, customCss, customfavIcon, swaggerUrl, customSiteTitle, _htmlTplString, _jsTplString) {
  149. var isExplorer
  150. var customJs
  151. var customJsStr
  152. var swaggerUrls
  153. var customCssUrl
  154. var customRobots
  155. if (opts && typeof opts === 'object') {
  156. options = opts.swaggerOptions
  157. customCss = opts.customCss
  158. customJs = opts.customJs
  159. customJsStr = opts.customJsStr
  160. customfavIcon = opts.customfavIcon
  161. customRobots = opts.customRobots
  162. swaggerUrl = opts.swaggerUrl
  163. swaggerUrls = opts.swaggerUrls
  164. isExplorer = opts.explorer || !!swaggerUrls
  165. customSiteTitle = opts.customSiteTitle
  166. customCssUrl = opts.customCssUrl
  167. } else {
  168. //support legacy params based function
  169. isExplorer = opts
  170. }
  171. options = options || {}
  172. var explorerString = isExplorer ? '' : '.swagger-ui .topbar .download-url-wrapper { display: none }'
  173. customCss = explorerString + ' ' + customCss || explorerString
  174. customfavIcon = customfavIcon || false
  175. customSiteTitle = customSiteTitle || 'Swagger UI'
  176. _htmlTplString = _htmlTplString || htmlTplString
  177. _jsTplString = _jsTplString || jsTplString
  178. var robotsMetaString = customRobots ? '<meta name="robots" content="' + customRobots + '" />' : ''
  179. var favIconString = customfavIcon ? '<link rel="icon" href="' + customfavIcon + '" />' : favIconHtml
  180. var htmlWithCustomCss = _htmlTplString.toString().replace('<% customCss %>', customCss)
  181. var htmlWithCustomRobots = htmlWithCustomCss.replace('<% robotsMetaString %>', robotsMetaString)
  182. var htmlWithFavIcon = htmlWithCustomRobots.replace('<% favIconString %>', favIconString)
  183. var htmlWithCustomJsUrl = htmlWithFavIcon.replace('<% customJs %>', toTags(customJs, toExternalScriptTag))
  184. var htmlWithCustomJs = htmlWithCustomJsUrl.replace('<% customJsStr %>', toTags(customJsStr, toInlineScriptTag))
  185. var htmlWithCustomCssUrl = htmlWithCustomJs.replace('<% customCssUrl %>', toTags(customCssUrl, toExternalStylesheetTag))
  186. var initOptions = {
  187. swaggerDoc: swaggerDoc || undefined,
  188. customOptions: options,
  189. swaggerUrl: swaggerUrl || undefined,
  190. swaggerUrls: swaggerUrls || undefined
  191. }
  192. swaggerInit = _jsTplString.toString().replace('<% swaggerOptions %>', stringify(initOptions))
  193. return htmlWithCustomCssUrl.replace('<% title %>', customSiteTitle)
  194. }
  195. var setup = function (swaggerDoc, opts, options, customCss, customfavIcon, swaggerUrl, customSiteTitle) {
  196. var html = generateHTML(swaggerDoc, opts, options, customCss, customfavIcon, swaggerUrl, customSiteTitle, htmlTplString, jsTplString)
  197. return function (req, res) {
  198. if (req.swaggerDoc) {
  199. var reqHtml = generateHTML(req.swaggerDoc, opts, options, customCss, customfavIcon, swaggerUrl, customSiteTitle, htmlTplString, jsTplString)
  200. res.send(reqHtml)
  201. } else {
  202. res.send(html)
  203. }
  204. }
  205. }
  206. function swaggerInitFn(req, res, next) {
  207. if (trimQuery(req.url).endsWith('/package.json')) {
  208. res.sendStatus(404)
  209. } else if (trimQuery(req.url).endsWith('/swagger-ui-init.js')) {
  210. res.set('Content-Type', 'application/javascript')
  211. res.send(swaggerInit)
  212. } else {
  213. next()
  214. }
  215. }
  216. var swaggerInitFunction = function (swaggerDoc, opts) {
  217. var swaggerInitFile = jsTplString.toString().replace('<% swaggerOptions %>', stringify(opts))
  218. return function (req, res, next) {
  219. if (trimQuery(req.url).endsWith('/package.json')) {
  220. res.sendStatus(404)
  221. } else if (trimQuery(req.url).endsWith('/swagger-ui-init.js')) {
  222. if (req.swaggerDoc) {
  223. opts.swaggerDoc = req.swaggerDoc
  224. swaggerInitFile = jsTplString.toString().replace('<% swaggerOptions %>', stringify(opts))
  225. }
  226. res.set('Content-Type', 'application/javascript')
  227. res.send(swaggerInitFile)
  228. } else {
  229. next()
  230. }
  231. }
  232. }
  233. var swaggerAssetMiddleware = options => {
  234. var opts = options || {}
  235. opts.index = false
  236. return express.static(getAbsoluteSwaggerFsPath(), opts)
  237. }
  238. var serveFiles = function (swaggerDoc, opts) {
  239. opts = opts || {}
  240. var initOptions = {
  241. swaggerDoc: swaggerDoc || undefined,
  242. customOptions: opts.swaggerOptions || {},
  243. swaggerUrl: opts.swaggerUrl || {},
  244. swaggerUrls: opts.swaggerUrls || undefined
  245. }
  246. var swaggerInitWithOpts = swaggerInitFunction(swaggerDoc, initOptions)
  247. return [swaggerInitWithOpts, swaggerAssetMiddleware()]
  248. }
  249. var serve = [swaggerInitFn, swaggerAssetMiddleware()]
  250. var serveWithOptions = options => [swaggerInitFn, swaggerAssetMiddleware(options)]
  251. var stringify = function (obj, prop) {
  252. var placeholder = '____FUNCTIONPLACEHOLDER____'
  253. var fns = []
  254. var json = JSON.stringify(obj, function (key, value) {
  255. if (typeof value === 'function') {
  256. fns.push(value)
  257. return placeholder
  258. }
  259. return value
  260. }, 2)
  261. json = json.replace(new RegExp('"' + placeholder + '"', 'g'), function (_) {
  262. return fns.shift()
  263. })
  264. return 'var options = ' + json + ';'
  265. }
  266. module.exports = {
  267. setup: setup,
  268. serve: serve,
  269. serveWithOptions: serveWithOptions,
  270. generateHTML: generateHTML,
  271. serveFiles: serveFiles
  272. }