aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/redis.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib/redis.ts')
-rw-r--r--server/lib/redis.ts105
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 @@
1import { createClient, RedisClientOptions, RedisModules } from 'redis' 1import IoRedis, { RedisOptions } from 'ioredis'
2import { exists } from '@server/helpers/custom-validators/misc' 2import { exists } from '@server/helpers/custom-validators/misc'
3import { sha256 } from '@shared/extra-utils' 3import { sha256 } from '@shared/extra-utils'
4import { logger } from '../helpers/logger' 4import { 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 }