feat(queue): support REDIS_PASS for Redis AUTH

Wire optional REDIS_PASS into the BullMQ ioredis connection, redact
credentials in startup logs, and document REDIS_PASS plus JFLOW_ROLE.

Co-authored-by: Nasyarobby Putra <nasyarobby@gmail.com>
This commit is contained in:
Cursor Agent
2026-08-19 09:05:29 +00:00
co-authored by nsrb
parent d122bd9a7f
commit 756ba8a7ac
3 changed files with 36 additions and 8 deletions
+3 -3
View File
@@ -26,7 +26,7 @@ import {
closeRedis,
createWorkflowQueue,
createWorkflowWorker,
getRedisUrl,
getRedisUrlForLog,
} from "./workflow-queue.js";
await migrate();
@@ -52,13 +52,13 @@ const role = (process.env.JFLOW_ROLE || "all").toLowerCase();
const runApi = role === "all" || role === "api";
const runWorker = role === "all" || role === "worker";
log.info({ redis: getRedisUrl(), role }, "starting jerapah-flow");
log.info({ redis: getRedisUrlForLog(), role }, "starting jerapah-flow");
const workflowQueue = createWorkflowQueue();
try {
await workflowQueue.waitUntilReady();
} catch (err) {
log.error({ err, redis: getRedisUrl() }, "failed to connect to Redis");
log.error({ err, redis: getRedisUrlForLog() }, "failed to connect to Redis");
process.exit(1);
}
+29 -2
View File
@@ -13,6 +13,28 @@ export function getRedisUrl() {
return process.env.REDIS_URL || DEFAULT_REDIS_URL;
}
/**
* Optional Redis AUTH password. Applied even when REDIS_URL has no embedded credentials.
* @returns {string | undefined}
*/
export function getRedisPassword() {
const pass = process.env.REDIS_PASS;
if (typeof pass !== "string" || pass.length === 0) return undefined;
return pass;
}
/** Redact credentials for logs. */
export function getRedisUrlForLog() {
try {
const url = new URL(getRedisUrl());
if (url.password || getRedisPassword()) url.password = "***";
if (url.username) url.username = url.username ? "***" : "";
return url.toString();
} catch {
return getRedisUrl();
}
}
export function getQueueName() {
return process.env.JFLOW_QUEUE_NAME || DEFAULT_QUEUE_NAME;
}
@@ -29,10 +51,15 @@ export function getWorkerConcurrency() {
*/
export function getSharedConnection() {
if (sharedConnection) return sharedConnection;
sharedConnection = new IORedis(getRedisUrl(), {
/** @type {import("ioredis").RedisOptions} */
const options = {
maxRetriesPerRequest: null,
enableReadyCheck: true,
});
};
const password = getRedisPassword();
if (password) options.password = password;
sharedConnection = new IORedis(getRedisUrl(), options);
sharedConnection.on("error", (err) => {
log.error({ err }, "redis connection error");
});