diff options
-rw-r--r-- | package.json | 3 | ||||
-rw-r--r-- | server/lib/plugins/plugin-manager.ts | 8 | ||||
-rw-r--r-- | server/lib/redis.ts | 168 | ||||
-rw-r--r-- | server/middlewares/cache/shared/api-cache.ts | 50 | ||||
-rw-r--r-- | yarn.lock | 66 |
5 files changed, 118 insertions, 177 deletions
diff --git a/package.json b/package.json index f5f2de33d..09081a54e 100644 --- a/package.json +++ b/package.json | |||
@@ -131,7 +131,7 @@ | |||
131 | "prompt": "^1.0.0", | 131 | "prompt": "^1.0.0", |
132 | "proxy-addr": "^2.0.7", | 132 | "proxy-addr": "^2.0.7", |
133 | "pug": "^3.0.0", | 133 | "pug": "^3.0.0", |
134 | "redis": "^3.0.2", | 134 | "redis": "^4.0.1", |
135 | "reflect-metadata": "^0.1.12", | 135 | "reflect-metadata": "^0.1.12", |
136 | "sanitize-html": "2.x", | 136 | "sanitize-html": "2.x", |
137 | "sequelize": "6.9.0", | 137 | "sequelize": "6.9.0", |
@@ -182,7 +182,6 @@ | |||
182 | "@types/nodemailer": "^6.2.0", | 182 | "@types/nodemailer": "^6.2.0", |
183 | "@types/oauth2-server": "^3.0.8", | 183 | "@types/oauth2-server": "^3.0.8", |
184 | "@types/pem": "^1.9.3", | 184 | "@types/pem": "^1.9.3", |
185 | "@types/redis": "^2.8.5", | ||
186 | "@types/request": "^2.0.3", | 185 | "@types/request": "^2.0.3", |
187 | "@types/supertest": "^2.0.3", | 186 | "@types/supertest": "^2.0.3", |
188 | "@types/validator": "^13.0.0", | 187 | "@types/validator": "^13.0.0", |
diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts index ff00ab9e8..39e7f9a5b 100644 --- a/server/lib/plugins/plugin-manager.ts +++ b/server/lib/plugins/plugin-manager.ts | |||
@@ -5,7 +5,13 @@ import { basename, join } from 'path' | |||
5 | import { decachePlugin } from '@server/helpers/decache' | 5 | import { decachePlugin } from '@server/helpers/decache' |
6 | import { MOAuthTokenUser, MUser } from '@server/types/models' | 6 | import { MOAuthTokenUser, MUser } from '@server/types/models' |
7 | import { getCompleteLocale } from '@shared/core-utils' | 7 | import { getCompleteLocale } from '@shared/core-utils' |
8 | import { ClientScriptJSON, PluginPackageJSON, PluginTranslation, PluginTranslationPathsJSON, RegisterServerHookOptions } from '@shared/models' | 8 | import { |
9 | ClientScriptJSON, | ||
10 | PluginPackageJSON, | ||
11 | PluginTranslation, | ||
12 | PluginTranslationPathsJSON, | ||
13 | RegisterServerHookOptions | ||
14 | } from '@shared/models' | ||
9 | import { getHookType, internalRunHook } from '../../../shared/core-utils/plugins/hooks' | 15 | import { getHookType, internalRunHook } from '../../../shared/core-utils/plugins/hooks' |
10 | import { PluginType } from '../../../shared/models/plugins/plugin.type' | 16 | import { PluginType } from '../../../shared/models/plugins/plugin.type' |
11 | import { ServerHook, ServerHookName } from '../../../shared/models/plugins/server/server-hook.model' | 17 | import { ServerHook, ServerHookName } from '../../../shared/models/plugins/server/server-hook.model' |
diff --git a/server/lib/redis.ts b/server/lib/redis.ts index 8aec4b793..0478bfc89 100644 --- a/server/lib/redis.ts +++ b/server/lib/redis.ts | |||
@@ -1,31 +1,29 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import { createClient, RedisClient } from 'redis' | 2 | import { createClient } from 'redis' |
3 | import { exists } from '@server/helpers/custom-validators/misc' | ||
3 | import { logger } from '../helpers/logger' | 4 | import { logger } from '../helpers/logger' |
4 | import { generateRandomString } from '../helpers/utils' | 5 | import { generateRandomString } from '../helpers/utils' |
6 | import { CONFIG } from '../initializers/config' | ||
5 | import { | 7 | import { |
6 | CONTACT_FORM_LIFETIME, | 8 | CONTACT_FORM_LIFETIME, |
9 | RESUMABLE_UPLOAD_SESSION_LIFETIME, | ||
10 | TRACKER_RATE_LIMITS, | ||
7 | USER_EMAIL_VERIFY_LIFETIME, | 11 | USER_EMAIL_VERIFY_LIFETIME, |
8 | USER_PASSWORD_RESET_LIFETIME, | ||
9 | USER_PASSWORD_CREATE_LIFETIME, | 12 | USER_PASSWORD_CREATE_LIFETIME, |
13 | USER_PASSWORD_RESET_LIFETIME, | ||
10 | VIEW_LIFETIME, | 14 | VIEW_LIFETIME, |
11 | WEBSERVER, | 15 | WEBSERVER |
12 | TRACKER_RATE_LIMITS, | ||
13 | RESUMABLE_UPLOAD_SESSION_LIFETIME | ||
14 | } from '../initializers/constants' | 16 | } from '../initializers/constants' |
15 | import { CONFIG } from '../initializers/config' | ||
16 | import { exists } from '@server/helpers/custom-validators/misc' | ||
17 | 17 | ||
18 | type CachedRoute = { | 18 | // Only used for typings |
19 | body: string | 19 | const redisClientWrapperForType = () => createClient<{}>() |
20 | contentType?: string | ||
21 | statusCode?: string | ||
22 | } | ||
23 | 20 | ||
24 | class Redis { | 21 | class Redis { |
25 | 22 | ||
26 | private static instance: Redis | 23 | private static instance: Redis |
27 | private initialized = false | 24 | private initialized = false |
28 | private client: RedisClient | 25 | private connected = false |
26 | private client: ReturnType<typeof redisClientWrapperForType> | ||
29 | private prefix: string | 27 | private prefix: string |
30 | 28 | ||
31 | private constructor () { | 29 | private constructor () { |
@@ -38,21 +36,24 @@ class Redis { | |||
38 | 36 | ||
39 | this.client = createClient(Redis.getRedisClientOptions()) | 37 | this.client = createClient(Redis.getRedisClientOptions()) |
40 | 38 | ||
39 | this.client.connect() | ||
40 | .then(() => { this.connected = true }) | ||
41 | .catch(err => { | ||
42 | logger.error('Cannot connect to redis', { err }) | ||
43 | process.exit(-1) | ||
44 | }) | ||
45 | |||
41 | this.client.on('error', err => { | 46 | this.client.on('error', err => { |
42 | logger.error('Error in Redis client.', { err }) | 47 | logger.error('Error in Redis client.', { err }) |
43 | process.exit(-1) | 48 | process.exit(-1) |
44 | }) | 49 | }) |
45 | 50 | ||
46 | if (CONFIG.REDIS.AUTH) { | ||
47 | this.client.auth(CONFIG.REDIS.AUTH) | ||
48 | } | ||
49 | |||
50 | this.prefix = 'redis-' + WEBSERVER.HOST + '-' | 51 | this.prefix = 'redis-' + WEBSERVER.HOST + '-' |
51 | } | 52 | } |
52 | 53 | ||
53 | static getRedisClientOptions () { | 54 | static getRedisClientOptions () { |
54 | return Object.assign({}, | 55 | return Object.assign({}, |
55 | (CONFIG.REDIS.AUTH && CONFIG.REDIS.AUTH != null) ? { password: CONFIG.REDIS.AUTH } : {}, | 56 | CONFIG.REDIS.AUTH ? { password: CONFIG.REDIS.AUTH } : {}, |
56 | (CONFIG.REDIS.DB) ? { db: CONFIG.REDIS.DB } : {}, | 57 | (CONFIG.REDIS.DB) ? { db: CONFIG.REDIS.DB } : {}, |
57 | (CONFIG.REDIS.HOSTNAME && CONFIG.REDIS.PORT) | 58 | (CONFIG.REDIS.HOSTNAME && CONFIG.REDIS.PORT) |
58 | ? { host: CONFIG.REDIS.HOSTNAME, port: CONFIG.REDIS.PORT } | 59 | ? { host: CONFIG.REDIS.HOSTNAME, port: CONFIG.REDIS.PORT } |
@@ -68,6 +69,10 @@ class Redis { | |||
68 | return this.prefix | 69 | return this.prefix |
69 | } | 70 | } |
70 | 71 | ||
72 | isConnected () { | ||
73 | return this.connected | ||
74 | } | ||
75 | |||
71 | /* ************ Forgot password ************ */ | 76 | /* ************ Forgot password ************ */ |
72 | 77 | ||
73 | async setResetPasswordVerificationString (userId: number) { | 78 | async setResetPasswordVerificationString (userId: number) { |
@@ -146,25 +151,6 @@ class Redis { | |||
146 | return this.exists(this.generateTrackerBlockIPKey(ip)) | 151 | return this.exists(this.generateTrackerBlockIPKey(ip)) |
147 | } | 152 | } |
148 | 153 | ||
149 | /* ************ API cache ************ */ | ||
150 | |||
151 | async getCachedRoute (req: express.Request) { | ||
152 | const cached = await this.getObject(this.generateCachedRouteKey(req)) | ||
153 | |||
154 | return cached as CachedRoute | ||
155 | } | ||
156 | |||
157 | setCachedRoute (req: express.Request, body: any, lifetime: number, contentType?: string, statusCode?: number) { | ||
158 | const cached: CachedRoute = Object.assign( | ||
159 | {}, | ||
160 | { body: body.toString() }, | ||
161 | (contentType) ? { contentType } : null, | ||
162 | (statusCode) ? { statusCode: statusCode.toString() } : null | ||
163 | ) | ||
164 | |||
165 | return this.setObject(this.generateCachedRouteKey(req), cached, lifetime) | ||
166 | } | ||
167 | |||
168 | /* ************ Video views stats ************ */ | 154 | /* ************ Video views stats ************ */ |
169 | 155 | ||
170 | addVideoViewStats (videoId: number) { | 156 | addVideoViewStats (videoId: number) { |
@@ -277,10 +263,6 @@ class Redis { | |||
277 | 263 | ||
278 | /* ************ Keys generation ************ */ | 264 | /* ************ Keys generation ************ */ |
279 | 265 | ||
280 | generateCachedRouteKey (req: express.Request) { | ||
281 | return req.method + '-' + req.originalUrl | ||
282 | } | ||
283 | |||
284 | private generateLocalVideoViewsKeys (videoId?: Number) { | 266 | private generateLocalVideoViewsKeys (videoId?: Number) { |
285 | return { setKey: `local-video-views-buffer`, videoKey: `local-video-views-buffer-${videoId}` } | 267 | return { setKey: `local-video-views-buffer`, videoKey: `local-video-views-buffer-${videoId}` } |
286 | } | 268 | } |
@@ -320,125 +302,45 @@ class Redis { | |||
320 | /* ************ Redis helpers ************ */ | 302 | /* ************ Redis helpers ************ */ |
321 | 303 | ||
322 | private getValue (key: string) { | 304 | private getValue (key: string) { |
323 | return new Promise<string>((res, rej) => { | 305 | return this.client.get(this.prefix + key) |
324 | this.client.get(this.prefix + key, (err, value) => { | ||
325 | if (err) return rej(err) | ||
326 | |||
327 | return res(value) | ||
328 | }) | ||
329 | }) | ||
330 | } | 306 | } |
331 | 307 | ||
332 | private getSet (key: string) { | 308 | private getSet (key: string) { |
333 | return new Promise<string[]>((res, rej) => { | 309 | return this.client.sMembers(this.prefix + key) |
334 | this.client.smembers(this.prefix + key, (err, value) => { | ||
335 | if (err) return rej(err) | ||
336 | |||
337 | return res(value) | ||
338 | }) | ||
339 | }) | ||
340 | } | 310 | } |
341 | 311 | ||
342 | private addToSet (key: string, value: string) { | 312 | private addToSet (key: string, value: string) { |
343 | return new Promise<void>((res, rej) => { | 313 | return this.client.sAdd(this.prefix + key, value) |
344 | this.client.sadd(this.prefix + key, value, err => err ? rej(err) : res()) | ||
345 | }) | ||
346 | } | 314 | } |
347 | 315 | ||
348 | private deleteFromSet (key: string, value: string) { | 316 | private deleteFromSet (key: string, value: string) { |
349 | return new Promise<void>((res, rej) => { | 317 | return this.client.sRem(this.prefix + key, value) |
350 | this.client.srem(this.prefix + key, value, err => err ? rej(err) : res()) | ||
351 | }) | ||
352 | } | 318 | } |
353 | 319 | ||
354 | private deleteKey (key: string) { | 320 | private deleteKey (key: string) { |
355 | return new Promise<void>((res, rej) => { | 321 | return this.client.del(this.prefix + key) |
356 | this.client.del(this.prefix + key, err => err ? rej(err) : res()) | ||
357 | }) | ||
358 | } | ||
359 | |||
360 | private deleteFieldInHash (key: string, field: string) { | ||
361 | return new Promise<void>((res, rej) => { | ||
362 | this.client.hdel(this.prefix + key, field, err => err ? rej(err) : res()) | ||
363 | }) | ||
364 | } | 322 | } |
365 | 323 | ||
366 | private setValue (key: string, value: string, expirationMilliseconds: number) { | 324 | private async setValue (key: string, value: string, expirationMilliseconds: number) { |
367 | return new Promise<void>((res, rej) => { | 325 | const result = await this.client.set(this.prefix + key, value, { PX: expirationMilliseconds }) |
368 | this.client.set(this.prefix + key, value, 'PX', expirationMilliseconds, (err, ok) => { | ||
369 | if (err) return rej(err) | ||
370 | |||
371 | if (ok !== 'OK') return rej(new Error('Redis set result is not OK.')) | ||
372 | 326 | ||
373 | return res() | 327 | if (result !== 'OK') throw new Error('Redis set result is not OK.') |
374 | }) | ||
375 | }) | ||
376 | } | 328 | } |
377 | 329 | ||
378 | private removeValue (key: string) { | 330 | private removeValue (key: string) { |
379 | return new Promise<void>((res, rej) => { | 331 | return this.client.del(this.prefix + key) |
380 | this.client.del(this.prefix + key, err => { | ||
381 | if (err) return rej(err) | ||
382 | |||
383 | return res() | ||
384 | }) | ||
385 | }) | ||
386 | } | ||
387 | |||
388 | private setObject (key: string, obj: { [id: string]: string }, expirationMilliseconds: number) { | ||
389 | return new Promise<void>((res, rej) => { | ||
390 | this.client.hmset(this.prefix + key, obj, (err, ok) => { | ||
391 | if (err) return rej(err) | ||
392 | if (!ok) return rej(new Error('Redis mset result is not OK.')) | ||
393 | |||
394 | this.client.pexpire(this.prefix + key, expirationMilliseconds, (err, ok) => { | ||
395 | if (err) return rej(err) | ||
396 | if (!ok) return rej(new Error('Redis expiration result is not OK.')) | ||
397 | |||
398 | return res() | ||
399 | }) | ||
400 | }) | ||
401 | }) | ||
402 | } | 332 | } |
403 | 333 | ||
404 | private getObject (key: string) { | 334 | private getObject (key: string) { |
405 | return new Promise<{ [id: string]: string }>((res, rej) => { | 335 | return this.client.hGetAll(this.prefix + key) |
406 | this.client.hgetall(this.prefix + key, (err, value) => { | ||
407 | if (err) return rej(err) | ||
408 | |||
409 | return res(value) | ||
410 | }) | ||
411 | }) | ||
412 | } | ||
413 | |||
414 | private setValueInHash (key: string, field: string, value: string) { | ||
415 | return new Promise<void>((res, rej) => { | ||
416 | this.client.hset(this.prefix + key, field, value, (err) => { | ||
417 | if (err) return rej(err) | ||
418 | |||
419 | return res() | ||
420 | }) | ||
421 | }) | ||
422 | } | 336 | } |
423 | 337 | ||
424 | private increment (key: string) { | 338 | private increment (key: string) { |
425 | return new Promise<number>((res, rej) => { | 339 | return this.client.incr(this.prefix + key) |
426 | this.client.incr(this.prefix + key, (err, value) => { | ||
427 | if (err) return rej(err) | ||
428 | |||
429 | return res(value) | ||
430 | }) | ||
431 | }) | ||
432 | } | 340 | } |
433 | 341 | ||
434 | private exists (key: string) { | 342 | private exists (key: string) { |
435 | return new Promise<boolean>((res, rej) => { | 343 | return this.client.exists(this.prefix + key) |
436 | this.client.exists(this.prefix + key, (err, existsNumber) => { | ||
437 | if (err) return rej(err) | ||
438 | |||
439 | return res(existsNumber === 1) | ||
440 | }) | ||
441 | }) | ||
442 | } | 344 | } |
443 | 345 | ||
444 | static get Instance () { | 346 | static get Instance () { |
diff --git a/server/middlewares/cache/shared/api-cache.ts b/server/middlewares/cache/shared/api-cache.ts index f8846dcfc..86c5095b5 100644 --- a/server/middlewares/cache/shared/api-cache.ts +++ b/server/middlewares/cache/shared/api-cache.ts | |||
@@ -7,6 +7,7 @@ import { isTestInstance, parseDurationToMs } from '@server/helpers/core-utils' | |||
7 | import { logger } from '@server/helpers/logger' | 7 | import { logger } from '@server/helpers/logger' |
8 | import { Redis } from '@server/lib/redis' | 8 | import { Redis } from '@server/lib/redis' |
9 | import { HttpStatusCode } from '@shared/models' | 9 | import { HttpStatusCode } from '@shared/models' |
10 | import { asyncMiddleware } from '@server/middlewares' | ||
10 | 11 | ||
11 | export interface APICacheOptions { | 12 | export interface APICacheOptions { |
12 | headerBlacklist?: string[] | 13 | headerBlacklist?: string[] |
@@ -40,24 +41,25 @@ export class ApiCache { | |||
40 | buildMiddleware (strDuration: string) { | 41 | buildMiddleware (strDuration: string) { |
41 | const duration = parseDurationToMs(strDuration) | 42 | const duration = parseDurationToMs(strDuration) |
42 | 43 | ||
43 | return (req: express.Request, res: express.Response, next: express.NextFunction) => { | 44 | return asyncMiddleware( |
44 | const key = Redis.Instance.getPrefix() + 'api-cache-' + req.originalUrl | 45 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
45 | const redis = Redis.Instance.getClient() | 46 | const key = Redis.Instance.getPrefix() + 'api-cache-' + req.originalUrl |
47 | const redis = Redis.Instance.getClient() | ||
46 | 48 | ||
47 | if (!redis.connected) return this.makeResponseCacheable(res, next, key, duration) | 49 | if (!Redis.Instance.isConnected()) return this.makeResponseCacheable(res, next, key, duration) |
48 | 50 | ||
49 | try { | 51 | try { |
50 | redis.hgetall(key, (err, obj) => { | 52 | const obj = await redis.hGetAll(key) |
51 | if (!err && obj && obj.response) { | 53 | if (obj?.response) { |
52 | return this.sendCachedResponse(req, res, JSON.parse(obj.response), duration) | 54 | return this.sendCachedResponse(req, res, JSON.parse(obj.response), duration) |
53 | } | 55 | } |
54 | 56 | ||
55 | return this.makeResponseCacheable(res, next, key, duration) | 57 | return this.makeResponseCacheable(res, next, key, duration) |
56 | }) | 58 | } catch (err) { |
57 | } catch (err) { | 59 | return this.makeResponseCacheable(res, next, key, duration) |
58 | return this.makeResponseCacheable(res, next, key, duration) | 60 | } |
59 | } | 61 | } |
60 | } | 62 | ) |
61 | } | 63 | } |
62 | 64 | ||
63 | private shouldCacheResponse (response: express.Response) { | 65 | private shouldCacheResponse (response: express.Response) { |
@@ -93,21 +95,22 @@ export class ApiCache { | |||
93 | } as CacheObject | 95 | } as CacheObject |
94 | } | 96 | } |
95 | 97 | ||
96 | private cacheResponse (key: string, value: object, duration: number) { | 98 | private async cacheResponse (key: string, value: object, duration: number) { |
97 | const redis = Redis.Instance.getClient() | 99 | const redis = Redis.Instance.getClient() |
98 | 100 | ||
99 | if (redis.connected) { | 101 | if (Redis.Instance.isConnected()) { |
100 | try { | 102 | await Promise.all([ |
101 | redis.hset(key, 'response', JSON.stringify(value)) | 103 | redis.hSet(key, 'response', JSON.stringify(value)), |
102 | redis.hset(key, 'duration', duration + '') | 104 | redis.hSet(key, 'duration', duration + ''), |
103 | redis.expire(key, duration / 1000) | 105 | redis.expire(key, duration / 1000) |
104 | } catch (err) { | 106 | ]) |
105 | logger.error('Cannot set cache in redis.', { err }) | ||
106 | } | ||
107 | } | 107 | } |
108 | 108 | ||
109 | // add automatic cache clearing from duration, includes max limit on setTimeout | 109 | // add automatic cache clearing from duration, includes max limit on setTimeout |
110 | this.timers[key] = setTimeout(() => this.clear(key), Math.min(duration, 2147483647)) | 110 | this.timers[key] = setTimeout(() => { |
111 | this.clear(key) | ||
112 | .catch(err => logger.error('Cannot clear Redis key %s.', key, { err })) | ||
113 | }, Math.min(duration, 2147483647)) | ||
111 | } | 114 | } |
112 | 115 | ||
113 | private accumulateContent (res: express.Response, content: any) { | 116 | private accumulateContent (res: express.Response, content: any) { |
@@ -184,6 +187,7 @@ export class ApiCache { | |||
184 | encoding | 187 | encoding |
185 | ) | 188 | ) |
186 | self.cacheResponse(key, cacheObject, duration) | 189 | self.cacheResponse(key, cacheObject, duration) |
190 | .catch(err => logger.error('Cannot cache response', { err })) | ||
187 | } | 191 | } |
188 | } | 192 | } |
189 | 193 | ||
@@ -235,7 +239,7 @@ export class ApiCache { | |||
235 | return response.end(data, cacheObject.encoding) | 239 | return response.end(data, cacheObject.encoding) |
236 | } | 240 | } |
237 | 241 | ||
238 | private clear (target: string) { | 242 | private async clear (target: string) { |
239 | const redis = Redis.Instance.getClient() | 243 | const redis = Redis.Instance.getClient() |
240 | 244 | ||
241 | if (target) { | 245 | if (target) { |
@@ -243,7 +247,7 @@ export class ApiCache { | |||
243 | delete this.timers[target] | 247 | delete this.timers[target] |
244 | 248 | ||
245 | try { | 249 | try { |
246 | redis.del(target) | 250 | await redis.del(target) |
247 | } catch (err) { | 251 | } catch (err) { |
248 | logger.error('Cannot delete %s in redis cache.', target, { err }) | 252 | logger.error('Cannot delete %s in redis cache.', target, { err }) |
249 | } | 253 | } |
@@ -255,7 +259,7 @@ export class ApiCache { | |||
255 | delete this.timers[key] | 259 | delete this.timers[key] |
256 | 260 | ||
257 | try { | 261 | try { |
258 | redis.del(key) | 262 | await redis.del(key) |
259 | } catch (err) { | 263 | } catch (err) { |
260 | logger.error('Cannot delete %s in redis cache.', key, { err }) | 264 | logger.error('Cannot delete %s in redis cache.', key, { err }) |
261 | } | 265 | } |
@@ -1365,6 +1365,31 @@ | |||
1365 | semver "^7.3.5" | 1365 | semver "^7.3.5" |
1366 | tar "^6.1.11" | 1366 | tar "^6.1.11" |
1367 | 1367 | ||
1368 | "@node-redis/client@^1.0.1": | ||
1369 | version "1.0.1" | ||
1370 | resolved "https://registry.yarnpkg.com/@node-redis/client/-/client-1.0.1.tgz#ddca6021097ce1026fedc193cac8c36b05c6cad8" | ||
1371 | integrity sha512-o0I4LdzJXP6QYxRnBPrQ7cIG5tF3SNM/PBnjC3mV6QkzIiGRElzWqSr9a9JCJdcyB1SIA80bhgGhpdTpCQ1Sdw== | ||
1372 | dependencies: | ||
1373 | cluster-key-slot "1.1.0" | ||
1374 | generic-pool "3.8.2" | ||
1375 | redis-parser "3.0.0" | ||
1376 | yallist "4.0.0" | ||
1377 | |||
1378 | "@node-redis/json@^1.0.1": | ||
1379 | version "1.0.1" | ||
1380 | resolved "https://registry.yarnpkg.com/@node-redis/json/-/json-1.0.1.tgz#8cd987c1855392adf21bc4f06163a7eda97a40a3" | ||
1381 | integrity sha512-2EB96ZN0yUr4mgA9Odme48jX8eF5Ji0jrsRn4rLfEhME7L3rHLdKeUfxJKxbPOxadP6k8+6ViElxPZrKuV2nvQ== | ||
1382 | |||
1383 | "@node-redis/search@^1.0.1": | ||
1384 | version "1.0.1" | ||
1385 | resolved "https://registry.yarnpkg.com/@node-redis/search/-/search-1.0.1.tgz#8d0936049f4858b9aefab40524ce8e5a52e5d08e" | ||
1386 | integrity sha512-iA2Gw6gr0X6IfNSjTyme9W1tDlLkwQ1bPApo4s8aVwZ2Ju8Z4COVik0vT6BJPRin79f5xPZgnaec3DIoC2UpHA== | ||
1387 | |||
1388 | "@node-redis/time-series@^1.0.0": | ||
1389 | version "1.0.0" | ||
1390 | resolved "https://registry.yarnpkg.com/@node-redis/time-series/-/time-series-1.0.0.tgz#3db4caa63d7c158f0b73ab6cd46bd6c9c187dfaf" | ||
1391 | integrity sha512-QcaCIL/DlYJXedSfmjF+IRxKJbBUXBrjA5Gv0IiPlXXFFOkRnbPGKq6hmwBAAWyk1U03wyBHDFKVS3/9GnZV8g== | ||
1392 | |||
1368 | "@nodelib/fs.scandir@2.1.5": | 1393 | "@nodelib/fs.scandir@2.1.5": |
1369 | version "2.1.5" | 1394 | version "2.1.5" |
1370 | resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" | 1395 | resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" |
@@ -1805,7 +1830,7 @@ | |||
1805 | resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" | 1830 | resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" |
1806 | integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== | 1831 | integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== |
1807 | 1832 | ||
1808 | "@types/redis@^2.8.0", "@types/redis@^2.8.5": | 1833 | "@types/redis@^2.8.0": |
1809 | version "2.8.32" | 1834 | version "2.8.32" |
1810 | resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.32.tgz#1d3430219afbee10f8cfa389dad2571a05ecfb11" | 1835 | resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.32.tgz#1d3430219afbee10f8cfa389dad2571a05ecfb11" |
1811 | integrity sha512-7jkMKxcGq9p242exlbsVzuJb57KqHRhNl4dHoQu2Y5v9bCAbtIXXH0R3HleSQW4CTOqpHIYUW3t6tpUj4BVQ+w== | 1836 | integrity sha512-7jkMKxcGq9p242exlbsVzuJb57KqHRhNl4dHoQu2Y5v9bCAbtIXXH0R3HleSQW4CTOqpHIYUW3t6tpUj4BVQ+w== |
@@ -2995,7 +3020,7 @@ clone@^2.0.0: | |||
2995 | resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" | 3020 | resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" |
2996 | integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= | 3021 | integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= |
2997 | 3022 | ||
2998 | cluster-key-slot@^1.1.0: | 3023 | cluster-key-slot@1.1.0, cluster-key-slot@^1.1.0: |
2999 | version "1.1.0" | 3024 | version "1.1.0" |
3000 | resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d" | 3025 | resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d" |
3001 | integrity sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw== | 3026 | integrity sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw== |
@@ -3475,7 +3500,7 @@ delegates@^1.0.0: | |||
3475 | resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" | 3500 | resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" |
3476 | integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= | 3501 | integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= |
3477 | 3502 | ||
3478 | denque@^1.1.0, denque@^1.5.0: | 3503 | denque@^1.1.0: |
3479 | version "1.5.1" | 3504 | version "1.5.1" |
3480 | resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf" | 3505 | resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf" |
3481 | integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== | 3506 | integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== |
@@ -4479,6 +4504,11 @@ gauge@^3.0.0: | |||
4479 | strip-ansi "^6.0.1" | 4504 | strip-ansi "^6.0.1" |
4480 | wide-align "^1.1.2" | 4505 | wide-align "^1.1.2" |
4481 | 4506 | ||
4507 | generic-pool@3.8.2: | ||
4508 | version "3.8.2" | ||
4509 | resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.8.2.tgz#aab4f280adb522fdfbdc5e5b64d718d3683f04e9" | ||
4510 | integrity sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg== | ||
4511 | |||
4482 | get-browser-rtc@^1.1.0: | 4512 | get-browser-rtc@^1.1.0: |
4483 | version "1.1.0" | 4513 | version "1.1.0" |
4484 | resolved "https://registry.yarnpkg.com/get-browser-rtc/-/get-browser-rtc-1.1.0.tgz#d1494e299b00f33fc8e9d6d3343ba4ba99711a2c" | 4514 | resolved "https://registry.yarnpkg.com/get-browser-rtc/-/get-browser-rtc-1.1.0.tgz#d1494e299b00f33fc8e9d6d3343ba4ba99711a2c" |
@@ -7352,7 +7382,7 @@ record-cache@^1.0.2: | |||
7352 | resolved "https://registry.yarnpkg.com/record-cache/-/record-cache-1.1.1.tgz#ba3088a489f50491a4af7b14d410822c394fb811" | 7382 | resolved "https://registry.yarnpkg.com/record-cache/-/record-cache-1.1.1.tgz#ba3088a489f50491a4af7b14d410822c394fb811" |
7353 | integrity sha512-L5hZlgWc7CmGbztnemQoKE1bLu9rtI2skOB0ttE4C5+TVszLE8Rd0YLTROSgvXKLAqPumS/soyN5tJW5wJLmJQ== | 7383 | integrity sha512-L5hZlgWc7CmGbztnemQoKE1bLu9rtI2skOB0ttE4C5+TVszLE8Rd0YLTROSgvXKLAqPumS/soyN5tJW5wJLmJQ== |
7354 | 7384 | ||
7355 | redis-commands@1.7.0, redis-commands@^1.7.0: | 7385 | redis-commands@1.7.0: |
7356 | version "1.7.0" | 7386 | version "1.7.0" |
7357 | resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89" | 7387 | resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89" |
7358 | integrity sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ== | 7388 | integrity sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ== |
@@ -7362,22 +7392,22 @@ redis-errors@^1.0.0, redis-errors@^1.2.0: | |||
7362 | resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" | 7392 | resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" |
7363 | integrity sha1-62LSrbFeTq9GEMBK/hUpOEJQq60= | 7393 | integrity sha1-62LSrbFeTq9GEMBK/hUpOEJQq60= |
7364 | 7394 | ||
7365 | redis-parser@^3.0.0: | 7395 | redis-parser@3.0.0, redis-parser@^3.0.0: |
7366 | version "3.0.0" | 7396 | version "3.0.0" |
7367 | resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" | 7397 | resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" |
7368 | integrity sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ= | 7398 | integrity sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ= |
7369 | dependencies: | 7399 | dependencies: |
7370 | redis-errors "^1.0.0" | 7400 | redis-errors "^1.0.0" |
7371 | 7401 | ||
7372 | redis@^3.0.2: | 7402 | redis@^4.0.1: |
7373 | version "3.1.2" | 7403 | version "4.0.1" |
7374 | resolved "https://registry.yarnpkg.com/redis/-/redis-3.1.2.tgz#766851117e80653d23e0ed536254677ab647638c" | 7404 | resolved "https://registry.yarnpkg.com/redis/-/redis-4.0.1.tgz#c020e2ac7f83f0c1d42ced50b8a7af28164bd6ee" |
7375 | integrity sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw== | 7405 | integrity sha512-qfcq1oz2ci7pNdCfTLLEuKhS8jZ17dFiT1exogOr+jd3EVP/h9qpy7K+VajB4BXA0k8q68KFqR6HrliKV6jt1Q== |
7376 | dependencies: | 7406 | dependencies: |
7377 | denque "^1.5.0" | 7407 | "@node-redis/client" "^1.0.1" |
7378 | redis-commands "^1.7.0" | 7408 | "@node-redis/json" "^1.0.1" |
7379 | redis-errors "^1.2.0" | 7409 | "@node-redis/search" "^1.0.1" |
7380 | redis-parser "^3.0.0" | 7410 | "@node-redis/time-series" "^1.0.0" |
7381 | 7411 | ||
7382 | reflect-metadata@^0.1.12: | 7412 | reflect-metadata@^0.1.12: |
7383 | version "0.1.13" | 7413 | version "0.1.13" |
@@ -9050,16 +9080,16 @@ y18n@^5.0.5: | |||
9050 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" | 9080 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" |
9051 | integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== | 9081 | integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== |
9052 | 9082 | ||
9083 | yallist@4.0.0, yallist@^4.0.0: | ||
9084 | version "4.0.0" | ||
9085 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" | ||
9086 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== | ||
9087 | |||
9053 | yallist@^2.1.2: | 9088 | yallist@^2.1.2: |
9054 | version "2.1.2" | 9089 | version "2.1.2" |
9055 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" | 9090 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" |
9056 | integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= | 9091 | integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= |
9057 | 9092 | ||
9058 | yallist@^4.0.0: | ||
9059 | version "4.0.0" | ||
9060 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" | ||
9061 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== | ||
9062 | |||
9063 | yaml@^1.10.0: | 9093 | yaml@^1.10.0: |
9064 | version "1.10.2" | 9094 | version "1.10.2" |
9065 | resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" | 9095 | resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" |