Files
backend/src/nats/CardKV.ts

68 lines
2.0 KiB
TypeScript

import { KvEntry } from 'nats';
import NatsClient from './NatsClient';
const BUCKET = 'card_state';
/** 1 hour TTL in milliseconds — sliding window, reset on each access. */
const TTL_MS = 60 * 60 * 1000;
/**
* Cached card data stored in NATS KV.
* Keyed by the stripped card id (rawBarcode % 200000000000).
* TTL of 1 hour of inactivity — re-put on each access to slide the window.
*/
export interface CardKVEntry {
runnerId: number;
runnerDisplayName: string;
enabled: boolean;
}
async function getBucket() {
return NatsClient.getKV(BUCKET, { ttl: TTL_MS });
}
function entryKey(cardId: number): string {
return `card.${cardId}`;
}
/**
* Returns the cached CardKVEntry for the given stripped card id, or null on a miss.
* On a cache hit the entry is re-put with a fresh TTL to slide the inactivity window.
*/
export async function getCardEntry(cardId: number): Promise<CardKVEntry | null> {
const bucket = await getBucket();
let entry: KvEntry | null = null;
try {
entry = await bucket.get(entryKey(cardId));
} catch {
return null;
}
if (!entry || entry.operation === 'DEL' || entry.operation === 'PURGE') {
return null;
}
const value = JSON.parse(entry.string()) as CardKVEntry;
// Re-put to slide the TTL window
await bucket.put(entryKey(cardId), JSON.stringify(value));
return value;
}
/**
* Writes a CardKVEntry for the given stripped card id with a 1-hour TTL.
*/
export async function setCardEntry(cardId: number, entry: CardKVEntry): Promise<void> {
const bucket = await getBucket();
await bucket.put(entryKey(cardId), JSON.stringify(entry));
}
/**
* Removes the cached entry for the given stripped card id.
* Call on card update (runner reassignment, enable/disable change) or delete.
*/
export async function deleteCardEntry(cardId: number): Promise<void> {
const bucket = await getBucket();
try {
await bucket.delete(entryKey(cardId));
} catch {
// Entry may not exist in KV yet — that's fine
}
}