From 23e4449f2795571d14f124f4d2de3c78e63392c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Skyler=20M=C3=A4ntysaari?= <samip5@users.noreply.github.com>
Date: Fri, 31 Mar 2023 23:33:21 +0300
Subject: [PATCH] feat(server): redis sentinel support (#2141)

* feat(server): redis sentinel initial support

* feat(server): Lint fixes

* Include example for Redis Sentinel.

* Address PR comments
---
 docker/example.env                    |  6 ++++++
 server/libs/infra/src/infra.config.ts | 28 ++++++++++++++++++++-------
 2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/docker/example.env b/docker/example.env
index 5281145663..357d338a59 100644
--- a/docker/example.env
+++ b/docker/example.env
@@ -16,6 +16,11 @@ DB_DATABASE_NAME=immich
 
 REDIS_HOSTNAME=immich_redis
 
+# REDIS_URL will be used to pass custom options to ioredis.
+# Example for Sentinel
+# {"sentinels":[{"host":"redis-sentinel-node-0","port":26379},{"host":"redis-sentinel-node-1","port":26379},{"host":"redis-sentinel-node-2","port":26379}],"name":"redis-sentinel"}
+# REDIS_URL=ioredis://eyJzZW50aW5lbHMiOlt7Imhvc3QiOiJyZWRpcy1zZW50aW5lbDEiLCJwb3J0IjoyNjM3OX0seyJob3N0IjoicmVkaXMtc2VudGluZWwyIiwicG9ydCI6MjYzNzl9XSwibmFtZSI6Im15bWFzdGVyIn0=
+
 # Optional Redis settings:
 
 # Note: these parameters are not automatically passed to the Redis Container
@@ -24,6 +29,7 @@ REDIS_HOSTNAME=immich_redis
 
 # REDIS_PORT=6379
 # REDIS_DBINDEX=0
+# REDIS_USERNAME=
 # REDIS_PASSWORD=
 # REDIS_SOCKET=
 
diff --git a/server/libs/infra/src/infra.config.ts b/server/libs/infra/src/infra.config.ts
index c0f6f94279..33be44b4e0 100644
--- a/server/libs/infra/src/infra.config.ts
+++ b/server/libs/infra/src/infra.config.ts
@@ -3,13 +3,27 @@ import { BullModuleOptions } from '@nestjs/bull';
 import { RedisOptions } from 'ioredis';
 import { ConfigurationOptions } from 'typesense/lib/Typesense/Configuration';
 
-export const redisConfig: RedisOptions = {
-  host: process.env.REDIS_HOSTNAME || 'immich_redis',
-  port: parseInt(process.env.REDIS_PORT || '6379'),
-  db: parseInt(process.env.REDIS_DBINDEX || '0'),
-  password: process.env.REDIS_PASSWORD || undefined,
-  path: process.env.REDIS_SOCKET || undefined,
-};
+function parseRedisConfig(): RedisOptions {
+  const redisUrl = process.env.REDIS_URL;
+  if (redisUrl && redisUrl.startsWith('ioredis://')) {
+    try {
+      const decodedString = Buffer.from(redisUrl.slice(10), 'base64').toString();
+      return JSON.parse(decodedString);
+    } catch (error) {
+      throw new Error(`Failed to decode redis options: ${error}`);
+    }
+  }
+  return {
+    host: process.env.REDIS_HOSTNAME || 'immich_redis',
+    port: parseInt(process.env.REDIS_PORT || '6379'),
+    db: parseInt(process.env.REDIS_DBINDEX || '0'),
+    username: process.env.REDIS_USERNAME || undefined,
+    password: process.env.REDIS_PASSWORD || undefined,
+    path: process.env.REDIS_SOCKET || undefined,
+  };
+}
+
+export const redisConfig: RedisOptions = parseRedisConfig();
 
 export const bullConfig: BullModuleOptions = {
   prefix: 'immich_bull',