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.

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