import IORedis, { Cluster } from 'ioredis'; import { DynamicModule, Module, ModuleMetadata, Type } from '@nestjs/common'; import { parse } from '../../utils/url'; export const REDIS_MODULE_OPTIONS = Symbol('REDIS_MODULE_OPTIONS'); export const REDIS_CLIENT = 'REDIS_CLIENT'; export const REDIS_DEFAULT_CLIENT = 'default'; export type RedisClientOptions = { url: string; }; export type RedisModuleOptions = RedisClientOptions & { name?: string; }; export interface RedisOptionsFactory { createRedisOptions(): Promise | RedisModuleOptions; } export interface RedisModuleAsyncOptions extends Pick { name?: string; useExisting?: Type; useClass?: Type; useFactory?: (...args: any[]) => Promise | RedisModuleOptions; inject?: any[]; } export const getRedisClientProviderKey = (name?: string) => [REDIS_CLIENT, name || REDIS_DEFAULT_CLIENT].join(':'); @Module({}) export class RedisModule { static register(config: RedisModuleOptions): DynamicModule { const { name } = config; return { module: RedisModule, providers: [ { provide: REDIS_MODULE_OPTIONS, useValue: config, }, { provide: getRedisClientProviderKey(name), useValue: this.getRedisClientProvider(config), }, ], exports: [getRedisClientProviderKey(name)], }; } static registerAsync(options: RedisModuleAsyncOptions) { const { name } = options; return { module: RedisModule, providers: this.createAsyncProviders(options), exports: [getRedisClientProviderKey(name)], }; } private static createAsyncProviders(options: RedisModuleAsyncOptions) { if (options.useExisting || options.useFactory) { return [...this.createAsyncOptionsProvider(options)]; } return [ ...this.createAsyncOptionsProvider(options), { provide: options.useClass, useClass: options.useClass, }, ]; } private static createAsyncOptionsProvider(options: RedisModuleAsyncOptions) { const { name } = options; if (options.useFactory) { return [ { provide: REDIS_MODULE_OPTIONS, useFactory: options.useFactory, inject: options.inject || [], }, { provide: getRedisClientProviderKey(name), useFactory: this.getRedisClientProvider, inject: [REDIS_MODULE_OPTIONS], }, ]; } return [ { provide: REDIS_MODULE_OPTIONS, useFactory: (optionsFactory: RedisOptionsFactory) => optionsFactory.createRedisOptions(), inject: [options.useExisting || options.useClass], }, { provide: getRedisClientProviderKey(name), useFactory: this.getRedisClientProvider, inject: [REDIS_MODULE_OPTIONS], }, ]; } private static async getRedisClientProvider(_options: RedisModuleOptions) { const { url } = _options; const parsed = parse(url); const cluster = parsed.hostname.includes('clustercfg'); const tls = parsed.protocol === 'rediss:' ? { checkServerIdentity: () => undefined, } : undefined; if (cluster) { return new Cluster([{ host: parsed.hostname, port: parseFloat(parsed.port) }], { redisOptions: { tls }, }); } return new IORedis(url); } }