diff options
Diffstat (limited to 'server/lib/redis.ts')
-rw-r--r-- | server/lib/redis.ts | 105 |
1 files changed, 59 insertions, 46 deletions
diff --git a/server/lib/redis.ts b/server/lib/redis.ts index 9b3c72300..c0e9aece7 100644 --- a/server/lib/redis.ts +++ b/server/lib/redis.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { createClient, RedisClientOptions, RedisModules } from 'redis' | 1 | import IoRedis, { RedisOptions } from 'ioredis' |
2 | import { exists } from '@server/helpers/custom-validators/misc' | 2 | import { exists } from '@server/helpers/custom-validators/misc' |
3 | import { sha256 } from '@shared/extra-utils' | 3 | import { sha256 } from '@shared/extra-utils' |
4 | import { logger } from '../helpers/logger' | 4 | import { logger } from '../helpers/logger' |
@@ -9,6 +9,7 @@ import { | |||
9 | CONTACT_FORM_LIFETIME, | 9 | CONTACT_FORM_LIFETIME, |
10 | RESUMABLE_UPLOAD_SESSION_LIFETIME, | 10 | RESUMABLE_UPLOAD_SESSION_LIFETIME, |
11 | TRACKER_RATE_LIMITS, | 11 | TRACKER_RATE_LIMITS, |
12 | TWO_FACTOR_AUTH_REQUEST_TOKEN_LIFETIME, | ||
12 | USER_EMAIL_VERIFY_LIFETIME, | 13 | USER_EMAIL_VERIFY_LIFETIME, |
13 | USER_PASSWORD_CREATE_LIFETIME, | 14 | USER_PASSWORD_CREATE_LIFETIME, |
14 | USER_PASSWORD_RESET_LIFETIME, | 15 | USER_PASSWORD_RESET_LIFETIME, |
@@ -21,7 +22,7 @@ class Redis { | |||
21 | private static instance: Redis | 22 | private static instance: Redis |
22 | private initialized = false | 23 | private initialized = false |
23 | private connected = false | 24 | private connected = false |
24 | private client: ReturnType<typeof createClient> | 25 | private client: IoRedis |
25 | private prefix: string | 26 | private prefix: string |
26 | 27 | ||
27 | private constructor () { | 28 | private constructor () { |
@@ -32,46 +33,42 @@ class Redis { | |||
32 | if (this.initialized === true) return | 33 | if (this.initialized === true) return |
33 | this.initialized = true | 34 | this.initialized = true |
34 | 35 | ||
35 | this.client = createClient(Redis.getRedisClientOptions()) | ||
36 | this.client.on('error', err => logger.error('Redis Client Error', { err })) | ||
37 | |||
38 | logger.info('Connecting to redis...') | 36 | logger.info('Connecting to redis...') |
39 | 37 | ||
40 | this.client.connect() | 38 | this.client = new IoRedis(Redis.getRedisClientOptions('', { enableAutoPipelining: true })) |
41 | .then(() => { | 39 | this.client.on('error', err => logger.error('Redis failed to connect', { err })) |
42 | logger.info('Connected to redis.') | 40 | this.client.on('connect', () => { |
43 | 41 | logger.info('Connected to redis.') | |
44 | this.connected = true | 42 | |
45 | }).catch(err => { | 43 | this.connected = true |
46 | logger.error('Cannot connect to redis', { err }) | 44 | }) |
47 | process.exit(-1) | 45 | this.client.on('reconnecting', (ms) => { |
48 | }) | 46 | logger.error(`Reconnecting to redis in ${ms}.`) |
47 | }) | ||
48 | this.client.on('close', () => { | ||
49 | logger.error('Connection to redis has closed.') | ||
50 | this.connected = false | ||
51 | }) | ||
52 | |||
53 | this.client.on('end', () => { | ||
54 | logger.error('Connection to redis has closed and no more reconnects will be done.') | ||
55 | }) | ||
49 | 56 | ||
50 | this.prefix = 'redis-' + WEBSERVER.HOST + '-' | 57 | this.prefix = 'redis-' + WEBSERVER.HOST + '-' |
51 | } | 58 | } |
52 | 59 | ||
53 | static getRedisClientOptions () { | 60 | static getRedisClientOptions (connectionName?: string, options: RedisOptions = {}): RedisOptions { |
54 | let config: RedisClientOptions<RedisModules, {}> = { | 61 | return { |
55 | socket: { | 62 | connectionName: [ 'PeerTube', connectionName ].join(''), |
56 | connectTimeout: 20000 // Could be slow since node use sync call to compile PeerTube | 63 | connectTimeout: 20000, // Could be slow since node use sync call to compile PeerTube |
57 | } | 64 | password: CONFIG.REDIS.AUTH, |
58 | } | 65 | db: CONFIG.REDIS.DB, |
59 | 66 | host: CONFIG.REDIS.HOSTNAME, | |
60 | if (CONFIG.REDIS.AUTH) { | 67 | port: CONFIG.REDIS.PORT, |
61 | config = { ...config, password: CONFIG.REDIS.AUTH } | 68 | path: CONFIG.REDIS.SOCKET, |
62 | } | 69 | showFriendlyErrorStack: true, |
63 | 70 | ...options | |
64 | if (CONFIG.REDIS.DB) { | ||
65 | config = { ...config, database: CONFIG.REDIS.DB } | ||
66 | } | ||
67 | |||
68 | if (CONFIG.REDIS.HOSTNAME && CONFIG.REDIS.PORT) { | ||
69 | config.socket = { ...config.socket, host: CONFIG.REDIS.HOSTNAME, port: CONFIG.REDIS.PORT } | ||
70 | } else { | ||
71 | config.socket = { ...config.socket, path: CONFIG.REDIS.SOCKET } | ||
72 | } | 71 | } |
73 | |||
74 | return config | ||
75 | } | 72 | } |
76 | 73 | ||
77 | getClient () { | 74 | getClient () { |
@@ -108,10 +105,24 @@ class Redis { | |||
108 | return this.removeValue(this.generateResetPasswordKey(userId)) | 105 | return this.removeValue(this.generateResetPasswordKey(userId)) |
109 | } | 106 | } |
110 | 107 | ||
111 | async getResetPasswordLink (userId: number) { | 108 | async getResetPasswordVerificationString (userId: number) { |
112 | return this.getValue(this.generateResetPasswordKey(userId)) | 109 | return this.getValue(this.generateResetPasswordKey(userId)) |
113 | } | 110 | } |
114 | 111 | ||
112 | /* ************ Two factor auth request ************ */ | ||
113 | |||
114 | async setTwoFactorRequest (userId: number, otpSecret: string) { | ||
115 | const requestToken = await generateRandomString(32) | ||
116 | |||
117 | await this.setValue(this.generateTwoFactorRequestKey(userId, requestToken), otpSecret, TWO_FACTOR_AUTH_REQUEST_TOKEN_LIFETIME) | ||
118 | |||
119 | return requestToken | ||
120 | } | ||
121 | |||
122 | async getTwoFactorRequestToken (userId: number, requestToken: string) { | ||
123 | return this.getValue(this.generateTwoFactorRequestKey(userId, requestToken)) | ||
124 | } | ||
125 | |||
115 | /* ************ Email verification ************ */ | 126 | /* ************ Email verification ************ */ |
116 | 127 | ||
117 | async setVerifyEmailVerificationString (userId: number) { | 128 | async setVerifyEmailVerificationString (userId: number) { |
@@ -342,6 +353,10 @@ class Redis { | |||
342 | return 'reset-password-' + userId | 353 | return 'reset-password-' + userId |
343 | } | 354 | } |
344 | 355 | ||
356 | private generateTwoFactorRequestKey (userId: number, token: string) { | ||
357 | return 'two-factor-request-' + userId + '-' + token | ||
358 | } | ||
359 | |||
345 | private generateVerifyEmailKey (userId: number) { | 360 | private generateVerifyEmailKey (userId: number) { |
346 | return 'verify-email-' + userId | 361 | return 'verify-email-' + userId |
347 | } | 362 | } |
@@ -369,15 +384,15 @@ class Redis { | |||
369 | } | 384 | } |
370 | 385 | ||
371 | private getSet (key: string) { | 386 | private getSet (key: string) { |
372 | return this.client.sMembers(this.prefix + key) | 387 | return this.client.smembers(this.prefix + key) |
373 | } | 388 | } |
374 | 389 | ||
375 | private addToSet (key: string, value: string) { | 390 | private addToSet (key: string, value: string) { |
376 | return this.client.sAdd(this.prefix + key, value) | 391 | return this.client.sadd(this.prefix + key, value) |
377 | } | 392 | } |
378 | 393 | ||
379 | private deleteFromSet (key: string, value: string) { | 394 | private deleteFromSet (key: string, value: string) { |
380 | return this.client.sRem(this.prefix + key, value) | 395 | return this.client.srem(this.prefix + key, value) |
381 | } | 396 | } |
382 | 397 | ||
383 | private deleteKey (key: string) { | 398 | private deleteKey (key: string) { |
@@ -391,16 +406,14 @@ class Redis { | |||
391 | return JSON.parse(value) | 406 | return JSON.parse(value) |
392 | } | 407 | } |
393 | 408 | ||
394 | private setObject (key: string, value: { [ id: string ]: number | string }) { | 409 | private setObject (key: string, value: { [ id: string ]: number | string }, expirationMilliseconds?: number) { |
395 | return this.setValue(key, JSON.stringify(value)) | 410 | return this.setValue(key, JSON.stringify(value), expirationMilliseconds) |
396 | } | 411 | } |
397 | 412 | ||
398 | private async setValue (key: string, value: string, expirationMilliseconds?: number) { | 413 | private async setValue (key: string, value: string, expirationMilliseconds?: number) { |
399 | const options = expirationMilliseconds | 414 | const result = expirationMilliseconds !== undefined |
400 | ? { PX: expirationMilliseconds } | 415 | ? await this.client.set(this.prefix + key, value, 'PX', expirationMilliseconds) |
401 | : {} | 416 | : await this.client.set(this.prefix + key, value) |
402 | |||
403 | const result = await this.client.set(this.prefix + key, value, options) | ||
404 | 417 | ||
405 | if (result !== 'OK') throw new Error('Redis set result is not OK.') | 418 | if (result !== 'OK') throw new Error('Redis set result is not OK.') |
406 | } | 419 | } |