68 lines
2.0 KiB
TypeScript
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
|
|
}
|
|
}
|