Self-hosted multi-platform broadcast newsroom for activist collectives: publishing pipeline with group approval and DST-proof scheduling, TweetDeck-style column wall with hostile-content-safe rendering, live control room with Auto Director, bridged chat governance with polls and quorum, distributed sandboxed transcode workers, encrypted content-addressed archive, monthly analytics. MIT licensed. See README.md and docs/.
28 lines
1.3 KiB
TypeScript
28 lines
1.3 KiB
TypeScript
import type { FeedDependencies } from "./feeds.dependencies.js";
|
|
|
|
/** TTL-faithful Redis surface, including ownership checks for renewable leases. */
|
|
type FeedRedis = FeedDependencies["redis"];
|
|
export class MemoryFeedRedis implements FeedRedis {
|
|
readonly entries = new Map<string, { value: string; expiresAt: number }>();
|
|
async get(key: string): Promise<string | null> {
|
|
const entry = this.entries.get(key);
|
|
if (!entry || entry.expiresAt <= Date.now()) { this.entries.delete(key); return null; }
|
|
return entry.value;
|
|
}
|
|
async set(key: string, value: string, options: { nx?: boolean; pxMs?: number } = {}): Promise<boolean> {
|
|
// Atomic NX check: do not yield between reading and writing.
|
|
const entry = this.entries.get(key);
|
|
if (options.nx && entry && entry.expiresAt > Date.now()) return false;
|
|
this.entries.set(key, { value, expiresAt: Date.now() + (options.pxMs ?? 1000) });
|
|
return true;
|
|
}
|
|
async eval(script: string, keys: string[], args: string[]): Promise<number> {
|
|
const key = keys[0] ?? "";
|
|
const entry = this.entries.get(key);
|
|
if (!entry || entry.value !== args[0] || entry.expiresAt <= Date.now()) return 0;
|
|
if (script.includes("PEXPIRE")) entry.expiresAt = Date.now() + Number(args[1]);
|
|
else this.entries.delete(key);
|
|
return 1;
|
|
}
|
|
close(): void {}
|
|
}
|