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
57
apps/scheduler/scripts/wp3-integration.mjs
Normal file
57
apps/scheduler/scripts/wp3-integration.mjs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// Local compiled-worker smoke. Uses a dedicated queue and only safe missing-job input.
|
||||
import { spawn } from "node:child_process";
|
||||
import { createServer } from "node:net";
|
||||
import { resolve } from "node:path";
|
||||
import { Client, Connection, WorkflowExecutionAlreadyStartedError } from "@temporalio/client";
|
||||
import { randomUUID } from "node:crypto";
|
||||
if (!process.env.PUBLISHING_IT_DATABASE_URL) throw new Error("PUBLISHING_IT_DATABASE_URL is required (migrate dedicated local DB first)");
|
||||
const probe = createServer();
|
||||
await new Promise((done) => probe.listen(0, "127.0.0.1", done));
|
||||
const port = probe.address().port;
|
||||
await new Promise((done) => probe.close(done));
|
||||
const queue = `wp3-sol-${randomUUID()}`;
|
||||
const child = spawn(process.execPath, [resolve("dist/worker.js")], { env: { ...process.env, SCHEDULER_DATABASE_URL: process.env.PUBLISHING_IT_DATABASE_URL, SCHEDULER_HEALTH_PORT: String(port), TEMPORAL_TASK_QUEUE: queue, TEMPORAL_ADDRESS: "127.0.0.1:7233" }, stdio: ["ignore", "pipe", "pipe"] });
|
||||
let output = "";
|
||||
child.stdout.on("data", (chunk) => { output += chunk.toString(); });
|
||||
child.stderr.on("data", (chunk) => { output += chunk.toString(); });
|
||||
const exit = new Promise((done) => child.once("exit", (code) => done(code)));
|
||||
let connection;
|
||||
try {
|
||||
let ready = false;
|
||||
for (let count = 0; count < 100; count++) {
|
||||
if (child.exitCode !== null) throw new Error("compiled worker exited before readiness");
|
||||
try { const response = await fetch(`http://127.0.0.1:${port}/ready`, { signal: AbortSignal.timeout(1000) }); ready = response.status === 200; } catch {}
|
||||
if (ready) break;
|
||||
await new Promise((done) => setTimeout(done, 300));
|
||||
}
|
||||
if (!ready) throw new Error("compiled worker readiness timed out");
|
||||
connection = await Connection.connect({ address: "127.0.0.1:7233" });
|
||||
const client = new Client({ connection, namespace: process.env.TEMPORAL_NAMESPACE ?? "default" });
|
||||
const ref = { workspaceId: randomUUID(), scheduleId: randomUUID(), generation: "1", idempotencyKey: `wp3-smoke-${randomUUID()}` };
|
||||
// Use an existing, empty fixture workspace so claim returns a safe missing-fence
|
||||
// outcome. This smoke does not acquire any credential or call a provider.
|
||||
const { Pool } = await import("pg");
|
||||
const pool = new Pool({ connectionString: process.env.PUBLISHING_IT_SUPER_DATABASE_URL });
|
||||
const workspace = await pool.query("INSERT INTO workspaces(name,slug) VALUES('Temporal Smoke',$1) RETURNING id", [queue]);
|
||||
ref.workspaceId = workspace.rows[0].id;
|
||||
try {
|
||||
const workflowId = `scheduled-publication-${ref.idempotencyKey}`;
|
||||
const handle = await client.workflow.start("scheduledPublish", { taskQueue: queue, workflowId, args: [ref] });
|
||||
const result = await handle.result();
|
||||
if (result.kind !== "failed" || result.code !== "EXECUTION_FENCE_MISSING") throw new Error("unexpected workflow result");
|
||||
let duplicate = false;
|
||||
try { await client.workflow.start("scheduledPublish", { taskQueue: queue, workflowId, args: [ref], workflowIdReusePolicy: "REJECT_DUPLICATE" }); }
|
||||
catch (error) { if (!(error instanceof WorkflowExecutionAlreadyStartedError)) throw error; duplicate = true; }
|
||||
if (!duplicate) throw new Error("duplicate start was not fenced");
|
||||
console.log("PASS: compiled worker readiness, registry/workflow bundle, real Temporal activity, structured outcome, duplicate start, graceful shutdown. No provider sends.");
|
||||
} finally { await pool.query("DELETE FROM workspaces WHERE id=$1", [ref.workspaceId]); await pool.end(); }
|
||||
} catch (error) {
|
||||
console.error(output.slice(-6000));
|
||||
throw error;
|
||||
} finally {
|
||||
child.kill("SIGTERM");
|
||||
const code = await Promise.race([exit, new Promise((done) => setTimeout(() => done("timeout"), 15_000))]);
|
||||
if (code === "timeout") { child.kill("SIGKILL"); throw new Error("worker did not drain"); }
|
||||
if (code !== 0) throw new Error(`worker shutdown exit code ${code}`);
|
||||
await connection?.close();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue