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/api/src/demo/demo.controller.ts
Normal file
57
apps/api/src/demo/demo.controller.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import { Controller, Get, Post, Body, Req, UseGuards } from "@nestjs/common";
|
||||
import { RbacGuard, RequirePermission, type AuthenticatedRequest } from "../rbac/rbac.guard.js";
|
||||
import { appendTenantAudit } from "../audit/audit.service.js";
|
||||
import { withTenant } from "../db/client.js";
|
||||
import type { Request } from "express";
|
||||
|
||||
@Controller("demo")
|
||||
@UseGuards(RbacGuard)
|
||||
export class DemoController {
|
||||
@Get("health-audit")
|
||||
@RequirePermission("audit.view")
|
||||
async viewAudit(@Req() req: Request & AuthenticatedRequest): Promise<{ workspaceId: string }> {
|
||||
return { workspaceId: req.session?.workspaceId ?? "none" };
|
||||
}
|
||||
|
||||
@Post("connections")
|
||||
@RequirePermission("connection.manage")
|
||||
async addConnection(
|
||||
@Req() req: Request & AuthenticatedRequest,
|
||||
@Body() body: { provider: string; channelId: string },
|
||||
): Promise<{ id: string }> {
|
||||
const session = req.session;
|
||||
if (session === undefined) throw new Error("unreachable: guard sets session");
|
||||
const workspaceId = session.workspaceId;
|
||||
return withTenant(workspaceId, async (client) => {
|
||||
const inserted = await client.query(
|
||||
"INSERT INTO connections (workspace_id, provider, channel_id) VALUES ($1, $2, $3) RETURNING id",
|
||||
[workspaceId, body.provider, body.channelId],
|
||||
);
|
||||
const id = inserted.rows[0]?.id as string;
|
||||
await client.query("SELECT audit_append($1, $2, $3, 'connection.created', 'connection', $4, $5)", [
|
||||
workspaceId,
|
||||
session.userId,
|
||||
session.role,
|
||||
id,
|
||||
JSON.stringify({ provider: body.provider }),
|
||||
]);
|
||||
return { id };
|
||||
});
|
||||
}
|
||||
|
||||
@Post("echo-audited")
|
||||
@RequirePermission("workspace.view")
|
||||
async echoAudited(@Req() req: Request & AuthenticatedRequest, @Body() body: { apiKey?: string; note?: string }): Promise<{ ok: true }> {
|
||||
const session = req.session;
|
||||
if (session === undefined) throw new Error("unreachable: guard sets session");
|
||||
await appendTenantAudit(session.workspaceId, {
|
||||
actorUserId: session.userId,
|
||||
actorRole: session.role,
|
||||
action: "demo.echo",
|
||||
targetType: "demo",
|
||||
targetId: "echo",
|
||||
diff: { apiKey: body.apiKey ?? null, note: body.note ?? null },
|
||||
});
|
||||
return { ok: true };
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue