diff options
Diffstat (limited to 'server/lib/redis.ts')
-rw-r--r-- | server/lib/redis.ts | 168 |
1 files changed, 35 insertions, 133 deletions
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 () { |