11const http = require ( 'http' ) ;
2- const https = require ( 'https' ) ;
3- const fs = require ( 'fs' ) ;
4- const autoBind = require ( 'auto-bind' ) ;
5- const _ = require ( 'lodash' ) ;
6- const Koa = require ( 'koa' ) ;
2+ const http2 = require ( 'spdy' ) ;
3+
74const Cabin = require ( 'cabin' ) ;
5+ const I18N = require ( '@ladjs/i18n' ) ;
6+ const Koa = require ( 'koa' ) ;
7+ const StoreIPAddress = require ( '@ladjs/store-ip-address' ) ;
8+ const Timeout = require ( 'koa-better-timeout' ) ;
9+ const _ = require ( 'lodash' ) ;
10+ const auth = require ( 'koa-basic-auth' ) ;
11+ const autoBind = require ( 'auto-bind' ) ;
12+ const bodyParser = require ( 'koa-bodyparser' ) ;
813const boolean = require ( 'boolean' ) ;
9- const conditional = require ( 'koa-conditional-get' ) ;
10- const etag = require ( 'koa-etag' ) ;
1114const compress = require ( 'koa-compress' ) ;
12- const responseTime = require ( 'koa-response-time' ) ;
13- const rateLimiter = require ( 'koa-simple-ratelimit' ) ;
14- const koaLogger = require ( 'koa-logger' ) ;
15- const bodyParser = require ( 'koa-bodyparser' ) ;
16- const koa404Handler = require ( 'koa-404-handler' ) ;
17- const json = require ( 'koa-json' ) ;
15+ const conditional = require ( 'koa-conditional-get' ) ;
16+ const cors = require ( 'kcors' ) ;
1817const errorHandler = require ( 'koa-better-error-handler' ) ;
18+ const etag = require ( 'koa-etag' ) ;
1919const helmet = require ( 'koa-helmet' ) ;
20- const cors = require ( 'kcors' ) ;
21- const removeTrailingSlashes = require ( 'koa-no-trailing-slash' ) ;
22- const redis = require ( 'redis' ) ;
23- const StoreIPAddress = require ( '@ladjs/store-ip-address' ) ;
2420const ip = require ( 'ip' ) ;
25- const Timeout = require ( 'koa-better-timeout' ) ;
26- const I18N = require ( '@ladjs/i18n' ) ;
27- const { oneLine } = require ( 'common-tags' ) ;
28-
29- const env = process . env . NODE_ENV || 'development' ;
30-
31- let rateLimit = {
32- duration : process . env . RATELIMIT_DURATION
33- ? parseInt ( process . env . RATELIMIT_DURATION , 10 )
34- : 60000 ,
35- max : process . env . RATELIMIT_MAX
36- ? parseInt ( process . env . RATELIMIT_MAX , 10 )
37- : 100 ,
38- id : ctx => ctx . ip ,
39- prefix : process . env . RATELIMIT_PREFIX
40- ? process . env . RATELIMIT_PREFIX
41- : `limit_${ env . toLowerCase ( ) } `
42- } ;
43-
44- if ( env === 'development' ) rateLimit = false ;
45-
46- class Server {
21+ const json = require ( 'koa-json' ) ;
22+ const koa404Handler = require ( 'koa-404-handler' ) ;
23+ const koaConnect = require ( 'koa-connect' ) ;
24+ const rateLimiter = require ( 'koa-simple-ratelimit' ) ;
25+ const redis = require ( 'redis' ) ;
26+ const removeTrailingSlashes = require ( 'koa-no-trailing-slash' ) ;
27+ const requestId = require ( 'express-request-id' ) ;
28+ const responseTime = require ( 'response-time' ) ;
29+ const sharedConfig = require ( '@ladjs/shared-config' ) ;
30+
31+ class API {
4732 constructor ( config ) {
48- this . config = Object . assign (
49- {
50- cabin : { axe : { capture : false } } ,
51- protocol : process . env . API_PROTOCOL || 'http' ,
52- ssl : {
53- key : process . env . API_SSL_KEY_PATH
54- ? fs . readFileSync ( process . env . API_SSL_KEY_PATH )
55- : null ,
56- cert : process . env . API_SSL_CERT_PATH
57- ? fs . readFileSync ( process . env . API_SSL_CERT_PATH )
58- : null ,
59- ca : process . env . API_SSL_CA_PATH
60- ? fs . readFileSync ( process . env . API_SSL_CA_PATH )
61- : null
62- } ,
63- routes : false ,
64- logger : console ,
65- passport : false ,
66- i18n : { } ,
67- rateLimit,
68- // <https://github.com/koajs/cors#corsoptions>
69- cors : { } ,
70- timeoutMs : process . env . API_TIMEOUT_MS
71- ? parseInt ( process . env . API_TIMEOUT_MS , 10 )
72- : 2000
73- } ,
74- config
75- ) ;
33+ this . config = {
34+ ...sharedConfig ( 'API' ) ,
35+ ...config
36+ } ;
7637
7738 const { logger } = this . config ;
7839 const storeIPAddress = new StoreIPAddress ( { logger } ) ;
79- const i18n = this . config . i18n . config
80- ? this . config . i18n
81- : new I18N ( { ...this . config . i18n , logger } ) ;
8240 const cabin = new Cabin ( {
8341 logger,
8442 ...this . config . cabin
@@ -102,31 +60,42 @@ class Server {
10260 // later on with `server.close()`
10361 let server ;
10462
105- app . on ( 'error' , logger . contextError || logger . error ) ;
63+ // listen for error and log events emitted by app
64+ app . on ( 'error' , ( err , ctx ) => ctx . logger . error ( err ) ) ;
10665 app . on ( 'log' , logger . log ) ;
10766
10867 // only trust proxy if enabled
10968 app . proxy = boolean ( process . env . TRUST_PROXY ) ;
11069
111- // compress/gzip
112- app . use ( compress ( ) ) ;
113-
114- // setup localization
115- app . use ( i18n . middleware ) ;
116-
11770 // specify that this is our api (used by error handler)
11871 app . context . api = true ;
11972
12073 // override koa's undocumented error handler
121- // TODO: <https://github.com/sindresorhus/eslint-plugin-unicorn/issues/174>
74+ // <https://github.com/sindresorhus/eslint-plugin-unicorn/issues/174>
12275 // eslint-disable-next-line unicorn/prefer-add-event-listener
12376 app . context . onerror = errorHandler ;
12477
125- // response time
126- app . use ( responseTime ( ) ) ;
78+ // adds `X-Response-Time` header to responses
79+ app . use ( koaConnect ( responseTime ) ) ;
12780
128- // request logger with custom logger
129- app . use ( koaLogger ( { logger } ) ) ;
81+ // adds or re-uses `X-Request-Id` header
82+ app . use ( koaConnect ( requestId ( ) ) ) ;
83+
84+ // use the cabin middleware (adds request-based logging and helpers)
85+ app . use ( cabin . middleware ) ;
86+
87+ // compress/gzip
88+ app . use ( compress ( ) ) ;
89+
90+ // setup localization
91+ if ( this . config . i18n ) {
92+ const i18n = this . config . i18n . config
93+ ? this . config . i18n
94+ : new I18N ( { ...this . config . i18n , logger } ) ;
95+ app . use ( i18n . middleware ) ;
96+ }
97+
98+ if ( this . config . auth ) app . use ( auth ( this . config . auth ) ) ;
13099
131100 // rate limiting
132101 if ( this . config . rateLimit )
@@ -146,9 +115,6 @@ class Server {
146115 // cors
147116 if ( this . config . cors ) app . use ( cors ( this . config . cors ) ) ;
148117
149- // TODO: add `cors-gate`
150- // <https://github.com/mixmaxhq/cors-gate/issues/6>
151-
152118 // security
153119 app . use ( helmet ( ) ) ;
154120
@@ -161,34 +127,25 @@ class Server {
161127 // pretty-printed json responses
162128 app . use ( json ( ) ) ;
163129
164- // add cabin middleware
165- app . use ( cabin . middleware ) ;
166-
167130 // 404 handler
168131 app . use ( koa404Handler ) ;
169132
170133 // passport
171134 if ( this . config . passport ) app . use ( this . config . passport . initialize ( ) ) ;
172135
173136 // configure timeout
174- app . use ( async ( ctx , next ) => {
175- try {
176- const timeout = new Timeout ( {
177- ms : this . config . timeoutMs ,
178- message : ctx . req . t (
179- oneLine `Sorry, your request has timed out.
180- We have been alerted of this issue. Please try again.`
181- )
182- } ) ;
183- await timeout . middleware ( ctx , next ) ;
184- } catch ( err ) {
185- ctx . throw ( err ) ;
186- }
187- } ) ;
137+ if ( this . config . timeout ) {
138+ const timeout = new Timeout ( this . config . timeout ) ;
139+ app . use ( timeout . middleware ) ;
140+ }
188141
189142 // store the user's last ip address in the background
190143 app . use ( storeIPAddress . middleware ) ;
191144
145+ // allow before hooks to get setup
146+ if ( _ . isFunction ( this . config . hookBeforeRoutes ) )
147+ this . config . hookBeforeRoutes ( app ) ;
148+
192149 // mount the app's defined and nested routes
193150 if ( this . config . routes ) {
194151 if ( _ . isFunction ( this . config . routes . routes ) )
@@ -198,16 +155,13 @@ class Server {
198155
199156 // start server on either http or https
200157 if ( this . config . protocol === 'https' )
201- server = https . createServer ( this . config . ssl , app . callback ( ) ) ;
158+ server = http2 . createServer ( this . config . ssl , app . callback ( ) ) ;
202159 else server = http . createServer ( app . callback ( ) ) ;
203160
204161 // expose app and server
205162 this . app = app ;
206163 this . server = server ;
207164
208- // Expose app so we can test it without the server wrapper
209- if ( env === 'test' ) this . app = app ;
210-
211165 autoBind ( this ) ;
212166 }
213167
@@ -222,7 +176,7 @@ class Server {
222176 fn = function ( ) {
223177 const { port } = this . address ( ) ;
224178 logger . info (
225- `api server listening on ${ port } (LAN: ${ ip . address ( ) } :${ port } )`
179+ `Lad API server listening on ${ port } (LAN: ${ ip . address ( ) } :${ port } )`
226180 ) ;
227181 } ;
228182
@@ -236,4 +190,4 @@ class Server {
236190 }
237191}
238192
239- module . exports = Server ;
193+ module . exports = API ;
0 commit comments