Tactical Media Broadcast Hub — initial release
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/.
This commit is contained in:
commit
aa5e262f3a
880 changed files with 111042 additions and 0 deletions
28
apps/api/src/feeds/testing.ts
Normal file
28
apps/api/src/feeds/testing.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
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 {}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue