Files
hackerspace.sh/app/(app)/door/buttons/page.tsx
T
Moheeb Zara 7ec90b0f1e Door epic phases 4-5: inbound access-log ingest + universal API-call buttons
Phase 4 (migration 053): pull entry/denied events into door_access_log and
match each card to a member, via two transports sharing one ingest core.
Poll: a CRON_SECRET-guarded /api/cron/door-ingest reads each opted-in
native-HeatSync connection's ?z log through the existing hardened executor
and parses it (firmware-characterized, best-effort ring buffer). Webhook:
/api/door/inbound/[connection] accepts pushed event JSON authenticated by a
per-connection bearer secret from the vault. Both dedupe on
(connection_id, dedupe_key) so re-polls and retries are no-ops. Adds an
inbound config panel to /door/manage.

Phase 5 (migration 054): admins define api_buttons that fire a configured
HTTP call (full verbs + headers + body) when an authorized member presses
them, through the same hardened egress (now shared between callDoor and the
new callApi). Each button carries a required_permission (default the new
apicall.invoke code); members see only buttons they may press. Builder at
/door/buttons with a door-template preset; members press from /doors. Every
press is recorded in api_call_log with secrets redacted.

Both subsystems are inert until an admin enables inbound on a connection or
creates a button. 624 unit + 50 integration tests; migrations applied
top-to-bottom in schema.sql; types and docs updated.
2026-05-20 20:45:44 -07:00

26 lines
843 B
TypeScript

import { requireDoorManagerPage } from '@/lib/door-guard'
import { listApiButtons, listSecretTitles, listApiCallLog } from '@/lib/actions'
import { PERMISSIONS } from '@/lib/permissions-catalog'
import { ApiButtonsClient } from './api-buttons-client'
export const dynamic = 'force-dynamic'
export default async function ApiButtonsPage() {
await requireDoorManagerPage()
const [buttons, secrets, log] = await Promise.all([
listApiButtons(),
listSecretTitles(),
listApiCallLog(),
])
return (
<ApiButtonsClient
initial={'data' in buttons ? (buttons.data as unknown[]) : []}
secrets={'data' in secrets ? (secrets.data as { id: string; title: string }[]) : []}
log={'data' in log ? (log.data as unknown[]) : []}
permissions={PERMISSIONS.map(p => ({ code: p.code, label: p.label }))}
/>
)
}