// @ts-check
/**
* Minimaler Redis-KV-Client für TTL-Keys (Runner-Registrierungscodes, …).
*
* Bewusst getrennt vom Pub/Sub-Client (utils/events.js): Diese Funktionen
* brauchen nur SET mit EX / GET / DEL / EXISTS. Verbindung wird lazy nach dem
* Secret-Load aufgebaut (mergedSecrets aus http-server.js). Fällt Redis aus,
* degradieren die Funktionen auf `false`/`null` — der Aufrufer entscheidet,
* ob er hart oder weich fehlschlagen will.
*/
import redis from 'redis';
import { errorLogger } from '../utils/requestLogger.js';
/** @type {import('redis').RedisClientType | null} */
let client = null;
let initialized = false;
const getConfig = async () => {
const { mergedSecrets } = await import('../http-server.js');
const secrets = mergedSecrets || {};
const server = secrets.redisServer || process.env.REDIS_SERVER;
const port = secrets.redisPort || process.env.REDIS_PORT || 6379;
const password = secrets.redisPassword || process.env.REDIS_PASSWORD || undefined;
return { server, port, password };
};
/** @type {Promise<boolean>} */
let connecting = null;
const connect = async () => {
if (initialized) return !!client;
if (connecting) return connecting;
connecting = (async () => {
try {
const { server, port, password } = await getConfig();
if (!server) return false;
client = redis.createClient({
url: `redis://${server}:${port}`,
password,
legacyMode: false,
socket: {
connectTimeout: 10000,
reconnectStrategy: (retries) => (retries > 5
? new Error('Max redis retries reached')
: Math.min(retries * 1000, 5000)),
},
});
client.on('error', (err) => console.warn('[runnerRedis] client error', err?.message));
await client.connect();
initialized = true;
return true;
} catch (err) {
console.warn('[runnerRedis] connect failed — transient keys (registration codes) unavailable', err?.message);
errorLogger(err);
return false;
} finally {
connecting = null;
}
})();
return connecting;
};
/**
* Key mit TTL setzen. @returns {Promise<boolean>} true bei Erfolg.
*/
export const kvSetEx = async (key, seconds, value) => {
try {
if (!(await connect())) return false;
await client.set(key, value, { EX: seconds });
return true;
} catch (err) {
console.warn('[runnerRedis] set failed', err?.message);
return false;
}
};
/** @returns {Promise<string|null>} */
export const kvGet = async (key) => {
try {
if (!(await connect())) return null;
return await client.get(key);
} catch (err) {
console.warn('[runnerRedis] get failed', err?.message);
return null;
}
};
/** @returns {Promise<boolean>} true wenn der Key (noch) existiert. */
export const kvExists = async (key) => {
try {
if (!(await connect())) return false;
return (await client.exists(key)) === 1;
} catch (err) {
console.warn('[runnerRedis] exists failed', err?.message);
return false;
}
};
/** @returns {Promise<boolean>} */
export const kvDel = async (key) => {
try {
if (!(await connect())) return false;
await client.del(key);
return true;
} catch (err) {
console.warn('[runnerRedis] del failed', err?.message);
return false;
}
};