diff --git a/package.json b/package.json index 36e4fe8..ffbeae7 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,7 @@ "@kikobeats/time-span": "~1.0.2", "@microlink/mql": "~0.12.1", "@microlink/ping-url": "~1.4.10", + "@microlink/ua": "~1.0.0", "async-ratelimiter": "~1.3.11", "browserless": "~10.2.0", "cacheable-lookup": "~6.1.0", diff --git a/src/authentication.js b/src/authentication.js index a4abfa4..81567bb 100644 --- a/src/authentication.js +++ b/src/authentication.js @@ -32,19 +32,19 @@ const rateLimitError = (() => { module.exports = rateLimiter ? async (req, res, next) => { - if (req.headers['x-api-key'] === API_KEY) return next() - const { total, reset, remaining } = await rateLimiter.get({ - id: req.ipAddress - }) - - if (!res.writableEnded) { - const _remaining = Math.max(0, remaining - 1) - res.setHeader('X-Rate-Limit-Limit', total) - res.setHeader('X-Rate-Limit-Remaining', _remaining) - res.setHeader('X-Rate-Limit-Reset', reset) - debug(req.ipAddress, { total, remaining: _remaining }) - } - - return remaining ? next() : next(rateLimitError) + if (req.headers['x-api-key'] === API_KEY) return next() + const { total, reset, remaining } = await rateLimiter.get({ + id: req.ipAddress + }) + + if (!res.writableEnded) { + const _remaining = Math.max(0, remaining - 1) + res.setHeader('X-Rate-Limit-Limit', total) + res.setHeader('X-Rate-Limit-Remaining', _remaining) + res.setHeader('X-Rate-Limit-Reset', reset) + debug(req.ipAddress, { total, remaining: _remaining }) } + + return remaining ? next() : next(rateLimitError) + } : false diff --git a/src/constant.js b/src/constant.js index 7cf2182..16a31b4 100644 --- a/src/constant.js +++ b/src/constant.js @@ -19,6 +19,7 @@ const API_URL = module.exports = { ...process.env, + isProduction: NODE_ENV === 'production', API_URL, ALLOWED_REQ_HEADERS, AVATAR_SIZE, diff --git a/src/index.js b/src/index.js index bc9bb79..ce3ff07 100644 --- a/src/index.js +++ b/src/index.js @@ -13,7 +13,7 @@ const { providers } = require('./providers') const ssrCache = require('./send/cache') const avatar = require('./avatar') -const { API_URL } = require('./constant') +const { isProduction, API_URL } = require('./constant') const router = createRouter((error, req, res) => { const hasError = error !== undefined @@ -41,6 +41,7 @@ router next() }, require('./authentication'), + isProduction && require('./ua'), (req, res, next) => { req.timestamp = timeSpan() req.query = Array.from(new URLSearchParams(req.query)).reduce( diff --git a/src/ua.js b/src/ua.js new file mode 100644 index 0000000..558a1fe --- /dev/null +++ b/src/ua.js @@ -0,0 +1,9 @@ +'use strict' + +const redis = require('./util/redis').ua +const ua = require('@microlink/ua')(redis) + +module.exports = async (req, _, next) => { + await ua.incr(req.headers['user-agent']) + next() +} diff --git a/src/util/keyv.js b/src/util/keyv.js index 354ba2d..e2cea68 100644 --- a/src/util/keyv.js +++ b/src/util/keyv.js @@ -6,7 +6,7 @@ const KeyvMulti = require('@keyvhq/multi') const Keyv = require('@keyvhq/core') const assert = require('assert') -const redis = require('./redis') +const redis = require('./redis').cache const { CACHE_TTL } = require('../constant') diff --git a/src/util/rate-limiter.js b/src/util/rate-limiter.js index d27201d..a59635f 100644 --- a/src/util/rate-limiter.js +++ b/src/util/rate-limiter.js @@ -4,13 +4,13 @@ const RateLimiter = require('async-ratelimiter') const { RATE_LIMIT_WINDOW, RATE_LIMIT } = require('../constant') -const db = require('./redis') +const db = require('./redis').cache module.exports = db ? new RateLimiter({ - db, - namespace: 'rate', - duration: RATE_LIMIT_WINDOW, - max: RATE_LIMIT - }) + db, + namespace: 'rate', + duration: RATE_LIMIT_WINDOW, + max: RATE_LIMIT + }) : undefined diff --git a/src/util/redis.js b/src/util/redis.js index 3237179..bf9454d 100644 --- a/src/util/redis.js +++ b/src/util/redis.js @@ -2,12 +2,18 @@ const Redis = require('ioredis') -const { REDIS_URI } = require('../constant') +const { REDIS_URI, REDIS_UA_URI } = require('../constant') -module.exports = REDIS_URI - ? new Redis(REDIS_URI, { +const createClient = uri => + uri + ? new Redis(uri, { lazyConnect: true, enableAutoPipelining: true, maxRetriesPerRequest: 2 }) - : undefined + : undefined + +module.exports = { + cache: createClient(REDIS_URI), + ua: createClient(REDIS_UA_URI) +}