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.
This commit is contained in:
+18
-6
@@ -155,15 +155,15 @@ export type Database = {
|
||||
Relationships: []
|
||||
}
|
||||
door_connections: {
|
||||
Row: { id: string; space_id: string; name: string; adapter: string; base_url: string; pinned_host: string; auth_mode: string; auth_param: string | null; secret_ref: string | null; verbs: Json; allow_member_self_entry: boolean; is_enabled: boolean; created_by: string | null; created_at: string; updated_at: string }
|
||||
Insert: { id?: string; space_id: string; name: string; adapter?: string; base_url: string; pinned_host: string; auth_mode?: string; auth_param?: string | null; secret_ref?: string | null; verbs?: Json; allow_member_self_entry?: boolean; is_enabled?: boolean; created_by?: string | null; created_at?: string; updated_at?: string }
|
||||
Update: { id?: string; space_id?: string; name?: string; adapter?: string; base_url?: string; pinned_host?: string; auth_mode?: string; auth_param?: string | null; secret_ref?: string | null; verbs?: Json; allow_member_self_entry?: boolean; is_enabled?: boolean; created_by?: string | null; created_at?: string; updated_at?: string }
|
||||
Row: { id: string; space_id: string; name: string; adapter: string; base_url: string; pinned_host: string; auth_mode: string; auth_param: string | null; secret_ref: string | null; verbs: Json; allow_member_self_entry: boolean; is_enabled: boolean; inbound_enabled: boolean; inbound_secret_ref: string | null; created_by: string | null; created_at: string; updated_at: string }
|
||||
Insert: { id?: string; space_id: string; name: string; adapter?: string; base_url: string; pinned_host: string; auth_mode?: string; auth_param?: string | null; secret_ref?: string | null; verbs?: Json; allow_member_self_entry?: boolean; is_enabled?: boolean; inbound_enabled?: boolean; inbound_secret_ref?: string | null; created_by?: string | null; created_at?: string; updated_at?: string }
|
||||
Update: { id?: string; space_id?: string; name?: string; adapter?: string; base_url?: string; pinned_host?: string; auth_mode?: string; auth_param?: string | null; secret_ref?: string | null; verbs?: Json; allow_member_self_entry?: boolean; is_enabled?: boolean; inbound_enabled?: boolean; inbound_secret_ref?: string | null; created_by?: string | null; created_at?: string; updated_at?: string }
|
||||
Relationships: []
|
||||
}
|
||||
door_access_log: {
|
||||
Row: { id: string; space_id: string; connection_id: string | null; actor_member_id: string | null; target_member_id: string | null; action: string; success: boolean; detail: string | null; occurred_at: string }
|
||||
Insert: { id?: string; space_id: string; connection_id?: string | null; actor_member_id?: string | null; target_member_id?: string | null; action: string; success?: boolean; detail?: string | null; occurred_at?: string }
|
||||
Update: { id?: string; space_id?: string; connection_id?: string | null; actor_member_id?: string | null; target_member_id?: string | null; action?: string; success?: boolean; detail?: string | null; occurred_at?: string }
|
||||
Row: { id: string; space_id: string; connection_id: string | null; actor_member_id: string | null; target_member_id: string | null; action: string; success: boolean; detail: string | null; dedupe_key: string | null; occurred_at: string }
|
||||
Insert: { id?: string; space_id: string; connection_id?: string | null; actor_member_id?: string | null; target_member_id?: string | null; action: string; success?: boolean; detail?: string | null; dedupe_key?: string | null; occurred_at?: string }
|
||||
Update: { id?: string; space_id?: string; connection_id?: string | null; actor_member_id?: string | null; target_member_id?: string | null; action?: string; success?: boolean; detail?: string | null; dedupe_key?: string | null; occurred_at?: string }
|
||||
Relationships: []
|
||||
}
|
||||
door_card_slots: {
|
||||
@@ -172,6 +172,18 @@ export type Database = {
|
||||
Update: { id?: string; space_id?: string; connection_id?: string; card_id?: string; slot?: number; created_by?: string | null; created_at?: string; updated_at?: string }
|
||||
Relationships: []
|
||||
}
|
||||
api_buttons: {
|
||||
Row: { id: string; space_id: string; label: string; button_group: string; sort_order: number; method: string; base_url: string; pinned_host: string; url_template: string | null; headers: Json; body_template: string | null; auth_mode: string; auth_param: string | null; secret_ref: string | null; required_permission: string; confirm: boolean; is_enabled: boolean; created_by: string | null; created_at: string; updated_at: string }
|
||||
Insert: { id?: string; space_id: string; label: string; button_group?: string; sort_order?: number; method?: string; base_url: string; pinned_host: string; url_template?: string | null; headers?: Json; body_template?: string | null; auth_mode?: string; auth_param?: string | null; secret_ref?: string | null; required_permission?: string; confirm?: boolean; is_enabled?: boolean; created_by?: string | null; created_at?: string; updated_at?: string }
|
||||
Update: { id?: string; space_id?: string; label?: string; button_group?: string; sort_order?: number; method?: string; base_url?: string; pinned_host?: string; url_template?: string | null; headers?: Json; body_template?: string | null; auth_mode?: string; auth_param?: string | null; secret_ref?: string | null; required_permission?: string; confirm?: boolean; is_enabled?: boolean; created_by?: string | null; created_at?: string; updated_at?: string }
|
||||
Relationships: []
|
||||
}
|
||||
api_call_log: {
|
||||
Row: { id: string; space_id: string; button_id: string | null; actor_member_id: string | null; action: string; success: boolean; detail: string | null; occurred_at: string }
|
||||
Insert: { id?: string; space_id: string; button_id?: string | null; actor_member_id?: string | null; action: string; success?: boolean; detail?: string | null; occurred_at?: string }
|
||||
Update: { id?: string; space_id?: string; button_id?: string | null; actor_member_id?: string | null; action?: string; success?: boolean; detail?: string | null; occurred_at?: string }
|
||||
Relationships: []
|
||||
}
|
||||
space_visits: {
|
||||
Row: { id: string; space_id: string; member_id: string; checked_in_at: string; checked_out_at: string | null; is_host: boolean; check_in_note: string | null; check_out_note: string | null; created_at: string }
|
||||
Insert: { id?: string; space_id: string; member_id: string; checked_in_at?: string; checked_out_at?: string | null; is_host?: boolean; check_in_note?: string | null; check_out_note?: string | null; created_at?: string }
|
||||
|
||||
Reference in New Issue
Block a user