1 |
|
2 | 'use strict'
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | const Boom = require('boom')
|
23 | const Hapi = require('hapi')
|
24 | const pify = require('pify')
|
25 |
|
26 | const apis = require('./apis.js')
|
27 | const handlers = require('./handlers.js')
|
28 | const values = require('./values.js')
|
29 | const wrapper = require('./wrapper.js')
|
30 | const variables = require('./variables.js')
|
31 | const awsRoles = require('./assume-aws-roles.js')
|
32 |
|
33 | function normaliseHapiCors (
|
34 | cors /* : CorsConfiguration | false */
|
35 | ) /* : HapiCorsConfiguration | false */ {
|
36 | if (!cors) {
|
37 | return false
|
38 | }
|
39 | return {
|
40 | credentials: cors.credentials,
|
41 | exposedHeaders: cors.exposedHeaders,
|
42 | headers: cors.headers,
|
43 | maxAge: cors.maxAge,
|
44 | origin: cors.origins
|
45 | }
|
46 | }
|
47 |
|
48 |
|
49 | function normaliseHapiRequest (
|
50 | request /* : any */,
|
51 | params /* : { [id:string]: string } */
|
52 | ) /* : BmRequest */ {
|
53 | const urlInfo = Object.assign({}, request.url, {
|
54 | host: request.info.host,
|
55 | hostname: request.info.hostname,
|
56 | params,
|
57 | protocol: wrapper.protocolFromHeaders(request.headers)
|
58 | })
|
59 | delete urlInfo.auth
|
60 | delete urlInfo.hash
|
61 | delete urlInfo.href
|
62 | delete urlInfo.path
|
63 | delete urlInfo.port
|
64 | delete urlInfo.search
|
65 | delete urlInfo.slashes
|
66 | return {
|
67 | body: request.payload,
|
68 | headers: request.headers,
|
69 | method: request.method,
|
70 | route: '',
|
71 | url: urlInfo
|
72 | }
|
73 | }
|
74 |
|
75 | function startServer (
|
76 | logger/* : typeof console */,
|
77 | options /* : {
|
78 | cors: CorsConfiguration | false,
|
79 | cwd: string,
|
80 | env: string,
|
81 | port: string | number
|
82 | } */,
|
83 | config ,
|
84 | blinkMobileIdentity ,
|
85 | env
|
86 | ) {
|
87 | return Promise.resolve()
|
88 | .then(() => {
|
89 | if (config.awsProfile) {
|
90 |
|
91 | process.env.AWS_PROFILE = config.awsProfile
|
92 | } else {
|
93 | return blinkMobileIdentity.getAccessToken()
|
94 | .then((accessToken) => {
|
95 | return awsRoles.assumeAWSRoleToServeLocally(config, env, accessToken)
|
96 | })
|
97 | .then((results) => {
|
98 |
|
99 | process.env.AWS_ACCESS_KEY_ID = results.accessKeyId
|
100 | process.env.AWS_SECRET_ACCESS_KEY = results.secretAccessKey
|
101 | process.env.AWS_SESSION_TOKEN = results.sessionToken
|
102 | })
|
103 | }
|
104 | })
|
105 | .then(() => {
|
106 | options = options || {}
|
107 | const server = new Hapi.Server()
|
108 | server.connection({ port: options.port })
|
109 | server.route({
|
110 | config: {
|
111 | cors: normaliseHapiCors(options.cors)
|
112 | },
|
113 | handler (request, reply) {
|
114 |
|
115 | const cwd = options.cwd
|
116 | const route = request.params.route
|
117 | if (!route) {
|
118 | reply(Boom.notImplemented('Must supply a route'))
|
119 | return
|
120 | }
|
121 |
|
122 | apis.getRouteConfig(cwd, route)
|
123 | .catch((err) => Promise.reject(Boom.boomify(err, {
|
124 | statusCode: 404
|
125 | })))
|
126 | .then((routeConfig) => {
|
127 | return apis.getHandlerConfig(routeConfig, request.method)
|
128 | .then((handlerConfig) => {
|
129 | const handler = handlerConfig.handler
|
130 | if (typeof handler !== 'function') {
|
131 | reply(Boom.methodNotAllowed(`${request.method.toUpperCase()} method has not been implemented`))
|
132 | return
|
133 | }
|
134 | const bmRequest = normaliseHapiRequest(request, handlerConfig.params)
|
135 | bmRequest.route = routeConfig.route
|
136 | return handlers.executeHandler(handler, bmRequest)
|
137 | .then((bmResponse) => {
|
138 | const response = reply(null, bmResponse.payload)
|
139 | .code(bmResponse.statusCode)
|
140 | .type('application/json')
|
141 | Object.keys(bmResponse.headers).forEach((key) => response.header(key, bmResponse.headers[key]))
|
142 | })
|
143 | })
|
144 | })
|
145 | .catch((err) => {
|
146 | if (!err || !err.isBoom) {
|
147 | err = Boom.boomify(err, {
|
148 | statusCode: 500
|
149 | })
|
150 | }
|
151 |
|
152 |
|
153 | err.output.payload.message = err.message
|
154 |
|
155 | logger.error(err.stack)
|
156 | if (err.data) {
|
157 | logger.error('Boom Data:', JSON.stringify(err.data, null, 2))
|
158 | }
|
159 | reply(err)
|
160 | })
|
161 | },
|
162 |
|
163 | method: values.METHODS.filter((method) => method !== 'OPTIONS'),
|
164 | path: '/{route*}'
|
165 | })
|
166 | return server.register({
|
167 | register: require('good'),
|
168 | options: {
|
169 | ops: false,
|
170 | reporters: {
|
171 | console: [{
|
172 | module: 'good-console'
|
173 | }, 'stdout']
|
174 | }
|
175 | }
|
176 | })
|
177 | .then(() => variables.setToCurrentProcess(logger, options.cwd, options.env))
|
178 | .then(() => pify(server.start.bind(server))())
|
179 | .then(() => server)
|
180 | })
|
181 | }
|
182 |
|
183 | module.exports = {
|
184 | normaliseHapiCors,
|
185 | normaliseHapiRequest,
|
186 | startServer
|
187 | }
|