You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
4.0 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. 'use strict'
  2. const path = require('path')
  3. const http = require('http')
  4. const url = require('url')
  5. const Koa = require('koa')
  6. const serve = require('koa-static')
  7. const convert = require('koa-convert')
  8. const serveIndex = require('koa-serve-index')
  9. const morgan = require('koa-morgan')
  10. const compress = require('koa-compress')
  11. const streamLogStats = require('stream-log-stats')
  12. const cors = require('kcors')
  13. const conditional = require('koa-conditional-get');
  14. const etag = require('koa-etag');
  15. const _ = require('koa-route')
  16. const mount = require('koa-mount')
  17. const httpProxy = require('http-proxy')
  18. const pathToRegexp = require('path-to-regexp')
  19. /**
  20. * @module local-web-server
  21. */
  22. module.exports = getApp
  23. function getApp (options) {
  24. options = Object.assign({
  25. static: {},
  26. serveIndex: {},
  27. log: {},
  28. compress: false,
  29. blacklist: [],
  30. directories: [],
  31. proxyRoutes: []
  32. }, options)
  33. const log = options.log
  34. log.options = log.options || {}
  35. const app = new Koa()
  36. const _use = app.use
  37. app.use = x => _use.call(app, convert(x))
  38. const proxy = httpProxy.createProxyServer({
  39. changeOrigin: true
  40. })
  41. /* Proxy routes */
  42. options.proxyRoutes.forEach(route => {
  43. app.use(_.all(route.from, function * () {
  44. const keys = []
  45. route.re = pathToRegexp(route.from, keys)
  46. route.new = route.to
  47. this.response = false
  48. keys.forEach((key, index) => {
  49. const re = {
  50. token: RegExp('\\$\\{' + key.name + '\\}', 'g'),
  51. index: RegExp('\\$\\{' + index + '\\}', 'g')
  52. }
  53. route.new = route.new
  54. .replace(re.token, arguments[index] || '')
  55. .replace(re.index, arguments[index] || '')
  56. })
  57. proxy.once('proxyReq', function (proxyReq) {
  58. proxyReq.path = url.parse(route.new).path;
  59. })
  60. proxy.web(this.req, this.res, { target: route.new })
  61. }))
  62. })
  63. /* CORS: allow from any origin */
  64. app.use(cors())
  65. /* path blacklist */
  66. if (options.blacklist.length) {
  67. app.use(function pathBlacklist (ctx, next) {
  68. if (options.blacklist.some(regexp => regexp.test(ctx.path))) {
  69. ctx.throw(403, http.STATUS_CODES[403])
  70. } else {
  71. return next()
  72. }
  73. })
  74. }
  75. app.use(conditional())
  76. app.use(etag())
  77. /* mime-type overrides */
  78. if (options.mime) {
  79. app.use((ctx, next) => {
  80. return next().then(() => {
  81. const reqPathExtension = path.extname(ctx.path).slice(1)
  82. Object.keys(options.mime).forEach(mimeType => {
  83. const extsToOverride = options.mime[mimeType]
  84. if (extsToOverride.indexOf(reqPathExtension) > -1) ctx.type = mimeType
  85. })
  86. })
  87. })
  88. }
  89. /* compress response */
  90. if (options.compress) {
  91. app.use(compress())
  92. }
  93. /* special case log formats */
  94. if (log.format) {
  95. if (log.format === 'none'){
  96. log.format = undefined
  97. } else if (log.format === 'logstalgia') {
  98. morgan.token('date', logstalgiaDate)
  99. log.format = 'combined'
  100. }
  101. /* if no specific log format was requested, show log stats */
  102. } else {
  103. log.format = 'common'
  104. log.options.stream = streamLogStats({ refreshRate: 500 })
  105. }
  106. if (log.format) app.use(morgan.middleware(log.format, log.options))
  107. // options.static.root = [
  108. // { route: '/one', root: 'lib' },
  109. // { route: '/two', root: 'node_modules' }
  110. // ]
  111. /* serve static files */
  112. if (options.static.root) {
  113. app.use(serve(options.static.root, options.static.options))
  114. // options.static.root.forEach(config => {
  115. // app.use(mount(config.route, serve(config.root)))
  116. // app.use(mount(config.route, serveIndex(config.root)))
  117. // })
  118. }
  119. /* serve directory index */
  120. if (options.serveIndex.path) {
  121. app.use(serveIndex(options.serveIndex.path, options.serveIndex.options))
  122. }
  123. return app
  124. }
  125. function logstalgiaDate () {
  126. var d = new Date()
  127. return (`${d.getDate()}/${d.getUTCMonth()}/${d.getFullYear()}:${d.toTimeString()}`)
  128. .replace('GMT', '')
  129. .replace(' (BST)', '')
  130. }
  131. process.on('unhandledRejection', (reason, p) => {
  132. throw reason
  133. })