@ladjs/api 8.0.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +49 -25
  2. package/package.json +14 -12
package/index.js CHANGED
@@ -5,6 +5,7 @@ const util = require('util');
5
5
 
6
6
  const Cabin = require('cabin');
7
7
  const I18N = require('@ladjs/i18n');
8
+ const Passport = require('@ladjs/passport');
8
9
  const Koa = require('koa');
9
10
  const Redis = require('@ladjs/redis');
10
11
  const StoreIPAddress = require('@ladjs/store-ip-address');
@@ -19,49 +20,65 @@ const etag = require('koa-etag');
19
20
  const json = require('koa-json');
20
21
  const koa404Handler = require('koa-404-handler');
21
22
  const koaConnect = require('koa-connect');
23
+ const ms = require('ms');
22
24
  const multimatch = require('multimatch');
25
+ const ratelimit = require('@ladjs/koa-simple-ratelimit');
23
26
  const removeTrailingSlashes = require('koa-no-trailing-slash');
24
27
  const requestId = require('express-request-id');
25
28
  const requestReceived = require('request-received');
26
29
  const responseTime = require('response-time');
27
30
  const sharedConfig = require('@ladjs/shared-config');
28
31
  const { boolean } = require('boolean');
29
- const { ratelimit } = require('koa-simple-ratelimit');
32
+
33
+ const RATE_LIMIT_EXCEEDED = `Rate limit exceeded, retry in %s.`;
30
34
 
