spine-framework 0.3.145 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +76 -0
- package/.framework/.spine-manifest.json +19 -0
- package/.framework/cli/commands/agents.ts +61 -33
- package/.framework/cli/commands/auth.ts +60 -28
- package/.framework/cli/commands/create-app.ts +212 -80
- package/.framework/cli/commands/dev.ts +184 -68
- package/.framework/cli/commands/doctor.ts +324 -152
- package/.framework/cli/commands/generate.ts +119 -68
- package/.framework/cli/commands/init.ts +141 -242
- package/.framework/cli/commands/install-app.ts +1336 -314
- package/.framework/cli/commands/items.ts +31 -29
- package/.framework/cli/commands/migrate.ts +299 -156
- package/.framework/cli/commands/migrations.ts +62 -30
- package/.framework/cli/commands/pipelines.ts +31 -29
- package/.framework/cli/commands/status.ts +105 -45
- package/.framework/cli/commands/system.ts +47 -42
- package/.framework/cli/commands/test.ts +161 -102
- package/.framework/cli/commands/upgrade.ts +260 -0
- package/.framework/cli/commands/validate-app.ts +517 -161
- package/.framework/cli/context.ts +117 -85
- package/.framework/cli/env-loader.ts +74 -43
- package/.framework/cli/index.ts +70 -46
- package/.framework/cli/lib/project-files.ts +404 -0
- package/.framework/docs/guides/testing-guide.md +1 -1
- package/.framework/docs/runtime-types.md +72 -72
- package/.framework/functions/_shared/access.ts +84 -0
- package/.framework/functions/_shared/agent-runner.ts +5051 -1026
- package/.framework/functions/_shared/app-manifest.ts +214 -94
- package/.framework/functions/_shared/ast.ts +214 -0
- package/.framework/functions/_shared/audit.ts +91 -55
- package/.framework/functions/_shared/cil.ts +368 -127
- package/.framework/functions/_shared/db.ts +337 -68
- package/.framework/functions/_shared/funnel.ts +228 -0
- package/.framework/functions/_shared/geo.ts +105 -0
- package/.framework/functions/_shared/hash-api-key.ts +94 -17
- package/.framework/functions/_shared/index.ts +22 -224
- package/.framework/functions/_shared/kb.ts +832 -58
- package/.framework/functions/_shared/llm.ts +118 -18
- package/.framework/functions/_shared/logging.ts +317 -19
- package/.framework/functions/_shared/middleware.ts +701 -131
- package/.framework/functions/_shared/notify.ts +70 -0
- package/.framework/functions/_shared/outcomes.ts +34 -19
- package/.framework/functions/_shared/permissions.ts +501 -315
- package/.framework/functions/_shared/pipeline-runner.ts +704 -155
- package/.framework/functions/_shared/principal.ts +436 -263
- package/.framework/functions/_shared/rate-limit.ts +233 -0
- package/.framework/functions/_shared/resolve-credentials.ts +86 -19
- package/.framework/functions/_shared/resolve-ids.ts +203 -139
- package/.framework/functions/_shared/retry-deliveries.ts +51 -16
- package/.framework/functions/_shared/schema-utils.ts +602 -521
- package/.framework/functions/_shared/security.ts +345 -0
- package/.framework/functions/_shared/testing.ts +197 -172
- package/.framework/functions/_shared/tracing.ts +351 -21
- package/.framework/functions/_shared/trigger-engine.ts +425 -136
- package/.framework/functions/_shared/webhook-registration.ts +71 -70
- package/.framework/functions/_shared/webhook-registry.ts +126 -83
- package/.framework/functions/account-nodes.ts +30 -30
- package/.framework/functions/adapters/generic-webhook-inbound.ts +49 -16
- package/.framework/functions/adapters/generic-webhook-outbound.ts +17 -17
- package/.framework/functions/adapters/openai-outbound.ts +15 -16
- package/.framework/functions/admin-data.ts +596 -173
- package/.framework/functions/admin-funnel.ts +64 -60
- package/.framework/functions/admin-install-funnel.ts +84 -66
- package/.framework/functions/admin-signals.ts +69 -51
- package/.framework/functions/ai-agents.ts +431 -106
- package/.framework/functions/ai-interactions.ts +186 -112
- package/.framework/functions/api-keys.ts +107 -107
- package/.framework/functions/apps.ts +154 -136
- package/.framework/functions/auth.ts +83 -65
- package/.framework/functions/cil-agent-ingest.ts +189 -232
- package/.framework/functions/cil-agent.ts +433 -177
- package/.framework/functions/cil-callmap.ts +642 -430
- package/.framework/functions/cil-config.ts +139 -135
- package/.framework/functions/cil-context.ts +214 -127
- package/.framework/functions/cil-doc.ts +587 -458
- package/.framework/functions/cil-embeddings.ts +453 -364
- package/.framework/functions/cil-graph.ts +427 -142
- package/.framework/functions/cil-ingest.ts +1418 -1154
- package/.framework/functions/cil-pipeline.ts +276 -21
- package/.framework/functions/cil-project.ts +659 -132
- package/.framework/functions/cil-validate.ts +198 -164
- package/.framework/functions/debug-auth.ts +57 -55
- package/.framework/functions/deliveries.ts +80 -21
- package/.framework/functions/embeddings.ts +1023 -318
- package/.framework/functions/eval-harness.ts +146 -108
- package/.framework/functions/integration-outbound.ts +205 -75
- package/.framework/functions/integration-routes.ts +128 -23
- package/.framework/functions/integrations.ts +125 -100
- package/.framework/functions/invites.ts +1095 -460
- package/.framework/functions/item-progress.ts +158 -102
- package/.framework/functions/kb.ts +34 -10
- package/.framework/functions/logs.ts +133 -133
- package/.framework/functions/observability.ts +152 -122
- package/.framework/functions/pipeline-executions.ts +147 -132
- package/.framework/functions/pipelines.ts +134 -119
- package/.framework/functions/prompt-configs.ts +96 -80
- package/.framework/functions/roles.ts +112 -82
- package/.framework/functions/security.ts +450 -0
- package/.framework/functions/system-cron.ts +117 -80
- package/.framework/functions/system.ts +158 -98
- package/.framework/functions/tests.ts +29 -29
- package/.framework/functions/timers.ts +94 -95
- package/.framework/functions/trigger-scheduler.ts +10 -0
- package/.framework/functions/triggers.ts +179 -149
- package/.framework/functions/types.ts +137 -125
- package/.framework/mcp/index.ts +133 -0
- package/.framework/mcp/load-env.ts +60 -0
- package/.framework/mcp/shared/app-manifest.ts +275 -0
- package/.framework/mcp/shared/db.ts +477 -0
- package/.framework/mcp/shared/kb.ts +118 -0
- package/.framework/mcp/tools/architect.ts +431 -0
- package/.framework/mcp/tools/cil/context.ts +345 -0
- package/.framework/mcp/tools/cil/graph.ts +242 -0
- package/.framework/mcp/tools/cil/ingest.ts +1217 -0
- package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +693 -0
- package/.framework/mcp/tools/cil/pipeline.ts +767 -0
- package/.framework/mcp/tools/cil/project.ts +791 -0
- package/.framework/mcp/tools/cil/retrieval.ts +336 -0
- package/.framework/mcp/tools/cil/search-core.ts +454 -0
- package/.framework/mcp/tools/cil/search.ts +97 -0
- package/.framework/mcp/tools/cil/suggest.ts +186 -0
- package/.framework/mcp/tools/cil/validate.ts +1369 -0
- package/.framework/mcp/tools/kb/delete.ts +139 -0
- package/.framework/mcp/tools/kb/index.ts +52 -0
- package/.framework/mcp/tools/kb/ingest.ts +84 -0
- package/.framework/mcp/tools/kb/list-types.ts +102 -0
- package/.framework/mcp/tools/kb/publish.ts +100 -0
- package/.framework/mcp/tools/kb/reindex.ts +86 -0
- package/.framework/mcp/tools/kb/revoke.ts +85 -0
- package/.framework/mcp/tools/kb/search.ts +101 -0
- package/.framework/mcp/tools/kb/upsert.ts +125 -0
- package/.framework/mcp/tools/observability.ts +510 -0
- package/.framework/migrations/001_core_schema.sql +15 -0
- package/.framework/migrations/002_core_rls_and_functions.sql +15 -0
- package/.framework/migrations/003_core_system_data.sql +16 -1
- package/.framework/migrations/004_core_rpc_and_broker.sql +17 -1
- package/.framework/migrations/005_core_observability.sql +17 -1
- package/.framework/migrations/006_security_hardening_rls.sql +15 -0
- package/.framework/migrations/007_kb_article_list.sql +17 -1
- package/.framework/migrations/008_items_target.sql +15 -0
- package/.framework/migrations/009_roles_slug_unique.sql +15 -0
- package/.framework/migrations/010_auth_user_trigger.sql +15 -0
- package/.framework/migrations/011_spine_system_account.sql +16 -0
- package/.framework/migrations/012_service_role_grants.sql +16 -0
- package/.framework/migrations/013_get_account_apps_role_filter.sql +15 -0
- package/.framework/migrations/014_apps_required_roles.sql +16 -0
- package/.framework/migrations/015_types_kind_slug_unique.sql +15 -0
- package/.framework/migrations/016_cil_types.sql +281 -0
- package/.framework/migrations/017_cil_stable_ids.sql +37 -0
- package/.framework/migrations/018_runtime_tables.sql +129 -0
- package/.framework/migrations/019_agent_prompt_columns.sql +56 -0
- package/.framework/migrations/020_schema_reconciliation.sql +67 -0
- package/.framework/migrations/021_kb_embedding_lifecycle.sql +31 -0
- package/.framework/migrations/032_cil_status_migration.sql +73 -0
- package/.framework/migrations/033_cil_versioning.sql +73 -0
- package/.framework/migrations/034_match_embeddings_knn.sql +115 -0
- package/.framework/migrations/035_rate_limits.sql +55 -0
- package/.framework/migrations/036_demo_record_flags.sql +72 -0
- package/.framework/migrations/037_kb_types_canonical.sql +244 -0
- package/.framework/migrations/038_drop_demo_record_flags.sql +38 -0
- package/.framework/migrations/042_registration_hierarchy.sql +309 -0
- package/.framework/migrations/043_policy_actor_cleanup.sql +39 -0
- package/.framework/migrations/044_match_embeddings_filtered_knn.sql +151 -0
- package/.framework/migrations/045_security_critical_rls.sql +139 -0
- package/.framework/migrations/046_security_missing_rls.sql +79 -0
- package/.framework/migrations/047_people_updated_by.sql +27 -0
- package/.framework/migrations/048_watcher_last_seen_at.sql +24 -0
- package/.framework/migrations/049_security_blocklist.sql +148 -0
- package/.framework/migrations/050_notifications.sql +49 -0
- package/.framework/migrations/051_message_agent_fields.sql +74 -0
- package/.framework/migrations/052_registration_security.sql +421 -0
- package/.framework/migrations/053_builtin_action_seeds.sql +66 -0
- package/.framework/migrations/054_kb_ingest_lease.sql +157 -0
- package/.framework/migrations/055_type_id_drift_reconciliation.sql +33 -0
- package/.framework/migrations/056_slug_column_reconciliation.sql +38 -0
- package/.framework/migrations/057_pipelines_slug_identity.sql +14 -0
- package/.framework/migrations/058_account_data_field_declarations.sql +53 -0
- package/.framework/migrations/059_registration_placement_fixes.sql +269 -0
- package/.framework/migrations/060_account_person_record_permissions.sql +38 -0
- package/.framework/migrations/061_fk_dedup.sql +32 -0
- package/.framework/public/spine-logo.svg +10 -3
- package/.framework/scripts/app-install-cli.ts +190 -93
- package/.framework/scripts/assemble-frontend.sh +127 -44
- package/.framework/scripts/assemble-functions.sh +128 -30
- package/.framework/scripts/boundary-check.sh +6 -2
- package/.framework/scripts/build-manifest.sh +24 -68
- package/.framework/scripts/check-core-integrity.sh +15 -76
- package/.framework/scripts/checksum-manifest.mts +343 -0
- package/.framework/scripts/cil-migrate-markers.ts +403 -0
- package/.framework/scripts/fix-cil-markers.mjs +176 -0
- package/.framework/scripts/verify-integrity.sh +21 -57
- package/.framework/scripts/verify-manifest.mts +305 -0
- package/.framework/seeds/types/funnel_signal_use.json +74 -21
- package/.framework/seeds/types/spine_instance.json +73 -21
- package/.framework/src/App.tsx +118 -72
- package/.framework/src/apps/admin/index.tsx +855 -58
- package/.framework/src/components/AppWrapper.tsx +43 -45
- package/.framework/src/components/CustomAppLoader.tsx +160 -116
- package/.framework/src/components/admin/AdminListPage.tsx +46 -44
- package/.framework/src/components/admin/AdminSidebar.tsx +57 -47
- package/.framework/src/components/admin/AdminStatsCard.tsx +46 -44
- package/.framework/src/components/admin/PageInstructions.tsx +98 -2
- package/.framework/src/components/admin/SortableTableHeader.tsx +44 -45
- package/.framework/src/components/app-shell/GenericAppShell.tsx +64 -65
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +44 -47
- package/.framework/src/components/app-shell/GenericListPage.tsx +45 -49
- package/.framework/src/components/app-sidebar.tsx +42 -45
- package/.framework/src/components/auth/ProtectedRoute.tsx +48 -41
- package/.framework/src/components/auth/TurnstileWidget.tsx +27 -0
- package/.framework/src/components/kb/KBArticleEditor.tsx +231 -22
- package/.framework/src/components/kb/KBArticleList.tsx +251 -10
- package/.framework/src/components/layout/AppShell.tsx +51 -45
- package/.framework/src/components/layout/Header.tsx +42 -43
- package/.framework/src/components/layout/Layout.tsx +46 -46
- package/.framework/src/components/layout/Sidebar.tsx +196 -96
- package/.framework/src/components/runtime/DataDetailHeader.tsx +55 -30
- package/.framework/src/components/runtime/DataDetailPage.tsx +31 -269
- package/.framework/src/components/runtime/DataFilters.tsx +49 -74
- package/.framework/src/components/runtime/DataHeader.tsx +53 -32
- package/.framework/src/components/runtime/DataListPage.tsx +51 -155
- package/.framework/src/components/runtime/DataStats.tsx +66 -44
- package/.framework/src/components/runtime/DataTable.tsx +49 -149
- package/.framework/src/components/runtime/SchemaDetailForm.tsx +45 -75
- package/.framework/src/components/runtime/index.ts +119 -16
- package/.framework/src/components/search-form.tsx +46 -29
- package/.framework/src/components/shared/AgentView.tsx +65 -48
- package/.framework/src/components/shared/FieldRenderer.tsx +144 -50
- package/.framework/src/components/shared/InviteManager.tsx +207 -97
- package/.framework/src/components/shared/MemberList.tsx +97 -0
- package/.framework/src/components/shared/SchemaFields.tsx +107 -86
- package/.framework/src/components/ui/DataTable.tsx +80 -68
- package/.framework/src/components/ui/Form.tsx +157 -146
- package/.framework/src/components/ui/ItemCard.tsx +74 -67
- package/.framework/src/components/ui/ItemListView.tsx +73 -68
- package/.framework/src/components/ui/LoadingSpinner.tsx +43 -48
- package/.framework/src/components/ui/Modal.tsx +78 -68
- package/.framework/src/components/ui/RichTextEditor.tsx +89 -69
- package/.framework/src/components/ui/accordion.tsx +85 -86
- package/.framework/src/components/ui/alert-dialog.tsx +197 -184
- package/.framework/src/components/ui/alert.tsx +105 -94
- package/.framework/src/components/ui/aspect-ratio.tsx +57 -43
- package/.framework/src/components/ui/avatar.tsx +122 -107
- package/.framework/src/components/ui/badge.tsx +60 -55
- package/.framework/src/components/ui/breadcrumb.tsx +127 -128
- package/.framework/src/components/ui/button-group.tsx +86 -72
- package/.framework/src/components/ui/button.tsx +71 -50
- package/.framework/src/components/ui/calendar.tsx +72 -57
- package/.framework/src/components/ui/card.tsx +136 -129
- package/.framework/src/components/ui/carousel.tsx +226 -107
- package/.framework/src/components/ui/chart.tsx +252 -147
- package/.framework/src/components/ui/checkbox.tsx +43 -44
- package/.framework/src/components/ui/collapsible.tsx +86 -71
- package/.framework/src/components/ui/combobox.tsx +282 -249
- package/.framework/src/components/ui/command.tsx +158 -146
- package/.framework/src/components/ui/context-menu.tsx +239 -240
- package/.framework/src/components/ui/dialog.tsx +174 -157
- package/.framework/src/components/ui/direction.tsx +72 -56
- package/.framework/src/components/ui/drawer.tsx +168 -169
- package/.framework/src/components/ui/dropdown-menu.tsx +254 -223
- package/.framework/src/components/ui/empty.tsx +131 -120
- package/.framework/src/components/ui/field.tsx +205 -173
- package/.framework/src/components/ui/hover-card.tsx +71 -72
- package/.framework/src/components/ui/input-group.tsx +148 -133
- package/.framework/src/components/ui/input-otp.tsx +99 -86
- package/.framework/src/components/ui/input.tsx +43 -41
- package/.framework/src/components/ui/item.tsx +207 -185
- package/.framework/src/components/ui/kbd.tsx +57 -56
- package/.framework/src/components/ui/label.tsx +45 -41
- package/.framework/src/components/ui/menubar.tsx +268 -236
- package/.framework/src/components/ui/native-select.tsx +85 -86
- package/.framework/src/components/ui/navigation-menu.tsx +166 -156
- package/.framework/src/components/ui/pagination.tsx +131 -141
- package/.framework/src/components/ui/popover.tsx +125 -128
- package/.framework/src/components/ui/progress.tsx +47 -44
- package/.framework/src/components/ui/radio-group.tsx +60 -55
- package/.framework/src/components/ui/resizable.tsx +85 -72
- package/.framework/src/components/ui/scroll-area.tsx +57 -58
- package/.framework/src/components/ui/select.tsx +182 -169
- package/.framework/src/components/ui/separator.tsx +42 -43
- package/.framework/src/components/ui/sheet.tsx +170 -159
- package/.framework/src/components/ui/sidebar.tsx +395 -398
- package/.framework/src/components/ui/skeleton.tsx +42 -43
- package/.framework/src/components/ui/slider.tsx +61 -44
- package/.framework/src/components/ui/sonner.tsx +44 -44
- package/.framework/src/components/ui/spinner.tsx +47 -42
- package/.framework/src/components/ui/switch.tsx +56 -42
- package/.framework/src/components/ui/table-primitives.tsx +272 -132
- package/.framework/src/components/ui/table.tsx +140 -142
- package/.framework/src/components/ui/tabs.tsx +110 -93
- package/.framework/src/components/ui/textarea.tsx +43 -44
- package/.framework/src/components/ui/toggle-group.tsx +87 -71
- package/.framework/src/components/ui/toggle.tsx +56 -56
- package/.framework/src/components/ui/tooltip.tsx +99 -85
- package/.framework/src/contexts/AppContext.tsx +166 -114
- package/.framework/src/contexts/AuthContext.tsx +232 -137
- package/.framework/src/hooks/use-mobile.ts +43 -44
- package/.framework/src/hooks/useApi.ts +185 -172
- package/.framework/src/hooks/useAppPath.ts +31 -29
- package/.framework/src/hooks/useApps.ts +77 -42
- package/.framework/src/hooks/useEntityList.ts +66 -29
- package/.framework/src/hooks/useEntityRecord.ts +76 -32
- package/.framework/src/hooks/useForm.ts +62 -30
- package/.framework/src/hooks/useListSchema.ts +60 -43
- package/.framework/src/hooks/useSchemaRecord.ts +115 -95
- package/.framework/src/lib/api.ts +258 -134
- package/.framework/src/lib/registration.ts +181 -0
- package/.framework/src/lib/supabase.ts +118 -81
- package/.framework/src/lib/utils.ts +266 -266
- package/.framework/src/main.tsx +43 -44
- package/.framework/src/pages/DashboardPage.tsx +44 -43
- package/.framework/src/pages/NoAppPage.tsx +50 -0
- package/.framework/src/pages/NotFoundPage.tsx +32 -29
- package/.framework/src/pages/SetupPage.tsx +34 -33
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +69 -58
- package/.framework/src/pages/admin/AIAgentsPage.tsx +43 -40
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +63 -55
- package/.framework/src/pages/admin/APIKeysPage.tsx +44 -42
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +117 -94
- package/.framework/src/pages/admin/AppDetailPage.tsx +48 -42
- package/.framework/src/pages/admin/AppsPage.tsx +45 -42
- package/.framework/src/pages/admin/DeliveriesPage.tsx +49 -17
- package/.framework/src/pages/admin/DesignedPage.tsx +31 -30
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +58 -0
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +49 -0
- package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +31 -29
- package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +46 -29
- package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -29
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +68 -55
- package/.framework/src/pages/admin/IntegrationsPage.tsx +45 -42
- package/.framework/src/pages/admin/InvitesPage.tsx +60 -44
- package/.framework/src/pages/admin/LogsPage.tsx +87 -137
- package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +31 -29
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +150 -89
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +77 -104
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +44 -43
- package/.framework/src/pages/admin/PipelinesPage.tsx +59 -145
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +53 -42
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +47 -117
- package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +31 -44
- package/.framework/src/pages/admin/RoleDetailPage.tsx +49 -92
- package/.framework/src/pages/admin/RolesPage.tsx +47 -164
- package/.framework/src/pages/admin/SecurityPage.tsx +310 -0
- package/.framework/src/pages/admin/SelectTestPage.tsx +30 -32
- package/.framework/src/pages/admin/ShadcnTestPage.tsx +37 -44
- package/.framework/src/pages/admin/SimpleDashboard.tsx +32 -44
- package/.framework/src/pages/admin/TestRunDetailPage.tsx +97 -44
- package/.framework/src/pages/admin/TestingDashboard.tsx +126 -71
- package/.framework/src/pages/admin/TimerDetailPage.tsx +67 -55
- package/.framework/src/pages/admin/TimersPage.tsx +44 -42
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +47 -42
- package/.framework/src/pages/admin/TriggersPage.tsx +58 -57
- package/.framework/src/pages/admin/TypeDetailPage.tsx +91 -55
- package/.framework/src/pages/admin/TypesPage.tsx +45 -42
- package/.framework/src/pages/admin/cil/CILAgentPage.tsx +258 -128
- package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +127 -123
- package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +308 -235
- package/.framework/src/pages/admin/cil/CILFileListPage.tsx +159 -110
- package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +432 -0
- package/.framework/src/pages/admin/cil/CILGraphPage.tsx +200 -178
- package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +286 -239
- package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +211 -90
- package/.framework/src/pages/admin/cil/CILSearchPage.tsx +156 -143
- package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +379 -0
- package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +337 -0
- package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +219 -214
- package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
- package/.framework/src/pages/auth/AuthConfirmPage.tsx +135 -65
- package/.framework/src/pages/auth/ForgotPasswordPage.tsx +39 -31
- package/.framework/src/pages/auth/LoginPage.tsx +38 -32
- package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -28
- package/.framework/src/pages/auth/RegisterPage.tsx +429 -155
- package/.framework/src/pages/spine-framework/APIPage.tsx +33 -30
- package/.framework/src/pages/spine-framework/CLIPage.tsx +32 -30
- package/.framework/src/types/auth.ts +101 -93
- package/.framework/src/types/types.ts +283 -262
- package/.framework/templates/project.json +87 -0
- package/.framework/templates/spine.config.neutral.json +7 -0
- package/.framework/tests/api/api-surface.test.ts +381 -0
- package/.framework/tests/fixtures/seed.ts +427 -0
- package/.framework/tests/fixtures/teardown.ts +228 -0
- package/.framework/tests/reporter.ts +351 -0
- package/.framework/tests/setup.ts +135 -0
- package/.framework/tests/ui/ui-sweep.ts +278 -0
- package/.framework/tests/unit/admin-data-logger.test.ts +310 -0
- package/.framework/tests/unit/agent-runner.test.ts +2389 -0
- package/.framework/tests/unit/ai-interactions.test.ts +81 -0
- package/.framework/tests/unit/app-manifest.test.ts +359 -0
- package/.framework/tests/unit/assembly-scripts.test.ts +172 -0
- package/.framework/tests/unit/cil-determinism.test.ts +85 -0
- package/.framework/tests/unit/core-isolation.test.ts +153 -0
- package/.framework/tests/unit/funnel-signals.test.ts +135 -0
- package/.framework/tests/unit/install-app.test.ts +1759 -0
- package/.framework/tests/unit/invites-security.test.ts +572 -0
- package/.framework/tests/unit/kb-ingest-lease.test.ts +287 -0
- package/.framework/tests/unit/kb-search.test.ts +119 -0
- package/.framework/tests/unit/migrate.test.ts +509 -0
- package/.framework/tests/unit/notify.test.ts +136 -0
- package/.framework/tests/unit/permissions.test.ts +269 -0
- package/.framework/tests/unit/pipeline-runner.test.ts +755 -0
- package/.framework/tests/unit/principal-cache.test.ts +138 -0
- package/.framework/tests/unit/retrieval-golden.test.ts +520 -0
- package/.framework/tests/unit/runtime-isolation.test.ts +642 -0
- package/.framework/tests/unit/schema-utils.test.ts +225 -0
- package/.framework/tests/unit/trigger-engine.test.ts +513 -0
- package/.framework/tests/unit/validate-app.test.ts +527 -0
- package/README.md +34 -3
- package/bin/spine-framework.cjs +1 -0
- package/config/tailwind.config.cjs +7 -0
- package/config/tsconfig.functions.json +42 -0
- package/config/tsconfig.json +37 -9
- package/config/vite.config.ts +31 -2
- package/netlify.toml +31 -3
- package/package.json +46 -29
- package/scripts/assemble-frontend.sh +127 -44
- package/scripts/assemble-functions.sh +127 -29
- package/scripts/boundary-check.sh +6 -2
- package/scripts/build-manifest.sh +24 -68
- package/scripts/checksum-manifest.mts +345 -0
- package/scripts/dev-clean.sh +86 -0
- package/scripts/dev-netlify.sh +49 -0
- package/scripts/netlify-dev-wrapper.sh +2 -0
- package/scripts/verify-install.ts +358 -0
- package/scripts/verify-integrity.sh +21 -57
- package/scripts/verify-manifest.mts +305 -0
- package/templates/project/gitignore +45 -0
- package/{config → templates/project}/package-lock.json +22335 -9366
- package/vitest.config.ts +46 -0
- package/.framework/public/spine.config.json +0 -91
- package/config/deno.lock +0 -108
- package/config/tsconfig.node.tsbuildinfo +0 -1
- package/config/tsconfig.tsbuildinfo +0 -1
- package/config/vite.config.d.ts +0 -2
- package/custom/.devin/AGENT.md +0 -357
- package/custom/.devin/skills/install-app/SKILL.md +0 -39
- package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +0 -39
- package/custom/.devin/skills/spine-workbench-idea/SKILL.md +0 -36
- package/custom/.devin/skills/spine-workbench-implement/SKILL.md +0 -46
- package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +0 -28
- package/custom/.devin/skills/spine-workbench-plan/SKILL.md +0 -38
- package/custom/.devin/workflows/app-install.yaml +0 -83
- package/custom/README.md +0 -79
- package/docs/README.md +0 -15
- package/scripts/app-install-cli.ts +0 -286
- package/scripts/check-core-integrity.sh +0 -82
- package/scripts/ingest-chunks.cjs +0 -202
- package/scripts/kb-chunk-parser.cjs +0 -312
- package/scripts/kb-chunk-parser.ts +0 -330
- package/scripts/kb-ingest-docs.ts +0 -203
- package/scripts/load-test-app-install.ts +0 -484
- package/scripts/test-login.js +0 -33
- package/spine.config.json +0 -91
|
@@ -1,22 +1,17 @@
|
|
|
1
|
+
// CIL[BEGIN stable_id="~/.framework/src/pages/admin/RolesPage.tsx:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/src/pages/admin/RolesPage.tsx"]
|
|
2
|
+
// CIL[DOC-MACRO purpose="Admin roles management page module: defines the Role interface and the RolesPage React component for listing, searching, filtering, and sorting roles."]
|
|
3
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Provide the top-level TypeScript module exports and role model shape consumed by the admin roles UI."]
|
|
4
|
+
// CIL[DOC-CONTEXT module="admin" layer="pages" related_files=".framework/src/components/admin/AdminListPage.tsx,.framework/src/components/admin/SortableTableHeader.tsx,.framework/src/components/ui/LoadingSpinner.tsx,.framework/src/components/ui/button.tsx,.framework/src/hooks/useApi.ts,.framework/src/lib/api.ts,.framework/src/lib/utils.ts"]
|
|
5
|
+
// CIL[DOC-CONTRACT inputs="module imports and role API response data" outputs="Role interface and RolesPage React component" errors="Network or API errors are surfaced by useApi hook" side_effects="none at module scope"]
|
|
6
|
+
// CIL[DOC-MICRO logic_summary="Imports React, hooks, shared components, and utilities; exports Role type and RolesPage component for rendering the admin roles list." notable_decisions="[]"]
|
|
7
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
8
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
9
|
+
// CIL[DOC-AUDIENCE audience="admin-frontend"]
|
|
10
|
+
|
|
1
11
|
|
|
2
12
|
|
|
3
13
|
|
|
4
14
|
|
|
5
|
-
// CIL[BEGIN chunk_id="rolespage_module_header_1783665503" type="module_header" identity="module_header" parent="none"]
|
|
6
|
-
// CIL[DOC-MACRO purpose="Import dependencies and define Role interface for admin roles management"]
|
|
7
|
-
// CIL[DOC-RESPONSIBILITY responsibility="Provide React imports, UI components, and Role type definition"]
|
|
8
|
-
// CIL[DOC-CONTEXT module="RolesPage" layer="frontend-page" related_files="[\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"\\\"]"]
|
|
9
|
-
// CIL[DOC-CONTRACT inputs="none" outputs="React components and Role interface" errors="none" side_effects="none"]
|
|
10
|
-
// CIL[DOC-MICRO logic_summary="Imports React hooks, UI components, and defines Role data structure" notable_decisions="[]"]
|
|
11
|
-
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
12
|
-
// CIL[DOC-STABILITY stability="stable"]
|
|
13
|
-
// CIL[DOC-AUDIENCE audience="developer"]
|
|
14
|
-
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, \."]
|
|
15
|
-
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
16
|
-
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
17
|
-
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
18
|
-
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
19
|
-
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
20
15
|
import React, { useState } from 'react'
|
|
21
16
|
import { Plus, ShieldCheck, CheckCircle, XCircle, Settings, FileText, Cog } from 'lucide-react';
|
|
22
17
|
import { LoadingSpinner } from '../../components/ui/LoadingSpinner'
|
|
@@ -27,6 +22,22 @@ import { formatDateTime } from '../../lib/utils'
|
|
|
27
22
|
import { useApi } from '../../hooks/useApi'
|
|
28
23
|
import { apiFetch } from '../../lib/api'
|
|
29
24
|
|
|
25
|
+
|
|
26
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Admin roles management page module: defines the Role interface and the RolesPage React component for listing, searching, filtering, and sorting roles.'; No verified external callers; imports: .framework/src/components/ui/LoadingSpinner.tsx,.framework/src/components/ui/button.tsx,.framework/src/components/admin/AdminListPage.tsx"]
|
|
27
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
28
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
29
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
30
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
31
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
32
|
+
// CIL[END stable_id="~/.framework/src/pages/admin/RolesPage.tsx:module_header:module_header"]
|
|
33
|
+
// CIL[BEGIN stable_id="~/.framework/src/pages/admin/RolesPage.tsx:Role:interface" type="interface" identity="Role" parent="~/.framework/src/pages/admin/RolesPage.tsx"]
|
|
34
|
+
// CIL[DOC-MACRO purpose="Define the shape of a role record for use in the admin roles list and related operations."]
|
|
35
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Declare the TypeScript interface for role objects returned by the roles API."]
|
|
36
|
+
// CIL[DOC-CONTEXT module="admin" layer="types" related_files=".framework/src/components/admin/AdminListPage.tsx,.framework/src/components/admin/SortableTableHeader.tsx,.framework/src/components/ui/LoadingSpinner.tsx,.framework/src/components/ui/button.tsx,.framework/src/hooks/useApi.ts,.framework/src/lib/api.ts,.framework/src/lib/utils.ts,.framework/src/pages/admin/RolesPage.tsx"]
|
|
37
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="Role interface type definition" errors="none" side_effects="none"]
|
|
38
|
+
// CIL[DOC-MICRO logic_summary="Typed interface with required and optional fields representing a role, its permissions, app association, and audit timestamps." notable_decisions="[]"]
|
|
39
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
40
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
30
41
|
interface Role {
|
|
31
42
|
id: string
|
|
32
43
|
name: string
|
|
@@ -40,21 +51,22 @@ interface Role {
|
|
|
40
51
|
created_at: string
|
|
41
52
|
updated_at: string
|
|
42
53
|
}
|
|
43
|
-
// CIL[
|
|
44
|
-
// CIL[
|
|
45
|
-
// CIL[DOC-
|
|
46
|
-
// CIL[DOC-
|
|
47
|
-
// CIL[DOC-CONTEXT module="RolesPage" layer="frontend-page" related_files="[\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/portal/pages/CoursesPage.tsx\"]"]
|
|
48
|
-
// CIL[DOC-CONTRACT inputs="none" outputs="AdminListPage JSX" errors="none" side_effects="none"]
|
|
49
|
-
// CIL[DOC-MICRO logic_summary="Renders AdminListPage with roles data, empty states, and table" notable_decisions="[]"]
|
|
50
|
-
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
51
|
-
// CIL[DOC-STABILITY stability="stable"]
|
|
52
|
-
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: custom/apps/cortex/pages/operations/OperationsDashboard.tsx, custom/apps/portal/pages/ContentPage.tsx, custom/apps/portal/pages/CoursesPage.tsx."]
|
|
53
|
-
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
54
|
-
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
55
|
-
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
54
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Define the shape of a role record for use in the admin roles list and related operations.'; No verified external callers; imports: .framework/src/components/ui/LoadingSpinner.tsx,.framework/src/components/ui/button.tsx,.framework/src/components/admin/AdminListPage.tsx"]
|
|
55
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
56
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
57
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
56
58
|
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
57
59
|
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
60
|
+
// CIL[END stable_id="~/.framework/src/pages/admin/RolesPage.tsx:Role:interface"]
|
|
61
|
+
|
|
62
|
+
// CIL[BEGIN stable_id="~/.framework/src/pages/admin/RolesPage.tsx:RolesPage:component" type="component" identity="RolesPage" parent="~/.framework/src/pages/admin/RolesPage.tsx"]
|
|
63
|
+
// CIL[DOC-MACRO purpose="Render the admin roles list page with search, filters, sorting, stats, and navigation to role detail."]
|
|
64
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Fetch and display roles; handle search, status/type filters, column sorting, row navigation, and summary statistics."]
|
|
65
|
+
// CIL[DOC-CONTEXT module="admin" layer="pages" related_files=".framework/src/components/admin/AdminListPage.tsx,.framework/src/components/admin/SortableTableHeader.tsx,.framework/src/components/ui/LoadingSpinner.tsx,.framework/src/components/ui/button.tsx,.framework/src/hooks/useApi.ts,.framework/src/lib/api.ts,.framework/src/lib/utils.ts,.framework/src/pages/admin/RolesPage.tsx"]
|
|
66
|
+
// CIL[DOC-CONTRACT inputs="none (internal state and useApi fetch)" outputs="JSX admin list page for roles" errors="API fetch error passed to AdminListPage; boolean sort fallback for permissions" side_effects="Navigates to /spine-framework/admin/configs/roles/:id on row click; uses window.location.href"]
|
|
67
|
+
// CIL[DOC-MICRO logic_summary="Uses useApi to load roles, applies search/status/type filters, sorts by selected column, computes stats cards, and renders an AdminListPage with a sortable table and clickable rows." notable_decisions="[]"]
|
|
68
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
69
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
58
70
|
export function RolesPage() {
|
|
59
71
|
const [searchTerm, setSearchTerm] = useState('')
|
|
60
72
|
const [selectedStatus, setSelectedStatus] = useState('all')
|
|
@@ -87,20 +99,6 @@ export function RolesPage() {
|
|
|
87
99
|
})
|
|
88
100
|
|
|
89
101
|
|
|
90
|
-
// CIL[BEGIN chunk_id="rolespage_getstatusbadge_1783665503" type="function" identity="getStatusBadge" parent="rolespage_rolespage_1783665503"]
|
|
91
|
-
// CIL[DOC-MACRO purpose="Generate status badge component based on role properties"]
|
|
92
|
-
// CIL[DOC-RESPONSIBILITY responsibility="Handle getStatusBadge"]
|
|
93
|
-
// CIL[DOC-CONTEXT module="RolesPage" layer="frontend-page" related_files="[\".framework/src/components/ui/dialog.tsx\",\".framework/src/components/layout/Sidebar.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/portal/pages/CoursesPage.tsx\",\"\\\"]"]
|
|
94
|
-
// CIL[DOC-CONTRACT inputs="none" outputs="JSX badge element" errors="none" side_effects="none"]
|
|
95
|
-
// CIL[DOC-MICRO logic_summary="Checks is_active and is_system flags to determine badge styling" notable_decisions="[]"]
|
|
96
|
-
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
97
|
-
// CIL[DOC-STABILITY stability="stable"]
|
|
98
|
-
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 4. Direct calls: 0. Related files: .framework/src/components/ui/dialog.tsx, .framework/src/components/layout/Sidebar.tsx, custom/apps/portal/pages/ContentPage.tsx, custom/apps/portal/pages/CoursesPage.tsx, \."]
|
|
99
|
-
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 4. Total affected: 4."]
|
|
100
|
-
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
101
|
-
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
102
|
-
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
103
|
-
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
104
102
|
const getStatusBadge = (role: Role) => {
|
|
105
103
|
if (!role.is_active) {
|
|
106
104
|
return <span className="inline-flex px-2 py-0.5 text-xs font-medium rounded-md bg-slate-100 text-slate-600">Inactive</span>
|
|
@@ -110,22 +108,7 @@ export function RolesPage() {
|
|
|
110
108
|
}
|
|
111
109
|
return <span className="inline-flex px-2 py-0.5 text-xs font-medium rounded-md bg-blue-100 text-blue-700">Custom</span>
|
|
112
110
|
}
|
|
113
|
-
// CIL[END chunk_id="rolespage_getstatusbadge_1783665503"]
|
|
114
111
|
|
|
115
|
-
// CIL[BEGIN chunk_id="rolespage_handlesort_1783665503" type="function" identity="handleSort" parent="rolespage_rolespage_1783665503"]
|
|
116
|
-
// CIL[DOC-MACRO purpose="Handle table column sorting with direction toggle"]
|
|
117
|
-
// CIL[DOC-RESPONSIBILITY responsibility="Handle handleSort"]
|
|
118
|
-
// CIL[DOC-CONTEXT module="RolesPage" layer="frontend-page" related_files="[\".framework/functions/_shared/schema-utils.ts\",\".framework/src/components/ui/dialog.tsx\",\".framework/functions/_shared/principal.ts\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"\\\"]"]
|
|
119
|
-
// CIL[DOC-CONTRACT inputs="none" outputs="state updates" errors="none" side_effects="Updates component state"]
|
|
120
|
-
// CIL[DOC-MICRO logic_summary="Toggles direction if same key, sets new key and asc direction otherwise" notable_decisions="[]"]
|
|
121
|
-
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
122
|
-
// CIL[DOC-STABILITY stability="stable"]
|
|
123
|
-
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: .framework/functions/_shared/schema-utils.ts, .framework/src/components/ui/dialog.tsx, .framework/functions/_shared/principal.ts, .framework/src/pages/admin/TypeDetailPage.tsx, \."]
|
|
124
|
-
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
125
|
-
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
126
|
-
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
127
|
-
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
128
|
-
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
129
112
|
const handleSort = (key: string) => {
|
|
130
113
|
if (sortKey === key) {
|
|
131
114
|
setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc')
|
|
@@ -134,41 +117,11 @@ export function RolesPage() {
|
|
|
134
117
|
setSortDirection('asc')
|
|
135
118
|
}
|
|
136
119
|
}
|
|
137
|
-
// CIL[END chunk_id="rolespage_handlesort_1783665503"]
|
|
138
120
|
|
|
139
|
-
// CIL[BEGIN chunk_id="rolespage_handlerowclick_1783665503" type="function" identity="handleRowClick" parent="rolespage_rolespage_1783665503"]
|
|
140
|
-
// CIL[DOC-MACRO purpose="Navigate to role detail page when table row is clicked"]
|
|
141
|
-
// CIL[DOC-RESPONSIBILITY responsibility="Handle handleRowClick"]
|
|
142
|
-
// CIL[DOC-CONTEXT module="handleRowClick" layer="unknown" related_files="[]"]
|
|
143
|
-
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
144
|
-
// CIL[DOC-MICRO logic_summary="Implements handleRowClick" notable_decisions="[]"]
|
|
145
|
-
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
146
|
-
// CIL[DOC-STABILITY stability="stable"]
|
|
147
|
-
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
148
|
-
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
149
|
-
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
150
|
-
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
151
|
-
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
152
|
-
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
153
121
|
const handleRowClick = (role: Role) => {
|
|
154
122
|
window.location.href = `/spine-framework/admin/configs/roles/${role.id}`
|
|
155
123
|
}
|
|
156
|
-
// CIL[END chunk_id="rolespage_handlerowclick_1783665503"]
|
|
157
124
|
|
|
158
|
-
// CIL[BEGIN chunk_id="rolespage_sortedroles_1783665503" type="variable" identity="sortedRoles" parent="rolespage_rolespage_1783665503"]
|
|
159
|
-
// CIL[DOC-MACRO purpose="Sort filtered roles based on current sort key and direction"]
|
|
160
|
-
// CIL[DOC-RESPONSIBILITY responsibility="Handle sortedRoles"]
|
|
161
|
-
// CIL[DOC-CONTEXT module="RolesPage" layer="frontend-page" related_files="[\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/portal/pages/CoursesPage.tsx\",\".framework/src/components/ui/dialog.tsx\",\"\\\"]"]
|
|
162
|
-
// CIL[DOC-CONTRACT inputs="none" outputs="sorted array" errors="none" side_effects="none"]
|
|
163
|
-
// CIL[DOC-MICRO logic_summary="Handles string and boolean sorting with localeCompare" notable_decisions="[]"]
|
|
164
|
-
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
165
|
-
// CIL[DOC-STABILITY stability="stable"]
|
|
166
|
-
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: custom/apps/portal/pages/ContentPage.tsx, custom/apps/portal/pages/CoursesPage.tsx, .framework/src/components/ui/dialog.tsx, \."]
|
|
167
|
-
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
168
|
-
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
169
|
-
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
170
|
-
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
171
|
-
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
172
125
|
const sortedRoles = [...(filteredRoles || [])].sort((a, b) => {
|
|
173
126
|
let aValue: any = a[sortKey as keyof Role]
|
|
174
127
|
let bValue: any = b[sortKey as keyof Role]
|
|
@@ -185,22 +138,7 @@ export function RolesPage() {
|
|
|
185
138
|
|
|
186
139
|
return 0
|
|
187
140
|
})
|
|
188
|
-
// CIL[END chunk_id="rolespage_sortedroles_1783665503"]
|
|
189
141
|
|
|
190
|
-
// CIL[BEGIN chunk_id="rolespage_statcards_1783665503" type="variable" identity="statsCards" parent="rolespage_rolespage_1783665503"]
|
|
191
|
-
// CIL[DOC-MACRO purpose="Generate statistics cards for roles dashboard"]
|
|
192
|
-
// CIL[DOC-RESPONSIBILITY responsibility="Handle statsCards"]
|
|
193
|
-
// CIL[DOC-CONTEXT module="RolesPage" layer="frontend-page" related_files="[\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"\\\"]"]
|
|
194
|
-
// CIL[DOC-CONTRACT inputs="none" outputs="stats cards array" errors="none" side_effects="none"]
|
|
195
|
-
// CIL[DOC-MICRO logic_summary="Calculates total, active, system, and custom role counts" notable_decisions="[]"]
|
|
196
|
-
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
197
|
-
// CIL[DOC-STABILITY stability="stable"]
|
|
198
|
-
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, \."]
|
|
199
|
-
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
200
|
-
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
201
|
-
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
202
|
-
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
203
|
-
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
204
142
|
const statsCards = [
|
|
205
143
|
{
|
|
206
144
|
title: 'Total Roles',
|
|
@@ -227,64 +165,19 @@ export function RolesPage() {
|
|
|
227
165
|
iconColor: 'text-orange-500'
|
|
228
166
|
}
|
|
229
167
|
]
|
|
230
|
-
// CIL[END chunk_id="rolespage_statcards_1783665503"]
|
|
231
168
|
|
|
232
|
-
// CIL[BEGIN chunk_id="rolespage_statusoptions_1783665503" type="variable" identity="statusOptions" parent="rolespage_rolespage_1783665503"]
|
|
233
|
-
// CIL[DOC-MACRO purpose="Define filter options for role status dropdown"]
|
|
234
|
-
// CIL[DOC-RESPONSIBILITY responsibility="Handle statusOptions"]
|
|
235
|
-
// CIL[DOC-CONTEXT module="RolesPage" layer="frontend-page" related_files="[\".framework/functions/_shared/principal.ts\",\".framework/src/pages/admin/IntegrationsPage.tsx\",\"\\\"]"]
|
|
236
|
-
// CIL[DOC-CONTRACT inputs="none" outputs="status options array" errors="none" side_effects="none"]
|
|
237
|
-
// CIL[DOC-MICRO logic_summary="Static array of status filter options" notable_decisions="[]"]
|
|
238
|
-
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
239
|
-
// CIL[DOC-STABILITY stability="stable"]
|
|
240
|
-
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: .framework/functions/_shared/principal.ts, .framework/src/pages/admin/IntegrationsPage.tsx, \."]
|
|
241
|
-
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
242
|
-
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
243
|
-
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
244
|
-
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
245
|
-
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
246
169
|
const statusOptions = [
|
|
247
170
|
{ value: 'all', label: 'All Status' },
|
|
248
171
|
{ value: 'active', label: 'Active' },
|
|
249
172
|
{ value: 'inactive', label: 'Inactive' }
|
|
250
173
|
]
|
|
251
|
-
// CIL[END chunk_id="rolespage_statusoptions_1783665503"]
|
|
252
174
|
|
|
253
|
-
// CIL[BEGIN chunk_id="rolespage_typeoptions_1783665503" type="variable" identity="typeOptions" parent="rolespage_rolespage_1783665503"]
|
|
254
|
-
// CIL[DOC-MACRO purpose="Define filter options for role type dropdown"]
|
|
255
|
-
// CIL[DOC-RESPONSIBILITY responsibility="Handle typeOptions"]
|
|
256
|
-
// CIL[DOC-CONTEXT module="RolesPage" layer="frontend-page" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/functions/_shared/schema-utils.ts\",\"\\\"]"]
|
|
257
|
-
// CIL[DOC-CONTRACT inputs="none" outputs="type options array" errors="none" side_effects="none"]
|
|
258
|
-
// CIL[DOC-MICRO logic_summary="Static array of role type filter options" notable_decisions="[]"]
|
|
259
|
-
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
260
|
-
// CIL[DOC-STABILITY stability="stable"]
|
|
261
|
-
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, .framework/functions/_shared/schema-utils.ts, \."]
|
|
262
|
-
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
263
|
-
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
264
|
-
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
265
|
-
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
266
|
-
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
267
175
|
const typeOptions = [
|
|
268
176
|
{ value: 'all', label: 'All Types' },
|
|
269
177
|
{ value: 'system', label: 'System' },
|
|
270
178
|
{ value: 'custom', label: 'Custom' }
|
|
271
179
|
]
|
|
272
|
-
// CIL[END chunk_id="rolespage_typeoptions_1783665503"]
|
|
273
180
|
|
|
274
|
-
// CIL[BEGIN chunk_id="rolespage_filters_1783665503" type="variable" identity="filters" parent="rolespage_rolespage_1783665503"]
|
|
275
|
-
// CIL[DOC-MACRO purpose="Combine status and type filters for AdminListPage component"]
|
|
276
|
-
// CIL[DOC-RESPONSIBILITY responsibility="Handle filters"]
|
|
277
|
-
// CIL[DOC-CONTEXT module="RolesPage" layer="frontend-page" related_files="[\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/components/layout/Sidebar.tsx\",\"\\\"]"]
|
|
278
|
-
// CIL[DOC-CONTRACT inputs="none" outputs="filters array" errors="none" side_effects="none"]
|
|
279
|
-
// CIL[DOC-MICRO logic_summary="Maps filter options to AdminListPage filter format" notable_decisions="[]"]
|
|
280
|
-
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
281
|
-
// CIL[DOC-STABILITY stability="stable"]
|
|
282
|
-
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/components/layout/Sidebar.tsx, \."]
|
|
283
|
-
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
284
|
-
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
285
|
-
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
286
|
-
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
287
|
-
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
288
181
|
const filters = [
|
|
289
182
|
{
|
|
290
183
|
label: 'Status',
|
|
@@ -299,22 +192,7 @@ export function RolesPage() {
|
|
|
299
192
|
onChange: setSelectedType
|
|
300
193
|
}
|
|
301
194
|
]
|
|
302
|
-
// CIL[END chunk_id="rolespage_filters_1783665503"]
|
|
303
195
|
|
|
304
|
-
// CIL[BEGIN chunk_id="rolespage_return_1783665503" type="jsx" identity="return" parent="rolespage_rolespage_1783665503"]
|
|
305
|
-
// CIL[DOC-MACRO purpose="Render the complete roles management page UI"]
|
|
306
|
-
// CIL[DOC-RESPONSIBILITY responsibility="Handle return"]
|
|
307
|
-
// CIL[DOC-CONTEXT module="return" layer="unknown" related_files="[]"]
|
|
308
|
-
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
309
|
-
// CIL[DOC-MICRO logic_summary="Implements return" notable_decisions="[]"]
|
|
310
|
-
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
311
|
-
// CIL[DOC-STABILITY stability="stable"]
|
|
312
|
-
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
313
|
-
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
314
|
-
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
315
|
-
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
316
|
-
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
317
|
-
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
318
196
|
return (
|
|
319
197
|
<AdminListPage
|
|
320
198
|
title="Roles"
|
|
@@ -440,6 +318,11 @@ export function RolesPage() {
|
|
|
440
318
|
)}
|
|
441
319
|
</AdminListPage>
|
|
442
320
|
)
|
|
443
|
-
// CIL[END chunk_id="rolespage_return_1783665503"]
|
|
444
321
|
}
|
|
445
|
-
// CIL[
|
|
322
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Render the admin roles list page with search, filters, sorting, stats, and navigation to role detail.'; No verified external callers; imports: .framework/src/components/ui/LoadingSpinner.tsx,.framework/src/components/ui/button.tsx,.framework/src/components/admin/AdminListPage.tsx"]
|
|
323
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
324
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
325
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
326
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
327
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
328
|
+
// CIL[END stable_id="~/.framework/src/pages/admin/RolesPage.tsx:RolesPage:component"]
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
// CIL[BEGIN stable_id="~/.framework/src/pages/admin/SecurityPage.tsx:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/src/pages/admin/SecurityPage.tsx"]
|
|
2
|
+
// CIL[DOC-MACRO purpose="System-admin security console: flagged actors report + blacklist management"]
|
|
3
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Render the flagged-accounts report from /api/security, let admins select rows via per-row checkboxes, and apply or lift three-layer bans; also list/revoke blocklist entries"]
|
|
4
|
+
// CIL[DOC-CONTEXT module="admin" layer="presentation" related_files=".framework/functions/security.ts,.framework/src/lib/api.ts,.framework/src/components/ui/checkbox.tsx,.framework/src/components/ui/table.tsx"]
|
|
5
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="Security admin page JSX" errors="API errors surfaced inline" side_effects="GET flagged_accounts + blocklist; POST ban_user/unban_user/blocklist_revoke via apiFetch"]
|
|
6
|
+
// CIL[DOC-MICRO logic_summary="Load report + blocklist; checkbox Set<string> keyed by row key (person_id or acct:account_id); Add to Blacklist issues one ban_user per selected row and reports per-layer results; refresh after actions" notable_decisions="ban_user accepts person_id OR email OR ip so anonymous-signal rows (no person) still ban by account/ip; all calls go through apiFetch — service role never leaves the server"]
|
|
7
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
8
|
+
// CIL[DOC-AUDIENCE audience="internal"]
|
|
9
|
+
|
|
10
|
+
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
11
|
+
import { apiFetch } from '../../lib/api'
|
|
12
|
+
import { Button } from '../../components/ui/button'
|
|
13
|
+
import { Checkbox } from '../../components/ui/checkbox'
|
|
14
|
+
import { Badge } from '../../components/ui/badge'
|
|
15
|
+
import { LoadingSpinner } from '../../components/ui/LoadingSpinner'
|
|
16
|
+
import {
|
|
17
|
+
Table, TableBody, TableCell, TableHead, TableHeader, TableRow,
|
|
18
|
+
} from '../../components/ui/table'
|
|
19
|
+
|
|
20
|
+
type FlaggedRow = {
|
|
21
|
+
person_id: string | null
|
|
22
|
+
email: string | null
|
|
23
|
+
account_id: string | null
|
|
24
|
+
account_name: string | null
|
|
25
|
+
last_ip: string | null
|
|
26
|
+
last_country: string | null
|
|
27
|
+
person_status: string | null
|
|
28
|
+
signals: number
|
|
29
|
+
kinds: string[]
|
|
30
|
+
patterns: string[]
|
|
31
|
+
last_seen_at: string | null
|
|
32
|
+
banned: boolean
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type BlocklistRow = {
|
|
36
|
+
id: string
|
|
37
|
+
type: string
|
|
38
|
+
value: string
|
|
39
|
+
severity: string
|
|
40
|
+
reason: string | null
|
|
41
|
+
expires_at: string | null
|
|
42
|
+
revoked_at: string | null
|
|
43
|
+
created_at: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function rowKey(r: FlaggedRow): string {
|
|
47
|
+
return r.person_id || `acct:${r.account_id || 'unknown'}`
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function callSecurity(action: string, body?: any) {
|
|
51
|
+
const res = await apiFetch(`/api/security?action=${action}`, {
|
|
52
|
+
method: 'POST',
|
|
53
|
+
headers: { 'Content-Type': 'application/json' },
|
|
54
|
+
body: JSON.stringify(body || {}),
|
|
55
|
+
})
|
|
56
|
+
const data = await res.json().catch(() => ({}))
|
|
57
|
+
if (!res.ok) throw new Error(data?.error || data?.message || `${action} failed (${res.status})`)
|
|
58
|
+
return data
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function SecurityPage() {
|
|
62
|
+
const [rows, setRows] = useState<FlaggedRow[]>([])
|
|
63
|
+
const [blocklist, setBlocklist] = useState<BlocklistRow[]>([])
|
|
64
|
+
const [loading, setLoading] = useState(true)
|
|
65
|
+
const [error, setError] = useState<string | null>(null)
|
|
66
|
+
const [selected, setSelected] = useState<Set<string>>(new Set())
|
|
67
|
+
const [actionBusy, setActionBusy] = useState(false)
|
|
68
|
+
const [actionResult, setActionResult] = useState<string | null>(null)
|
|
69
|
+
|
|
70
|
+
const load = useCallback(async () => {
|
|
71
|
+
setLoading(true)
|
|
72
|
+
setError(null)
|
|
73
|
+
try {
|
|
74
|
+
const [flagged, bl] = await Promise.all([
|
|
75
|
+
callSecurity('flagged_accounts'),
|
|
76
|
+
callSecurity('blocklist'),
|
|
77
|
+
])
|
|
78
|
+
setRows(flagged?.rows || [])
|
|
79
|
+
setBlocklist(bl?.rows || [])
|
|
80
|
+
} catch (e: any) {
|
|
81
|
+
setError(e?.message || 'Failed to load security data')
|
|
82
|
+
} finally {
|
|
83
|
+
setLoading(false)
|
|
84
|
+
}
|
|
85
|
+
}, [])
|
|
86
|
+
|
|
87
|
+
useEffect(() => { load() }, [load])
|
|
88
|
+
|
|
89
|
+
const rowByKey = useMemo(() => new Map(rows.map(r => [rowKey(r), r])), [rows])
|
|
90
|
+
const selectedRows = useMemo(
|
|
91
|
+
() => [...selected].map(k => rowByKey.get(k)).filter(Boolean) as FlaggedRow[],
|
|
92
|
+
[selected, rowByKey]
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
const toggle = (key: string, checked: boolean) => {
|
|
96
|
+
setSelected(prev => {
|
|
97
|
+
const next = new Set(prev)
|
|
98
|
+
if (checked) next.add(key); else next.delete(key)
|
|
99
|
+
return next
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const toggleAll = (checked: boolean) => {
|
|
104
|
+
setSelected(checked ? new Set(rows.map(rowKey)) : new Set())
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const banSelected = async () => {
|
|
108
|
+
if (!selectedRows.length) return
|
|
109
|
+
setActionBusy(true)
|
|
110
|
+
setActionResult(null)
|
|
111
|
+
const results: string[] = []
|
|
112
|
+
for (const r of selectedRows) {
|
|
113
|
+
try {
|
|
114
|
+
const res = await callSecurity('ban_user', {
|
|
115
|
+
person_id: r.person_id || undefined,
|
|
116
|
+
email: r.email || undefined,
|
|
117
|
+
ip: r.last_ip || undefined,
|
|
118
|
+
reason: `Security admin action — signals: ${r.kinds.join(', ')}`,
|
|
119
|
+
})
|
|
120
|
+
results.push(`${r.email || r.account_name || rowKey(r)}: banned (auth:${res.layers?.auth} edge:${res.layers?.edge})`)
|
|
121
|
+
} catch (e: any) {
|
|
122
|
+
results.push(`${r.email || rowKey(r)}: FAILED — ${e?.message}`)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
setActionResult(results.join('\n'))
|
|
126
|
+
setSelected(new Set())
|
|
127
|
+
setActionBusy(false)
|
|
128
|
+
void load()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const unbanRow = async (r: FlaggedRow) => {
|
|
132
|
+
if (!r.person_id) { setActionResult('Cannot unban — no person id on this row'); return }
|
|
133
|
+
setActionBusy(true)
|
|
134
|
+
try {
|
|
135
|
+
const res = await callSecurity('unban_user', { person_id: r.person_id })
|
|
136
|
+
setActionResult(`${r.email || r.person_id}: unbanned (auth:${res.layers?.auth} edge:${res.layers?.edge})`)
|
|
137
|
+
} catch (e: any) {
|
|
138
|
+
setActionResult(`Unban failed: ${e?.message}`)
|
|
139
|
+
}
|
|
140
|
+
setActionBusy(false)
|
|
141
|
+
void load()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const revokeEntry = async (entry: BlocklistRow) => {
|
|
145
|
+
setActionBusy(true)
|
|
146
|
+
try {
|
|
147
|
+
await callSecurity('blocklist_revoke', { id: entry.id })
|
|
148
|
+
} catch (e: any) {
|
|
149
|
+
setActionResult(`Revoke failed: ${e?.message}`)
|
|
150
|
+
}
|
|
151
|
+
setActionBusy(false)
|
|
152
|
+
void load()
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const allChecked = rows.length > 0 && selected.size === rows.length
|
|
156
|
+
const activeBlocklist = blocklist.filter(b => !b.revoked_at)
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<div className="p-6 space-y-8">
|
|
160
|
+
<div>
|
|
161
|
+
<h1 className="text-2xl font-semibold">Security</h1>
|
|
162
|
+
<p className="text-sm text-muted-foreground mt-1">
|
|
163
|
+
Flagged actors and platform blocklist. "Add to Blacklist" applies all
|
|
164
|
+
three layers: internal blocklist, Supabase Auth ban, and Netlify edge IP deny.
|
|
165
|
+
</p>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
{error && (
|
|
169
|
+
<div className="rounded-md bg-red-50 p-4 text-sm text-red-800">{error}</div>
|
|
170
|
+
)}
|
|
171
|
+
{actionResult && (
|
|
172
|
+
<pre className="rounded-md bg-slate-100 border border-slate-200 p-3 text-xs whitespace-pre-wrap">{actionResult}</pre>
|
|
173
|
+
)}
|
|
174
|
+
|
|
175
|
+
<section className="space-y-3">
|
|
176
|
+
<div className="flex items-center justify-between">
|
|
177
|
+
<h2 className="text-lg font-medium">Flagged actors</h2>
|
|
178
|
+
<div className="flex items-center gap-2">
|
|
179
|
+
<Button variant="outline" size="sm" onClick={() => load()} disabled={loading}>
|
|
180
|
+
Refresh
|
|
181
|
+
</Button>
|
|
182
|
+
<Button
|
|
183
|
+
size="sm"
|
|
184
|
+
variant="destructive"
|
|
185
|
+
disabled={!selected.size || actionBusy}
|
|
186
|
+
onClick={banSelected}
|
|
187
|
+
>
|
|
188
|
+
{actionBusy ? <LoadingSpinner size="sm" /> : null}
|
|
189
|
+
Add to Blacklist{selected.size ? ` (${selected.size})` : ''}
|
|
190
|
+
</Button>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
{loading ? (
|
|
195
|
+
<div className="flex justify-center py-12"><LoadingSpinner /></div>
|
|
196
|
+
) : rows.length === 0 ? (
|
|
197
|
+
<div className="rounded-md border border-dashed border-slate-300 p-8 text-center text-sm text-muted-foreground">
|
|
198
|
+
No security signals yet. Rate-limit violations, prompt-injection detections,
|
|
199
|
+
and blocked registrations appear here.
|
|
200
|
+
</div>
|
|
201
|
+
) : (
|
|
202
|
+
<Table>
|
|
203
|
+
<TableHeader>
|
|
204
|
+
<TableRow>
|
|
205
|
+
<TableHead className="w-10">
|
|
206
|
+
<Checkbox
|
|
207
|
+
checked={allChecked}
|
|
208
|
+
onCheckedChange={(c) => toggleAll(c === true)}
|
|
209
|
+
aria-label="Select all"
|
|
210
|
+
/>
|
|
211
|
+
</TableHead>
|
|
212
|
+
<TableHead>Actor</TableHead>
|
|
213
|
+
<TableHead>Account</TableHead>
|
|
214
|
+
<TableHead>IP / Country</TableHead>
|
|
215
|
+
<TableHead>Signals</TableHead>
|
|
216
|
+
<TableHead>Last seen</TableHead>
|
|
217
|
+
<TableHead>Status</TableHead>
|
|
218
|
+
<TableHead className="w-24"></TableHead>
|
|
219
|
+
</TableRow>
|
|
220
|
+
</TableHeader>
|
|
221
|
+
<TableBody>
|
|
222
|
+
{rows.map((r) => (
|
|
223
|
+
<TableRow key={rowKey(r)}>
|
|
224
|
+
<TableCell>
|
|
225
|
+
<Checkbox
|
|
226
|
+
checked={selected.has(rowKey(r))}
|
|
227
|
+
onCheckedChange={(c) => toggle(rowKey(r), c === true)}
|
|
228
|
+
aria-label={`Select ${r.email || rowKey(r)}`}
|
|
229
|
+
/>
|
|
230
|
+
</TableCell>
|
|
231
|
+
<TableCell className="font-medium">
|
|
232
|
+
{r.email || <span className="text-muted-foreground">(no person)</span>}
|
|
233
|
+
</TableCell>
|
|
234
|
+
<TableCell>{r.account_name || '—'}</TableCell>
|
|
235
|
+
<TableCell className="text-xs">
|
|
236
|
+
{r.last_ip || '—'}{r.last_country ? ` · ${r.last_country.toUpperCase()}` : ''}
|
|
237
|
+
</TableCell>
|
|
238
|
+
<TableCell>
|
|
239
|
+
<div className="flex flex-wrap gap-1 items-center">
|
|
240
|
+
<Badge variant="secondary">{r.signals}×</Badge>
|
|
241
|
+
{r.kinds.map(k => (
|
|
242
|
+
<Badge key={k} variant="outline" className="text-xs">{k}</Badge>
|
|
243
|
+
))}
|
|
244
|
+
</div>
|
|
245
|
+
</TableCell>
|
|
246
|
+
<TableCell className="text-xs text-muted-foreground">
|
|
247
|
+
{r.last_seen_at ? new Date(r.last_seen_at).toLocaleString() : '—'}
|
|
248
|
+
</TableCell>
|
|
249
|
+
<TableCell>
|
|
250
|
+
{r.banned
|
|
251
|
+
? <Badge variant="destructive">banned</Badge>
|
|
252
|
+
: <Badge variant="outline">{r.person_status || 'active'}</Badge>}
|
|
253
|
+
</TableCell>
|
|
254
|
+
<TableCell>
|
|
255
|
+
{r.banned && r.person_id ? (
|
|
256
|
+
<Button variant="ghost" size="sm" disabled={actionBusy} onClick={() => unbanRow(r)}>
|
|
257
|
+
Unban
|
|
258
|
+
</Button>
|
|
259
|
+
) : null}
|
|
260
|
+
</TableCell>
|
|
261
|
+
</TableRow>
|
|
262
|
+
))}
|
|
263
|
+
</TableBody>
|
|
264
|
+
</Table>
|
|
265
|
+
)}
|
|
266
|
+
</section>
|
|
267
|
+
|
|
268
|
+
<section className="space-y-3">
|
|
269
|
+
<h2 className="text-lg font-medium">Active blocklist</h2>
|
|
270
|
+
{activeBlocklist.length === 0 ? (
|
|
271
|
+
<div className="rounded-md border border-dashed border-slate-300 p-6 text-center text-sm text-muted-foreground">
|
|
272
|
+
No active blocklist entries.
|
|
273
|
+
</div>
|
|
274
|
+
) : (
|
|
275
|
+
<Table>
|
|
276
|
+
<TableHeader>
|
|
277
|
+
<TableRow>
|
|
278
|
+
<TableHead>Type</TableHead>
|
|
279
|
+
<TableHead>Value</TableHead>
|
|
280
|
+
<TableHead>Severity</TableHead>
|
|
281
|
+
<TableHead>Reason</TableHead>
|
|
282
|
+
<TableHead>Created</TableHead>
|
|
283
|
+
<TableHead className="w-24"></TableHead>
|
|
284
|
+
</TableRow>
|
|
285
|
+
</TableHeader>
|
|
286
|
+
<TableBody>
|
|
287
|
+
{activeBlocklist.map((b) => (
|
|
288
|
+
<TableRow key={b.id}>
|
|
289
|
+
<TableCell><Badge variant="outline">{b.type}</Badge></TableCell>
|
|
290
|
+
<TableCell className="font-mono text-xs">{b.value}</TableCell>
|
|
291
|
+
<TableCell>{b.severity}</TableCell>
|
|
292
|
+
<TableCell className="text-xs text-muted-foreground">{b.reason || '—'}</TableCell>
|
|
293
|
+
<TableCell className="text-xs text-muted-foreground">
|
|
294
|
+
{new Date(b.created_at).toLocaleDateString()}
|
|
295
|
+
</TableCell>
|
|
296
|
+
<TableCell>
|
|
297
|
+
<Button variant="ghost" size="sm" disabled={actionBusy} onClick={() => revokeEntry(b)}>
|
|
298
|
+
Revoke
|
|
299
|
+
</Button>
|
|
300
|
+
</TableCell>
|
|
301
|
+
</TableRow>
|
|
302
|
+
))}
|
|
303
|
+
</TableBody>
|
|
304
|
+
</Table>
|
|
305
|
+
)}
|
|
306
|
+
</section>
|
|
307
|
+
</div>
|
|
308
|
+
)
|
|
309
|
+
}
|
|
310
|
+
// CIL[END stable_id="~/.framework/src/pages/admin/SecurityPage.tsx:module_header:module_header"]
|