chatapi / src /redis /presenceClient.ts
wilooper's picture
initial commit
87cb242
Raw History Blame Contribute Delete
2.71 kB
import Redis from 'ioredis';
import { CONFIG } from '../config';
import { logger } from '../utils/logger';
/**
* Redis Bucket 1 β€” Presence
*
* Key schema:
* presence:uid:{uid} β†’ socket_id (TTL = PRESENCE_TTL_S)
* status:uid:{uid} β†’ "online" (TTL = PRESENCE_TTL_S)
*
* Separate client instance so Bucket 1 errors never affect Bucket 2.
*/
const presenceClient = new Redis(CONFIG.REDIS_PRESENCE_URL, {
maxRetriesPerRequest: 2,
connectTimeout: 5000,
lazyConnect: false,
enableReadyCheck: true,
// Keep-alive so the HF container connection stays warm
keepAlive: 10000,
});
presenceClient.on('connect', () => logger.info('Presence Redis connected'));
presenceClient.on('error', (err: Error) =>
logger.error('Presence Redis error', { error: err.message })
);
presenceClient.on('reconnecting', () => logger.warn('Presence Redis reconnecting'));
export { presenceClient };
// ── Key builders ─────────────────────────────────────────────────────────────
const presenceKey = (uid: string) => `presence:uid:${uid}`;
const statusKey = (uid: string) => `status:uid:${uid}`;
// ── Operations ───────────────────────────────────────────────────────────────
/** Map uid β†’ socketId, mark user online. Called on WS connect. */
export async function setOnline(uid: string, socketId: string): Promise<void> {
const ttl = CONFIG.PRESENCE_TTL_S;
// MULTI/EXEC keeps it atomic
await presenceClient
.multi()
.set(presenceKey(uid), socketId, 'EX', ttl)
.set(statusKey(uid), 'online', 'EX', ttl)
.exec();
}
/** Refresh TTL on heartbeat. Does not overwrite the socket id. */
export async function refreshPresence(uid: string): Promise<void> {
const ttl = CONFIG.PRESENCE_TTL_S;
await presenceClient
.multi()
.expire(presenceKey(uid), ttl)
.expire(statusKey(uid), ttl)
.exec();
}
/** Remove user from presence. Called on WS close. */
export async function setOffline(uid: string): Promise<void> {
await presenceClient
.multi()
.del(presenceKey(uid))
.del(statusKey(uid))
.exec();
}
/** Returns the socket id for a uid, or null if offline. */
export async function getSocketId(uid: string): Promise<string | null> {
return presenceClient.get(presenceKey(uid));
}
/** Returns true if the user is online. */
export async function isOnline(uid: string): Promise<boolean> {
const status = await presenceClient.get(statusKey(uid));
return status === 'online';
}