31
35
  class API {
32
- constructor(config, client) {
36
+ // eslint-disable-next-line complexity
37
+ constructor(config, Users) {
33
38
  this.config = {
34
39
  ...sharedConfig('API'),
35
40
  rateLimitIgnoredGlobs: [],
36
41
  ...config
37
42
  };
38
43
 
39
- const cabin = new Cabin({
40
- logger: this.config.logger,
41
- ...this.config.cabin
42
- });
43
-
44
- // initialize redis
45
- this.client = client
46
- ? client
47
- : new Redis(this.config.redis, cabin, this.config.redisMonitor);
48
-
49
44
  // initialize the app
50
45
  const app = new Koa();
51
46
 
52
- // allow middleware to access redis client
47
+ // initialize cabin
48
+ this.logger = _.isPlainObject(this.config.logger)
49
+ ? new Cabin(this.config.logger)
50
+ : this.config.logger instanceof Cabin
51
+ ? this.config.logger
52
+ : new Cabin({
53
+ logger: this.config.logger ? this.config.logger : console
54
+ });
55
+ app.context.logger = this.logger;
56
+
57
+ // initialize redis
58
+ this.client =
59
+ this.config.redis === false
60
+ ? false
61
+ : _.isPlainObject(this.config.redis)
62
+ ? new Redis(this.config.redis, this.logger, this.config.redisMonitor)
63
+ : this.config.redis;
53
64
  app.context.client = this.client;
54
65
 
55
- // listen for error and log events emitted by app
66
+ // expose passport
67
+ this.passport =
68
+ this.config.passport === false
69
+ ? false
70
+ : _.isPlainObject(this.config.passport)
71
+ ? new Passport(this.config.passport, Users)
72
+ : this.config.passport;
73
+ app.context.passport = this.passport;
74
+
75
+ // listen for errors emitted by app
56
76
  app.on('error', (err, ctx) => {
57
- const level = err.status && err.status < 500 ? 'warn' : 'error';
58
- if (ctx.logger) ctx.logger[level](err);
59
- else cabin[level](err);
77
+ ctx.logger[err.status && err.status < 500 ? 'warn' : 'error'](err);
60
78
  });
61
- app.on('log', cabin.log);
62
79
 
63
80
  // override koa's undocumented error handler
64
- app.context.onerror = errorHandler(false, cabin);
81
+ app.context.onerror = errorHandler(false, this.logger);
65
82
 
66
83
  // only trust proxy if enabled
67
84
  app.proxy = boolean(process.env.TRUST_PROXY);
@@ -86,7 +103,7 @@ class API {
86
103
  app.use(koaConnect(requestId()));
87
104
 
88
105
  // use the cabin middleware (adds request-based logging and helpers)
89
- app.use(cabin.middleware);
106
+ app.use(this.logger.middleware);
90
107
 
91
108
  // allow before hooks to get setup
92
109
  if (_.isFunction(this.config.hookBeforeSetup))
@@ -96,7 +113,7 @@ class API {
96
113
  if (this.config.auth) app.use(auth(this.config.auth));
97
114
 
98
115
  // rate limiting
99
- if (this.config.rateLimit) {
116
+ if (this.client && this.config.rateLimit) {
100
117
  app.use((ctx, next) => {
101
118
  // check against ignored/whitelisted paths
102
119
  if (
@@ -109,7 +126,14 @@ class API {
109
126
 
110
127
  return ratelimit({
111
128
  ...this.config.rateLimit,
112
- db: this.client
129
+ db: this.client,
130
+ logger: this.logger,
131
+ errorMessage(exp) {
132
+ const fn =
133
+ typeof ctx.request.t === 'function' ? ctx.request.t : util.format;
134
+ // NOTE: ms does not support i18n localization
135
+ return fn(RATE_LIMIT_EXCEEDED, ms(exp, { long: true }));
136
+ }
113
137
  })(ctx, next);
114
138
  });
115
139
  }
@@ -121,7 +145,7 @@ class API {
121
145
  if (this.config.i18n) {
122
146
  const i18n = this.config.i18n.config
123
147
  ? this.config.i18n
124
- : new I18N({ ...this.config.i18n, logger: cabin });
148
+ : new I18N({ ...this.config.i18n, logger: this.logger });
125
149
  app.use(i18n.middleware);
126
150
  }
127
151
 
@@ -141,12 +165,12 @@ class API {
141
165
  app.use(json());
142
166
 
143
167
  // passport
144
- if (this.config.passport) app.use(this.config.passport.initialize());
168
+ if (this.passport) app.use(this.passport.initialize());
145
169
 
146
170
  // store the user's last ip address in the background
147
171
  if (this.config.storeIPAddress) {
148
172
  const storeIPAddress = new StoreIPAddress({
149
- logger: cabin,
173
+ logger: this.logger,
150
174
  ...this.config.storeIPAddress
151
175
  });
152
176
  app.use(storeIPAddress.middleware);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ladjs/api",
3
3
  "description": "API server for Lad",
4
- "version": "8.0.0",
4
+ "version": "9.0.0",
5
5
  "author": "Nick Baugh <niftylettuce@gmail.com> (http://niftylettuce.com/)",
6
6
  "ava": {
7
7
  "failFast": true,
@@ -22,8 +22,9 @@
22
22
  "dependencies": {
23
23
  "@koa/router": "^10.1.1",
24
24
  "@ladjs/i18n": "^7.2.6",
25
+ "@ladjs/koa-simple-ratelimit": "^3.0.0",
25
26
  "@ladjs/redis": "^1.0.7",
26
- "@ladjs/shared-config": "^6.0.0",
27
+ "@ladjs/shared-config": "^7.0.3",
27
28
  "@ladjs/store-ip-address": "^0.0.7",
28
29
  "boolean": "^3.2.0",
29
30
  "cabin": "^9.1.2",
@@ -41,29 +42,30 @@
41
42
  "koa-etag": "^4.0.0",
42
43
  "koa-json": "^2.0.2",
43
44
  "koa-no-trailing-slash": "^2.1.0",
44
- "koa-simple-ratelimit": "^5.1.1",
45
45
  "lodash": "^4.17.21",
46
+ "ms": "^2.1.3",
46
47
  "multimatch": "5",
47
48
  "request-received": "^0.0.3",
48
49
  "response-time": "^2.3.2"
49
50
  },
50
51
  "devDependencies": {
51
- "@commitlint/cli": "^16.2.4",
52
- "@commitlint/config-conventional": "^16.2.4",
53
- "ava": "^4.2.0",
52
+ "@commitlint/cli": "^17.0.2",
53
+ "@commitlint/config-conventional": "^17.0.2",
54
+ "@ladjs/passport": "^5.0.0",
55
+ "ava": "^4.3.0",
54
56
  "codecov": "^3.8.3",
55
57
  "cross-env": "^7.0.3",
56
- "eslint": "^8.14.0",
58
+ "eslint": "^8.16.0",
57
59
  "eslint-config-xo-lass": "^1.0.6",
58
60
  "fixpack": "^4.0.0",
59
- "husky": "^7.0.4",
60
- "lint-staged": "12.4.1",
61
- "mongoose": "^6.3.2",
61
+ "husky": "^8.0.1",
62
+ "lint-staged": "13.0.0",
63
+ "mongoose": "^6.3.5",
62
64
  "nyc": "^15.1.0",
63
65
  "remark-cli": "^10.0.1",
64
- "remark-preset-github": "^4.0.1",
66
+ "remark-preset-github": "^4.0.2",
65
67
  "supertest": "^6.2.3",
66
- "xo": "^0.48.0"
68
+ "xo": "^0.49.0"
67
69
  },
68
70
  "engines": {
69
71
  "node": ">=10.10.0"