aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--package.json3
-rw-r--r--server/lib/plugins/plugin-manager.ts8
-rw-r--r--server/lib/redis.ts168
-rw-r--r--server/middlewares/cache/shared/api-cache.ts50
-rw-r--r--yarn.lock66
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'
5import { decachePlugin } from '@server/helpers/decache' 5import { decachePlugin } from '@server/helpers/decache'
6import { MOAuthTokenUser, MUser } from '@server/types/models' 6import { MOAuthTokenUser, MUser } from '@server/types/models'
7import { getCompleteLocale } from '@shared/core-utils' 7import { getCompleteLocale } from '@shared/core-utils'
8import { ClientScriptJSON, PluginPackageJSON, PluginTranslation, PluginTranslationPathsJSON, RegisterServerHookOptions } from '@shared/models' 8import {
9 ClientScriptJSON,
10 PluginPackageJSON,
11 PluginTranslation,
12 PluginTranslationPathsJSON,
13 RegisterServerHookOptions
14} from '@shared/models'
9import { getHookType, internalRunHook } from '../../../shared/core-utils/plugins/hooks' 15import { getHookType, internalRunHook } from '../../../shared/core-utils/plugins/hooks'
10import { PluginType } from '../../../shared/models/plugins/plugin.type' 16import { PluginType } from '../../../shared/models/plugins/plugin.type'
11import { ServerHook, ServerHookName } from '../../../shared/models/plugins/server/server-hook.model' 17import { 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 @@
1import express from 'express' 1import express from 'express'
2import { createClient, RedisClient } from 'redis' 2import { createClient } from 'redis'
3import { exists } from '@server/helpers/custom-validators/misc'
3import { logger } from '../helpers/logger' 4import { logger } from '../helpers/logger'
4import { generateRandomString } from '../helpers/utils' 5import { generateRandomString } from '../helpers/utils'
6import { CONFIG } from '../initializers/config'
5import { 7import {
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'
15import { CONFIG } from '../initializers/config'
16import { exists } from '@server/helpers/custom-validators/misc'
17 17
18type CachedRoute = { 18// Only used for typings
19 body: string 19const redisClientWrapperForType = () => createClient<{}>()
20 contentType?: string
21 statusCode?: string
22}
23 20
24class Redis { 21class 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'
7import { logger } from '@server/helpers/logger' 7import { logger } from '@server/helpers/logger'
8import { Redis } from '@server/lib/redis' 8import { Redis } from '@server/lib/redis'
9import { HttpStatusCode } from '@shared/models' 9import { HttpStatusCode } from '@shared/models'
10import { asyncMiddleware } from '@server/middlewares'
10 11
11export interface APICacheOptions { 12export 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 }
diff --git a/yarn.lock b/yarn.lock
index f7d41f2ac..5320b106b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -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
2998cluster-key-slot@^1.1.0: 3023cluster-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
3478denque@^1.1.0, denque@^1.5.0: 3503denque@^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
4507generic-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
4482get-browser-rtc@^1.1.0: 4512get-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
7355redis-commands@1.7.0, redis-commands@^1.7.0: 7385redis-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
7365redis-parser@^3.0.0: 7395redis-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
7372redis@^3.0.2: 7402redis@^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
7382reflect-metadata@^0.1.12: 7412reflect-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
9083yallist@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
9053yallist@^2.1.2: 9088yallist@^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
9058yallist@^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
9063yaml@^1.10.0: 9093yaml@^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"