11'use strict' ;
22
3- const http = require ( 'http' ) ;
4- const BBPromise = require ( 'bluebird' ) ;
53const express = require ( 'express' ) ;
6- const compression = require ( 'compression' ) ;
7- const bodyParser = require ( 'body-parser' ) ;
8- const fs = BBPromise . promisifyAll ( require ( 'fs' ) ) ;
9- const sUtil = require ( './lib/util' ) ;
10- const apiUtil = require ( './lib/api-util' ) ;
11- const packageInfo = require ( './package.json' ) ;
12- const yaml = require ( 'js-yaml' ) ;
13- const addShutdown = require ( 'http-shutdown' ) ;
14- const path = require ( 'path' ) ;
15-
16- /**
17- * Creates an express app and initialises it
18- *
19- * @param {Object } options the options to initialise the app with
20- * @return {bluebird } the promise resolving to the app object
21- */
22- function initApp ( options ) {
23-
24- // the main application object
25- const app = express ( ) ;
26-
27- // get the options and make them available in the app
28- app . logger = options . logger ; // the logging device
29- app . metrics = options . metrics ; // the metrics
30- app . conf = options . config ; // this app's config options
31- app . info = packageInfo ; // this app's package info
32-
33- // ensure some sane defaults
34- app . conf . port = app . conf . port || 8888 ;
35- app . conf . interface = app . conf . interface || '0.0.0.0' ;
36- // eslint-disable-next-line max-len
37- app . conf . compression_level = app . conf . compression_level === undefined ? 3 : app . conf . compression_level ;
38- app . conf . cors = app . conf . cors === undefined ? '*' : app . conf . cors ;
39- if ( app . conf . csp === undefined ) {
40- app . conf . csp = "default-src 'self'; object-src 'none'; media-src 'none'; img-src 'none'; style-src 'none'; base-uri 'self'; frame-ancestors 'self'" ;
41- }
42-
43- // set outgoing proxy
44- if ( app . conf . proxy ) {
45- process . env . HTTP_PROXY = app . conf . proxy ;
46- // if there is a list of domains which should
47- // not be proxied, set it
48- if ( app . conf . no_proxy_list ) {
49- if ( Array . isArray ( app . conf . no_proxy_list ) ) {
50- process . env . NO_PROXY = app . conf . no_proxy_list . join ( ',' ) ;
51- } else {
52- process . env . NO_PROXY = app . conf . no_proxy_list ;
53- }
54- }
55- }
56-
57- // set up header whitelisting for logging
58- if ( ! app . conf . log_header_whitelist ) {
59- app . conf . log_header_whitelist = [
60- 'cache-control' , 'content-type' , 'content-length' , 'if-match' ,
61- 'user-agent' , 'x-request-id'
62- ] ;
63- }
64- app . conf . log_header_whitelist = new RegExp ( `^(?:${ app . conf . log_header_whitelist . map ( ( item ) => {
65- return item . trim ( ) ;
66- } ) . join ( '|' ) } )$`, 'i' ) ;
67-
68- // set up the request templates for the APIs
69- apiUtil . setupApiTemplates ( app ) ;
70-
71- // set up the spec
72- if ( ! app . conf . spec ) {
73- app . conf . spec = `${ __dirname } /spec.yaml` ;
74- }
75- if ( app . conf . spec . constructor !== Object ) {
76- try {
77- app . conf . spec = yaml . safeLoad ( fs . readFileSync ( app . conf . spec ) ) ;
78- } catch ( e ) {
79- app . logger . log ( 'warn/spec' , `Could not load the spec: ${ e } ` ) ;
80- app . conf . spec = { } ;
81- }
82- }
83- if ( ! app . conf . spec . openapi ) {
84- app . conf . spec . openapi = '3.0.0' ;
85- }
86- if ( ! app . conf . spec . info ) {
87- app . conf . spec . info = {
88- version : app . info . version ,
89- title : app . info . name ,
90- description : app . info . description
91- } ;
92- }
93- app . conf . spec . info . version = app . info . version ;
94- if ( ! app . conf . spec . paths ) {
95- app . conf . spec . paths = { } ;
96- }
97-
98- // set the CORS and CSP headers
99- app . all ( '*' , ( req , res , next ) => {
100- if ( app . conf . cors !== false ) {
101- res . header ( 'access-control-allow-origin' , app . conf . cors ) ;
102- res . header ( 'access-control-allow-headers' , 'accept, x-requested-with, content-type' ) ;
103- res . header ( 'access-control-expose-headers' , 'etag' ) ;
104- }
105- if ( app . conf . csp !== false ) {
106- res . header ( 'x-xss-protection' , '1; mode=block' ) ;
107- res . header ( 'x-content-type-options' , 'nosniff' ) ;
108- res . header ( 'x-frame-options' , 'SAMEORIGIN' ) ;
109- res . header ( 'content-security-policy' , app . conf . csp ) ;
110- }
111- sUtil . initAndLogRequest ( req , app ) ;
112- next ( ) ;
113- } ) ;
114-
115- // set up the user agent header string to use for requests
116- app . conf . user_agent = app . conf . user_agent || app . info . name ;
117-
118- // disable the X-Powered-By header
119- app . set ( 'x-powered-by' , false ) ;
120- // disable the ETag header - users should provide them!
121- app . set ( 'etag' , false ) ;
122- // enable compression
123- app . use ( compression ( { level : app . conf . compression_level } ) ) ;
124- // use the JSON body parser
125- app . use ( bodyParser . json ( { limit : app . conf . max_body_size || '100kb' } ) ) ;
126- // use the application/x-www-form-urlencoded parser
127- app . use ( bodyParser . urlencoded ( { extended : true } ) ) ;
128-
129- return BBPromise . resolve ( app ) ;
130-
131- }
132-
133- /**
134- * Loads all routes declared in routes/ into the app
135- *
136- * @param {Application } app the application object to load routes into
137- * @param {string } dir routes folder
138- * @return {bluebird } a promise resolving to the app object
139- */
140- function loadRoutes ( app , dir ) {
141-
142- // recursively load routes from .js files under routes/
143- return fs . readdirAsync ( dir ) . map ( ( fname ) => {
144- return BBPromise . try ( ( ) => {
145- const resolvedPath = path . resolve ( dir , fname ) ;
146- const isDirectory = fs . statSync ( resolvedPath ) . isDirectory ( ) ;
147- if ( isDirectory ) {
148- loadRoutes ( app , resolvedPath ) ;
149- } else if ( / \. j s $ / . test ( fname ) ) {
150- // import the route file
151- const route = require ( `${ dir } /${ fname } ` ) ;
152- return route ( app ) ;
153- }
154- } ) . then ( ( route ) => {
155- if ( route === undefined ) {
156- return undefined ;
157- }
158- // check that the route exports the object we need
159- if ( route . constructor !== Object || ! route . path || ! route . router ||
160- ! ( route . api_version || route . skip_domain ) ) {
161- throw new TypeError ( `routes/${ fname } does not export the correct object!` ) ;
162- }
163- // normalise the path to be used as the mount point
164- if ( route . path [ 0 ] !== '/' ) {
165- route . path = `/${ route . path } ` ;
166- }
167- if ( route . path [ route . path . length - 1 ] !== '/' ) {
168- route . path = `${ route . path } /` ;
169- }
170- if ( ! route . skip_domain ) {
171- route . path = `/:domain/v${ route . api_version } ${ route . path } ` ;
172- }
173- // wrap the route handlers with Promise.try() blocks
174- sUtil . wrapRouteHandlers ( route , app ) ;
175- // all good, use that route
176- app . use ( route . path , route . router ) ;
177- } ) ;
178- } ) . then ( ( ) => {
179- // catch errors
180- sUtil . setErrorHandler ( app ) ;
181- // route loading is now complete, return the app object
182- return BBPromise . resolve ( app ) ;
183- } ) ;
184-
185- }
186-
187- /**
188- * Creates and start the service's web server
189- *
190- * @param {Application } app the app object to use in the service
191- * @return {bluebird } a promise creating the web server
192- */
193- function createServer ( app ) {
194-
195- // return a promise which creates an HTTP server,
196- // attaches the app to it, and starts accepting
197- // incoming client requests
198- let server ;
199- return new BBPromise ( ( resolve ) => {
200- server = http . createServer ( app ) . listen (
201- app . conf . port ,
202- app . conf . interface ,
203- resolve
204- ) ;
205- server = addShutdown ( server ) ;
206- } ) . then ( ( ) => {
207- app . logger . log ( 'info' ,
208- `Worker ${ process . pid } listening on ${ app . conf . interface || '*' } :${ app . conf . port } ` ) ;
209-
210- // Don't delay incomplete packets for 40ms (Linux default) on
211- // pipelined HTTP sockets. We write in large chunks or buffers, so
212- // lack of coalescing should not be an issue here.
213- server . on ( 'connection' , ( socket ) => {
214- socket . setNoDelay ( true ) ;
215- } ) ;
216-
217- return server ;
218- } ) ;
219-
220- }
4+ const init = require ( 'servicelib-node/init' ) ;
2215
2226/**
2237 * The service's entry point. It takes over the configuration
@@ -229,13 +13,11 @@ function createServer(app) {
22913 * @return {bluebird } HTTP server
23014 */
23115module . exports = ( options ) => {
232-
233- return initApp ( options )
234- . then ( ( app ) => loadRoutes ( app , `${ __dirname } /routes` ) )
16+ return init . initApp ( options )
17+ . then ( ( app ) => init . loadRoutes ( app , `${ __dirname } /routes` ) )
23518 . then ( ( app ) => {
23619 // serve static files from static/
23720 app . use ( '/static' , express . static ( `${ __dirname } /static` ) ) ;
23821 return app ;
239- } ) . then ( createServer ) ;
240-
22+ } ) . then ( init . createServer ) ;
24123} ;
0 commit comments