queryapigate 0.43.0__tar.gz → 0.44.0__tar.gz
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.
- {queryapigate-0.43.0/queryapigate.egg-info → queryapigate-0.44.0}/PKG-INFO +1 -1
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/__init__.py +1 -1
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/app.py +72 -1
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/config.py +14 -0
- queryapigate-0.43.0/queryapigate/console_dist/assets/AccessMapPage-C0Ao2DXb.js → queryapigate-0.44.0/queryapigate/console_dist/assets/AccessMapPage-CHMmNdte.js +2 -2
- queryapigate-0.43.0/queryapigate/console_dist/assets/AdministratorsPage-CF6Go3C2.js → queryapigate-0.44.0/queryapigate/console_dist/assets/AdministratorsPage-BcRj2iee.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/AgentsPage-aI5-GZDw.js → queryapigate-0.44.0/queryapigate/console_dist/assets/AgentsPage-DW3FEK58.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/AlertsPage-BpDKzDi6.js → queryapigate-0.44.0/queryapigate/console_dist/assets/AlertsPage-B_rOXFVO.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/ApiKeyDetailPage-D2_U3VuH.js → queryapigate-0.44.0/queryapigate/console_dist/assets/ApiKeyDetailPage-nPdRuGU_.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/ApiKeysPage-DpCXI_0g.js → queryapigate-0.44.0/queryapigate/console_dist/assets/ApiKeysPage-ghPfvq81.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/ApprovalsPage-BGWpuKi6.js → queryapigate-0.44.0/queryapigate/console_dist/assets/ApprovalsPage-MRLqAd_b.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/AuditLogPage-BFEnPYVL.js → queryapigate-0.44.0/queryapigate/console_dist/assets/AuditLogPage-BvZwseJz.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/CachingPage-Dt3rcH86.js → queryapigate-0.44.0/queryapigate/console_dist/assets/CachingPage-BlwUvdK5.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/ConnectionsPage-BELxZBmR.js → queryapigate-0.44.0/queryapigate/console_dist/assets/ConnectionsPage-DiqCYvzf.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/DesignerPage-mB7FGL4z.js → queryapigate-0.44.0/queryapigate/console_dist/assets/DesignerPage-DOyJWKl2.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/DestinationsPage-Dm96V2vC.js → queryapigate-0.44.0/queryapigate/console_dist/assets/DestinationsPage-BER4b6Ry.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/ExportsPage-DNlYFWce.js → queryapigate-0.44.0/queryapigate/console_dist/assets/ExportsPage-hkbyClkf.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/HelpPage-D2fka50S.js → queryapigate-0.44.0/queryapigate/console_dist/assets/HelpPage-BDfcaLWe.js +2 -2
- queryapigate-0.43.0/queryapigate/console_dist/assets/HomePage-CUgo9g94.js → queryapigate-0.44.0/queryapigate/console_dist/assets/HomePage-CmqH7zlU.js +2 -2
- queryapigate-0.43.0/queryapigate/console_dist/assets/KeyActivity-CKbbdkvM.js → queryapigate-0.44.0/queryapigate/console_dist/assets/KeyActivity-BC6RPPpH.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/Managed-CbQD1mHQ.js → queryapigate-0.44.0/queryapigate/console_dist/assets/Managed-B1rh-_gw.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/McpPage-CGJFAHiN.js → queryapigate-0.44.0/queryapigate/console_dist/assets/McpPage-BIve0LH3.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/MetricsPage-DgJlA8TP.js → queryapigate-0.44.0/queryapigate/console_dist/assets/MetricsPage-DT-KeJBh.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/QueryInfo-B9xmoHuw.js → queryapigate-0.44.0/queryapigate/console_dist/assets/QueryInfo-DB1HWOF1.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/RepositoryPage-Dbdn3QrH.js → queryapigate-0.44.0/queryapigate/console_dist/assets/RepositoryPage-B1HT63ey.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/Results-fzKK4V5W.js → queryapigate-0.44.0/queryapigate/console_dist/assets/Results-BsH71FdD.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/RolesPage-BELto4hG.js → queryapigate-0.44.0/queryapigate/console_dist/assets/RolesPage-obvoaIWp.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/SchemaBrowser-1u2p1iY-.js → queryapigate-0.44.0/queryapigate/console_dist/assets/SchemaBrowser-DLfU-4bj.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/SettingsPage-DJToGsgg.js → queryapigate-0.44.0/queryapigate/console_dist/assets/SettingsPage-D3_OfVa3.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/Time-USlo65Ii.js → queryapigate-0.44.0/queryapigate/console_dist/assets/Time-DE-u5yL9.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/WorkspacesPage-D_1ftIEs.js → queryapigate-0.44.0/queryapigate/console_dist/assets/WorkspacesPage-BLeFFFzT.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/api-DhjsrkN_.js → queryapigate-0.44.0/queryapigate/console_dist/assets/api-BlpyufoX.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/forms-Bn5QJbOg.js → queryapigate-0.44.0/queryapigate/console_dist/assets/forms-BOFRAueI.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/forms-BRJEHg6U.js → queryapigate-0.44.0/queryapigate/console_dist/assets/forms-DL4MJS43.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/index-uRArh6IJ.js → queryapigate-0.44.0/queryapigate/console_dist/assets/index-DBrO0zMK.js +2 -2
- queryapigate-0.43.0/queryapigate/console_dist/assets/shared-i1-TsKj7.js → queryapigate-0.44.0/queryapigate/console_dist/assets/shared-1MeLE7ZH.js +1 -1
- queryapigate-0.43.0/queryapigate/console_dist/assets/useMutation-Cheq0sq0.js → queryapigate-0.44.0/queryapigate/console_dist/assets/useMutation-BHDQeA9M.js +1 -1
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/console_dist/index.html +1 -1
- queryapigate-0.44.0/queryapigate/dataplane.py +157 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/db.py +12 -2
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/engine.py +3 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/events.py +9 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/experimental.py +7 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/history.py +55 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/masking.py +12 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/metacache.py +43 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/metrics.py +27 -8
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/sqltools.py +29 -19
- {queryapigate-0.43.0 → queryapigate-0.44.0/queryapigate.egg-info}/PKG-INFO +1 -1
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate.egg-info/SOURCES.txt +35 -33
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_collections.py +2 -1
- queryapigate-0.44.0/tests/test_dataplane.py +415 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_history.py +48 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_settings.py +1 -1
- {queryapigate-0.43.0 → queryapigate-0.44.0}/LICENSE +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/NOTICE +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/README.md +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/pyproject.toml +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/__main__.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/accesslog.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/adminroles.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/admins.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/agentwrites.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/alerts.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/apikeys.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/auditchain.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/auditstream.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/aws.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/broadcast.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/bundle.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/cache.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/cli.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/collection_admin.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/configcode.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/console.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/console_dist/assets/CodeBox-Cwh5KHLp.js +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/console_dist/assets/Experimental-B3dnaeV7.js +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/console_dist/assets/StatTile-Cft4kJPH.js +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/console_dist/assets/client-ZHNiMG8H.js +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/console_dist/assets/icons-CFXE8hAz.js +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/console_dist/assets/index-Cv64XKNs.css +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/console_dist/assets/maskRules-CvKQIPgN.js +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/console_dist/assets/reach-BAOLd75Y.js +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/console_dist/assets/state-O13cl7cO.js +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/console_dist/favicon.svg +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/cors.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/databases.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/definitions.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/deprecations.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/descriptions.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/destinations.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/duckfiles.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/embeddings.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/errors.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/examples.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/exporting.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/exports.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/formats.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/governance.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/gunicorn_conf.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/instances.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/jwtauth.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/lib/h2-2.2.224.jar +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/limits.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/logging_setup.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/mcp_server.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/mongodocs.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/mongotools.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/oidc.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/openapi.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/params.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/parquet.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/pool.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/postman.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/quotas.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/ratelimit.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/rediscache.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/rekey.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/runners.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/schema.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/scope.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/secheaders.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/secretrefs.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/services/__init__.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/services/access.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/services/administrators.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/services/audit.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/services/collections.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/services/connections.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/services/descriptions.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/services/destinations.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/services/exports.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/services/mcp.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/services/queries.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/services/workspaces.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/sqlflow.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/sso.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/store.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/tableguard.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/tracing.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/usage.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/v1.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/v1_spec.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/vault.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate/workspaces.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate.egg-info/dependency_links.txt +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate.egg-info/entry_points.txt +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate.egg-info/requires.txt +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/queryapigate.egg-info/top_level.txt +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/setup.cfg +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_adhoc_history.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_adminroles.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_admins.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_agent_writes.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_alerts.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_api_administrators.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_api_keys.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_api_v1.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_audit_chain.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_audit_log.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_audit_siem.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_audit_stream.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_aws.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_backup_restore.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_broadcast.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_cli.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_collection_bundle.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_collection_postman.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_configcode.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_connection_encryption.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_console.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_cors_and_rate_limit.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_data_protection.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_databases.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_db.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_dependency_floors.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_deprecations.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_descriptions.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_destinations.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_docs_site.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_duckdb.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_duckdb_files.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_embeddings.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_errors.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_events.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_events_server.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_examples.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_experimental.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_export_security.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_exporting.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_exports.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_fips.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_helm.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_howto.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_instances.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_integration.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_jdbc.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_jwt.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_key_rotation.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_limits.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_masking.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_masking_requests.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_mcp.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_mcp_keycloak.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_mcp_oauth.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_metacache.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_mongo_streaming.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_mongotools.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_multinode.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_observability.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_oidc.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_params.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_parquet.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_pool.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_publishing.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_query_window.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_queryapigate.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_quotas.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_rekey.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_release_check.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_response_cache.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_roles.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_rolling_upgrade.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_secretrefs.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_security_headers.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_shared_rate_limit.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_sql_guard_fuzz.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_sql_guard_writes.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_sqlflow.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_sso.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_sso_keycloak.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_supply_chain.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_tableguard.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_tracing.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_upgrades.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_usage.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_vault.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_workspace_config_cli.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_workspace_isolation.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_workspace_observability.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_workspace_store.py +0 -0
- {queryapigate-0.43.0 → queryapigate-0.44.0}/tests/test_workspaces_api.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: queryapigate
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.44.0
|
|
4
4
|
Summary: Turn SQL into governed REST APIs over MySQL, PostgreSQL, ClickHouse, SQLite, DuckDB, H2, MongoDB, and Parquet/CSV/JSON files - JSON, CSV, Parquet or Excel out, with scoped keys, an admin Console and MCP for AI agents.
|
|
5
5
|
Author-email: Anantha Raju C <arcswdev@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -24,6 +24,7 @@ from . import (
|
|
|
24
24
|
config,
|
|
25
25
|
console,
|
|
26
26
|
cors,
|
|
27
|
+
dataplane,
|
|
27
28
|
db,
|
|
28
29
|
definitions,
|
|
29
30
|
deprecations,
|
|
@@ -40,6 +41,7 @@ from . import (
|
|
|
40
41
|
limits,
|
|
41
42
|
logging_setup,
|
|
42
43
|
masking,
|
|
44
|
+
metacache,
|
|
43
45
|
metrics,
|
|
44
46
|
mongodocs,
|
|
45
47
|
mongotools,
|
|
@@ -65,6 +67,7 @@ bp = Blueprint('api', __name__)
|
|
|
65
67
|
RESERVED_ARGS = {'format', 'page', 'page_size', 'connection_name', 'version', 'timeout', 'stream'}
|
|
66
68
|
PUBLIC_ENDPOINTS = {'api.index', 'api.favicon', 'api.health', 'api.docs', 'api.openapi_spec', 'api.admin_ui',
|
|
67
69
|
'api.console_page', 'api.metrics_endpoint',
|
|
70
|
+
'api.dataplane_runs', # the data plane's reports: gate() lets only its secret through (ADR 0028)
|
|
68
71
|
# Console sign-in (sso.py): each checks what it needs itself - state, the session and its CSRF token
|
|
69
72
|
'auth.login', 'auth.callback', 'auth.session', 'auth.logout'}
|
|
70
73
|
# Monitoring keeps working while a client is throttled; the Console's static files are exempt so loading the page
|
|
@@ -167,12 +170,24 @@ def warn_env_references():
|
|
|
167
170
|
|
|
168
171
|
class _CapturePeer:
|
|
169
172
|
"""Record the real socket address (`REMOTE_ADDR`) before ProxyFix can replace it with an X-Forwarded-For value,
|
|
170
|
-
so the open-access loopback check (gate()) judges the actual connection and never a header a client can forge.
|
|
173
|
+
so the open-access loopback check (gate()) judges the actual connection and never a header a client can forge.
|
|
174
|
+
|
|
175
|
+
Behind a data plane (dataplane.py, ADR 0028) every request comes from it, over loopback: one that carries its
|
|
176
|
+
secret names the client's own socket address in X-QAG-Peer, and that address is taken as the real one - so this
|
|
177
|
+
server sees the request as if the client had connected directly, ProxyFix and QUERYAPIGATE_TRUST_PROXY included.
|
|
178
|
+
A peer that isn't an IP address counts as remote, as a Unix socket does. Both headers are removed here, so
|
|
179
|
+
nothing after this layer reads, logs or passes on the secret."""
|
|
171
180
|
|
|
172
181
|
def __init__(self, app):
|
|
173
182
|
self.app = app
|
|
174
183
|
|
|
175
184
|
def __call__(self, environ, start_response):
|
|
185
|
+
supplied = environ.pop(dataplane.SECRET_ENVIRON, None)
|
|
186
|
+
peer = environ.pop(dataplane.PEER_ENVIRON, None)
|
|
187
|
+
wanted = environ.pop(dataplane.WANTED_ENVIRON, None)
|
|
188
|
+
if supplied is not None and dataplane.matches(supplied):
|
|
189
|
+
environ['queryapigate.dataplane'] = 'plan' if wanted == '1' else True
|
|
190
|
+
environ['REMOTE_ADDR'] = peer if dataplane.is_address(peer) else ''
|
|
176
191
|
environ['queryapigate.peer_addr'] = environ.get('REMOTE_ADDR')
|
|
177
192
|
return self.app(environ, start_response)
|
|
178
193
|
|
|
@@ -301,12 +316,21 @@ def create_app():
|
|
|
301
316
|
@app.teardown_request
|
|
302
317
|
def end_trace(_error):
|
|
303
318
|
tracing.finish(g.pop('trace', None))
|
|
319
|
+
token = g.pop('dataplane_token', None)
|
|
320
|
+
if token is not None:
|
|
321
|
+
metacache.reset(token)
|
|
304
322
|
|
|
305
323
|
@app.before_request
|
|
306
324
|
def gate():
|
|
307
325
|
# Set first so every request - including one rejected below - gets an ID and a measured duration.
|
|
308
326
|
g.request_id = new_request_id(request.headers.get('X-Request-Id'))
|
|
309
327
|
g.request_started = time.monotonic()
|
|
328
|
+
if request.endpoint == 'api.dataplane_runs': # the data plane's own reports: nothing else applies to them
|
|
329
|
+
if request.environ.get('queryapigate.dataplane'):
|
|
330
|
+
return None
|
|
331
|
+
return jsonify(error_body('Not Found', 404)), 404
|
|
332
|
+
if request.environ.get('queryapigate.dataplane') == 'plan': # before the key is read: a plan names its row
|
|
333
|
+
g.dataplane_record, g.dataplane_token = metacache.capture()
|
|
310
334
|
# The request's span (tracing.py, ADR 0010): named by the route rule, never the path's values or query string
|
|
311
335
|
rule = request.url_rule.rule if request.url_rule is not None else None
|
|
312
336
|
g.trace = tracing.start(f'{request.method} {rule}' if rule else request.method, 'server', {
|
|
@@ -346,6 +370,8 @@ def create_app():
|
|
|
346
370
|
|
|
347
371
|
@app.after_request
|
|
348
372
|
def decorate(response):
|
|
373
|
+
if request.endpoint == 'api.dataplane_runs': # counted and logged as the runs it reports, not itself
|
|
374
|
+
return response
|
|
349
375
|
if not request.path.startswith('/auth/'): # Console sign-in is same-origin only (sso.py)
|
|
350
376
|
cors.add_headers(response, request.headers.get('Origin'))
|
|
351
377
|
if g.get('rate_limit'):
|
|
@@ -1075,6 +1101,12 @@ def _run_saved(ref, body, url_params):
|
|
|
1075
1101
|
**masked_entry(result.masked)})
|
|
1076
1102
|
if cache_key is not None:
|
|
1077
1103
|
response = cache_store(cache_key, response, ttl, cache_meta)
|
|
1104
|
+
elif (g.get('dataplane_record') is not None and g.get('transport', 'rest') == 'rest' and not ttl
|
|
1105
|
+
and output_format == 'json'): # the one format the data plane renders
|
|
1106
|
+
plan = dataplane.build(g.dataplane_record, g.permission, query_name, number, saved, connection_name, result,
|
|
1107
|
+
config.allow_writes() and effective_allow_writes)
|
|
1108
|
+
if plan is not None:
|
|
1109
|
+
response.headers[dataplane.PLAN_HEADER] = dataplane.encode(plan)
|
|
1078
1110
|
return response
|
|
1079
1111
|
|
|
1080
1112
|
|
|
@@ -1192,6 +1224,45 @@ def run_named_query(name):
|
|
|
1192
1224
|
return run_saved(name, body, url_params)
|
|
1193
1225
|
|
|
1194
1226
|
|
|
1227
|
+
@bp.route('/internal/dataplane/runs', methods=['POST'])
|
|
1228
|
+
def dataplane_runs():
|
|
1229
|
+
"""The runs a data plane in front of this server served itself (dataplane.py, ADR 0028), batched: each recorded as
|
|
1230
|
+
run_saved() records one - run history, the event log, the live feed - and counted in /metrics as it would have been.
|
|
1231
|
+
Only the data plane reaches it: gate() answers 404 to anything without its secret. `dropped`: runs it served but
|
|
1232
|
+
couldn't queue for here, counted as history's own dropped runs are.
|
|
1233
|
+
|
|
1234
|
+
Each run comes with its history entry already JSON (`entry`), and the labels and timings /metrics counts it by: at
|
|
1235
|
+
the data plane's rates Python's own work per run - encoding it, and its labels - is what it can't keep up with, so
|
|
1236
|
+
a batch is queued at once (history.record_reported()) and counted once per set of labels."""
|
|
1237
|
+
report = request.get_json(silent=True) or {}
|
|
1238
|
+
runs = report.get('runs') or []
|
|
1239
|
+
broadcaster = current_app.extensions['queryapigate_broadcaster']
|
|
1240
|
+
with scope.using(scope.current()): # resolved once, not by each of a busy batch's few thousand label lookups
|
|
1241
|
+
history.record_reported(runs)
|
|
1242
|
+
if broadcaster.subscriber_count(): # the live feed's events, only when anyone is listening
|
|
1243
|
+
for run in runs:
|
|
1244
|
+
broadcaster.publish(events.run_payload(run['query'], run['version'], json.loads(run['entry'])),
|
|
1245
|
+
run['key'], scope.current())
|
|
1246
|
+
labelled = {}
|
|
1247
|
+
for run in runs:
|
|
1248
|
+
seen = labelled.setdefault((run['connection'], run['dialect'], run['status'], run['key'], run['method'],
|
|
1249
|
+
str(run['http_status'])), ([], [], [], [0]))
|
|
1250
|
+
seen[0].append(run['query_seconds'])
|
|
1251
|
+
seen[1].append(run['seconds'])
|
|
1252
|
+
if run['status'] == 'success':
|
|
1253
|
+
seen[2].append(run['serialization_seconds'])
|
|
1254
|
+
seen[3][0] += run['rows']
|
|
1255
|
+
for (connection, dialect, status, key, method, http_status), seen in labelled.items():
|
|
1256
|
+
metrics.observe_queries(connection, dialect, status, seen[0], key)
|
|
1257
|
+
if status == 'success':
|
|
1258
|
+
metrics.observe_rows(connection, dialect, key, seen[3][0])
|
|
1259
|
+
metrics.observe_serializations('json', seen[2])
|
|
1260
|
+
metrics.observe_requests(method, 'api.run_named_query', http_status, seen[1], key or '-')
|
|
1261
|
+
if report.get('dropped'):
|
|
1262
|
+
metrics.inc_history('dropped', int(report['dropped']))
|
|
1263
|
+
return '', 204
|
|
1264
|
+
|
|
1265
|
+
|
|
1195
1266
|
# --------------------------------------------------------------------------------------
|
|
1196
1267
|
# Live events
|
|
1197
1268
|
# --------------------------------------------------------------------------------------
|
|
@@ -387,6 +387,9 @@ def check_settings():
|
|
|
387
387
|
for folder in os.environ.get('QUERYAPIGATE_SECRET_FILE_DIRS', '').split(','):
|
|
388
388
|
if folder.strip() and not os.path.isabs(folder.strip()):
|
|
389
389
|
raise ValueError(f"QUERYAPIGATE_SECRET_FILE_DIRS: '{folder.strip()}' must be an absolute path")
|
|
390
|
+
if dataplane_secret() is not None and len(dataplane_secret()) < DATAPLANE_SECRET_MIN:
|
|
391
|
+
raise ValueError(f'QUERYAPIGATE_DATAPLANE_SECRET must be at least {DATAPLANE_SECRET_MIN} characters - the data '
|
|
392
|
+
'plane refuses a shorter one too')
|
|
390
393
|
if metrics_token() is not None and len(metrics_token()) < METRICS_TOKEN_MIN:
|
|
391
394
|
raise ValueError(f'QUERYAPIGATE_METRICS_TOKEN must be at least {METRICS_TOKEN_MIN} characters - '
|
|
392
395
|
'`openssl rand -hex 32` makes one')
|
|
@@ -699,6 +702,7 @@ def secret_source(name):
|
|
|
699
702
|
|
|
700
703
|
|
|
701
704
|
METRICS_TOKEN_MIN = 16
|
|
705
|
+
DATAPLANE_SECRET_MIN = 16 # dataplane/src/config.rs's MIN_SECRET: it is all that lets a request speak for a client
|
|
702
706
|
|
|
703
707
|
|
|
704
708
|
def metrics_token():
|
|
@@ -713,6 +717,13 @@ def mask_key():
|
|
|
713
717
|
return os.environ.get('QUERYAPIGATE_MASK_KEY', '').strip() or None
|
|
714
718
|
|
|
715
719
|
|
|
720
|
+
def dataplane_secret():
|
|
721
|
+
"""QUERYAPIGATE_DATAPLANE_SECRET: what a Rust data plane in front of this server sends to have its forwarded `/q/`
|
|
722
|
+
calls answered with a plan (dataplane.py, ADR 0028), or None - then no request gets one. A prototype setting:
|
|
723
|
+
shared by the two processes only, never by a client."""
|
|
724
|
+
return os.environ.get('QUERYAPIGATE_DATAPLANE_SECRET', '').strip() or None
|
|
725
|
+
|
|
726
|
+
|
|
716
727
|
def secret_keys():
|
|
717
728
|
"""The keys connection and destination passwords are encrypted with at rest (QUERYAPIGATE_SECRET_KEY), first
|
|
718
729
|
to last - empty when unset: encryption at rest is opt-in, and without it a password is stored exactly as given.
|
|
@@ -1401,6 +1412,9 @@ def describe_settings():
|
|
|
1401
1412
|
secret=True),
|
|
1402
1413
|
row('Masking hash key', "Key for column masking's hash action. Unset: one made once in the store.",
|
|
1403
1414
|
'QUERYAPIGATE_MASK_KEY', 'configured' if mask_key() else 'from the store', secret=True),
|
|
1415
|
+
row('Data plane secret', 'Shared with a Rust data plane in front of this server (ADR 0028), which then '
|
|
1416
|
+
'serves /q/ reads itself. Unset: no data plane can.',
|
|
1417
|
+
'QUERYAPIGATE_DATAPLANE_SECRET', 'configured' if dataplane_secret() else 'not set', secret=True),
|
|
1404
1418
|
row('Export folders', 'Local folders a destination may write under. Unset: object storage only.',
|
|
1405
1419
|
'QUERYAPIGATE_EXPORT_DIRS', ', '.join(export_dirs()) or 'object storage only'),
|
|
1406
1420
|
row('Trusted proxy hops', 'Reverse proxies whose X-Forwarded-* headers are trusted.',
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/forms-
|
|
2
|
-
import{B as e,H as t,z as n}from"./client-ZHNiMG8H.js";import{D as r,M as i,P as a,c as o,d as s,g as c,k as l,s as u}from"./index-uRArh6IJ.js";import{t as d}from"./Time-USlo65Ii.js";import{t as f}from"./QueryInfo-B9xmoHuw.js";import{a as p}from"./reach-BAOLd75Y.js";import{i as m,n as h,o as g,r as _}from"./SchemaBrowser-1u2p1iY-.js";var v=t(e(),1),y=n(),b=(0,v.lazy)(()=>a(()=>import(`./forms-BRJEHg6U.js`).then(e=>({default:e.QueryForm})),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9]))),x={query:`Q`,collection:`C`,connection:`W`},S=[`Database`,`Created`,`Last modified`,`Last used`,`Version`],C=[`database`,`created`,`modified`,`lastUsed`,`version`],w=()=>new Date().toISOString().slice(0,10);function T(){let e=i(),{openDrawer:t}=l(),n=u(),a=o(),d=c(),x=s(),[S,C]=(0,v.useState)(m),w=e=>{let t={...S,...e};g(t),C(t)},T=x.data??[],D=e=>T.find(t=>t.name===e)?.db??null,O=h(S.conn||void 0),k=O.data?.tables??[],A=_(S.conn||void 0,D(S.conn),S.table&&!S.tableMatches?[S.table]:[]),j=S.table?S.tableMatches??(A.data?A.data[S.table]??[]:null):null,M=n.data??[],N=[...new Set(M.map(e=>D(e.connection_name)).filter(e=>!!e))].sort(),P=a.data??{},F=d.data??{},I=Object.keys(P).sort(),L=Object.keys(F).sort(),R,z=!0;S.conn?O.isPending?R=(0,y.jsx)(`option`,{value:``,children:`Loading tables…`}):O.isError?R=(0,y.jsx)(`option`,{value:``,children:`Schema unavailable`}):(z=!1,R=(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(`option`,{value:``,children:`All tables (${k.length})`}),k.map(e=>(0,y.jsx)(`option`,{value:e.name,children:`${e.name} · ${e.columns.length}${e.columns.length===1?` col`:` cols`}`},e.name))]})):R=(0,y.jsx)(`option`,{value:``,children:`Select a connection first`});let B=(e,t)=>p(e,t.name,t.collection,t.connection_name),V;if(n.isPending)V=(0,y.jsx)(r,{});else if(!M.length)V=(0,y.jsx)(`div`,{className:`empty`,children:(0,y.jsx)(`strong`,{children:`No saved queries yet`})});else if(!I.length&&!L.length)V=(0,y.jsxs)(`div`,{className:`empty`,children:[(0,y.jsx)(`strong`,{children:`No scoped API keys or roles yet`}),(0,y.jsx)(`span`,{children:`Every query is reachable by the admin key only until a scoped key is created.`})]});else{let n=S.q.trim().toLowerCase(),i=[...M].sort((e,t)=>(e.collection||``)+e.name<(t.collection||``)+t.name?-1:1),a=e=>e.toLowerCase().includes(n),o=n?i.filter(e=>a(e.name)||e.collection&&a(e.collection)):i,s=n?I.filter(a):I,c=n?L.filter(a):L,l=o.length||s.length||c.length,u=o.length?o:l?i:[],d=s.length?s:l?I:[],p=c.length?c:l?L:[];if(S.conn&&(u=u.filter(e=>e.connection_name===S.conn)),S.table&&j===null)V=(0,y.jsx)(r,{text:`Checking which queries reference ${S.table}…`});else if(j&&(u=u.filter(e=>j.includes(e.name))),u.length){let n=u.map(e=>({q:e,dbType:D(e.connection_name),reach:d.filter(t=>B(P[t],e).length).length}));S.db&&(n=n.filter(e=>e.dbType===S.db)),S.reach===`reachable`?n=n.filter(e=>e.reach>0):S.reach===`unreachable`&&(n=n.filter(e=>e.reach===0));let r=S.sort;if(r.field){let e=e=>{switch(r.field){case`query`:return e.q.name.toLowerCase();case`database`:return(e.dbType||``).toLowerCase();case`created`:return e.q.created_at||``;case`modified`:return e.q.updated_at||``;case`lastUsed`:return e.q.last_used_at||``;case`version`:return e.q.latest_version;case`connection`:return(e.q.connection_name||``).toLowerCase();case`reach`:return e.reach;case`col`:{let t=r.colType===`key`?P[r.colName]:F[r.colName];return t&&B(t,e.q).length?1:0}default:return``}};n=[...n].sort((t,n)=>{let i=e(t),a=e(n);return i<a?-r.dir:i>a?r.dir:0})}V=n.length?(0,y.jsx)(E,{rows:n,keyCols:d,roleCols:p,keyMap:P,roleMap:F,st:S,onSort:e=>w({sort:e}),onOpen:t=>e(`/queries/${encodeURIComponent(t)}`),onInfo:e=>t({title:e.q.name,kicker:`${e.q.collection||`uncollected`} · ${e.q.version_count}${e.q.version_count===1?` version`:` versions`}`,content:(0,y.jsx)(f,{name:e.q.name,dbType:e.dbType})})}):(0,y.jsx)(`div`,{className:`empty`,children:(0,y.jsx)(`span`,{children:`No queries match these filters.`})})}else{let i=n?`Nothing matches “${n}”.`:S.table?`“${S.table}” on ${S.conn} is not exposed by any saved query yet.`:S.conn?`No saved query uses “${S.conn}” yet.`:`Nothing matches these filters.`;V=(0,y.jsxs)(`div`,{className:`empty`,children:[(0,y.jsx)(`span`,{children:i}),!n&&S.conn?(0,y.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:()=>{let n=D(S.conn)===`mongo`,i=S.table?n?JSON.stringify({collection:S.table,filter:{}},null,2):`SELECT * FROM `+S.table:void 0;e(`/queries`),t({title:`New API`,kicker:`POST /api/v1/queries`,content:(0,y.jsx)(v.Suspense,{fallback:(0,y.jsx)(r,{}),children:(0,y.jsx)(b,{starter:{connection:S.conn,sql:i}})})})},children:`New API`+(S.table?` on `+S.table:``)}):null]})}}return(0,y.jsxs)(y.Fragment,{children:[(0,y.jsxs)(`div`,{className:`page-head`,children:[(0,y.jsxs)(`div`,{className:`titles`,children:[(0,y.jsx)(`h1`,{children:`Access map`}),(0,y.jsxs)(`span`,{className:`sub`,children:[`Which API keys can reach which saved queries - the whole point of collections and connection grants, in one place.`,` `,(0,y.jsxs)(`span`,{className:`legend`,children:[(0,y.jsx)(`span`,{className:`amap-dot`,children:`Q`}),` named query \xA0`,(0,y.jsx)(`span`,{className:`amap-dot`,children:`C`}),` `,`collection \xA0`,(0,y.jsx)(`span`,{className:`amap-dot conn`,children:`W`}),` whole connection`]})]})]}),(0,y.jsx)(`span`,{className:`spacer`}),(0,y.jsxs)(`select`,{id:`accessmap-conn-filter`,"aria-label":`Connection`,value:T.some(e=>e.name===S.conn)?S.conn:``,onChange:e=>w({conn:e.target.value,table:``,tableMatches:null}),children:[(0,y.jsx)(`option`,{value:``,children:`All connections`}),[...T].sort((e,t)=>e.name<t.name?-1:1).map(e=>(0,y.jsx)(`option`,{value:e.name,children:e.name},e.name))]}),(0,y.jsx)(`select`,{id:`accessmap-table-filter`,"aria-label":`Table`,disabled:z,value:k.some(e=>e.name===S.table)?S.table:``,onChange:e=>w({table:e.target.value,tableMatches:null}),children:R}),(0,y.jsxs)(`select`,{id:`accessmap-db-filter`,"aria-label":`Database`,value:N.includes(S.db)?S.db:``,onChange:e=>w({db:e.target.value}),children:[(0,y.jsx)(`option`,{value:``,children:`All databases`}),N.map(e=>(0,y.jsx)(`option`,{value:e,children:e},e))]}),(0,y.jsxs)(`select`,{id:`accessmap-reach-filter`,"aria-label":`Reach`,value:S.reach,onChange:e=>w({reach:e.target.value}),children:[(0,y.jsx)(`option`,{value:``,children:`Any reach`}),(0,y.jsx)(`option`,{value:`reachable`,children:`Reachable by a key`}),(0,y.jsx)(`option`,{value:`unreachable`,children:`Reachable by no key`})]}),(0,y.jsx)(`input`,{id:`accessmap-filter`,className:`search`,type:`search`,placeholder:`Filter by query or key…`,value:S.q,onChange:e=>w({q:e.target.value})})]}),(0,y.jsx)(`div`,{id:`accessmap-body`,className:`panel`,children:V})]})}function E({rows:e,keyCols:t,roleCols:n,keyMap:r,roleMap:i,st:a,onSort:o,onOpen:s,onInfo:c}){let l=a.sort,u=(e,t=null,n=null)=>l.field===e&&l.colType===t&&l.colName===n,f=(e,t=null,n=null)=>u(e,t,n)?l.dir===1?` ▲`:` ▼`:``,m=(e,t=null,n=null)=>{let r=u(e,t,n);o(r&&l.dir===1?{field:e,colType:t,colName:n,dir:-1}:r?{field:null,colType:null,colName:null,dir:1}:{field:e,colType:t,colName:n,dir:1})},h=(e,t,n)=>(0,y.jsx)(`th`,{className:t,onClick:()=>m(n),children:e+f(n)},n),g=(e,t,n,r)=>(0,y.jsxs)(`th`,{className:`amap-key-head amap-sortable`+(n?` revoked`:``),title:n?e+n:e,onClick:()=>m(`col`,r,e),children:[(0,y.jsxs)(`span`,{className:`amap-key-name`,children:[e,f(`col`,r,e),n?(0,y.jsx)(`span`,{className:`dim`,children:n}):null]}),(0,y.jsxs)(`span`,{className:`amap-key-sub`,children:[(t.allow_writes?`Read/write`:`Read-only`)+` · `+(t.rate_limit||`server default`),t.mask?.length?(0,y.jsx)(`span`,{className:`tag`,title:t.mask.map(e=>`${e.column}: ${e.action}`).join(`, `),children:`masked`}):null]})]},r+`:`+e),_=(e,t,n,r)=>{let i=p(e,t.name,t.collection,t.connection_name);if(!i.length)return(0,y.jsx)(`td`,{className:`amap-cell`},r);let a=[...new Set(i.map(e=>x[e.kind]))],o=a.length===1&&a[0]===`W`;return(0,y.jsx)(`td`,{className:`amap-cell`,title:i.map(e=>e.label).join(`, `),children:(0,y.jsx)(`span`,{className:`amap-dot`+(n?` role`:``)+(o?` conn`:``),children:a.join(`·`)})},r)},v=w();return(0,y.jsx)(`div`,{style:{overflow:`auto`},children:(0,y.jsxs)(`table`,{className:`amap`,children:[(0,y.jsxs)(`thead`,{children:[(0,y.jsxs)(`tr`,{children:[(0,y.jsx)(`th`,{colSpan:3+S.length}),t.length?(0,y.jsx)(`th`,{colSpan:t.length,className:`amap-group`,children:`API KEYS`}):null,n.length?(0,y.jsx)(`th`,{colSpan:n.length,className:`amap-group role`,title:`What a key created from each role would reach - a role grants nothing on its own`,children:`ROLES`}):null]}),(0,y.jsxs)(`tr`,{children:[h(`Query`,`amap-query amap-sortable`,`query`),C.map((e,t)=>h(S[t],`amap-sortable`,e)),h(`Connection`,`amap-sortable`,`connection`),h(`Reach`,`num amap-sortable`,`reach`),t.map(e=>{let t=r[e],n=!!(t.expires_at&&t.expires_at<v);return g(e,t,t.active?n?` (expired ${t.expires_at})`:``:` (revoked)`,`key`)}),n.map(e=>g(e,i[e],``,`role`))]})]}),(0,y.jsx)(`tbody`,{children:e.map(e=>{let a=e.q;return(0,y.jsxs)(`tr`,{children:[(0,y.jsxs)(`td`,{className:`amap-query`,children:[(0,y.jsxs)(`div`,{className:`amap-q-row`,children:[(0,y.jsx)(`button`,{type:`button`,className:`amap-q-name`,title:`Open ${a.name} in API Repository`,onClick:()=>s(a.name),children:a.name}),(0,y.jsx)(`button`,{type:`button`,className:`amap-info`,title:`Details for ${a.name}`,"aria-label":`Details for ${a.name}`,onClick:()=>c(e),children:`i`})]}),a.collection?(0,y.jsx)(`span`,{className:`amap-coll`,children:a.collection}):null]}),(0,y.jsx)(`td`,{children:e.dbType?(0,y.jsx)(`span`,{className:`tag`,children:e.dbType}):(0,y.jsx)(`span`,{className:`dim`,children:`—`})}),(0,y.jsx)(`td`,{className:`mono dim`,children:(0,y.jsx)(d,{value:a.created_at})}),(0,y.jsx)(`td`,{className:`mono dim`,children:(0,y.jsx)(d,{value:a.updated_at})}),(0,y.jsx)(`td`,{className:a.last_used_at?`mono`:`mono dim`,children:(0,y.jsx)(d,{value:a.last_used_at,fallback:`never`})}),(0,y.jsx)(`td`,{className:`mono`,children:`v`+a.latest_version}),(0,y.jsx)(`td`,{className:`mono dim`,children:a.connection_name||`—`}),(0,y.jsx)(`td`,{className:`num`,title:`Reachable by an actual API key (roles alone reach nothing)`,children:e.reach?String(e.reach):`—`}),t.map(e=>_(r[e],a,!1,`k:`+e)),n.map(e=>_(i[e],a,!0,`r:`+e))]},a.name)})}),(0,y.jsx)(`tfoot`,{children:(0,y.jsxs)(`tr`,{children:[(0,y.jsx)(`td`,{className:`amap-query`,children:`Reaches`}),S.map(e=>(0,y.jsx)(`td`,{},e)),(0,y.jsx)(`td`,{}),(0,y.jsx)(`td`,{}),t.map(t=>(0,y.jsx)(`td`,{className:`num`,children:`${e.filter(e=>p(r[t],e.q.name,e.q.collection,e.q.connection_name).length).length} / ${e.length}`},t)),n.map(t=>(0,y.jsx)(`td`,{className:`num`,children:`${e.filter(e=>p(i[t],e.q.name,e.q.collection,e.q.connection_name).length).length} / ${e.length}`},t))]})})]})})}export{T as AccessMapPage};
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/forms-DL4MJS43.js","assets/client-ZHNiMG8H.js","assets/index-DBrO0zMK.js","assets/index-Cv64XKNs.css","assets/CodeBox-Cwh5KHLp.js","assets/api-BlpyufoX.js","assets/useMutation-BHDQeA9M.js","assets/maskRules-CvKQIPgN.js","assets/SchemaBrowser-DLfU-4bj.js","assets/reach-BAOLd75Y.js"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{B as e,H as t,z as n}from"./client-ZHNiMG8H.js";import{D as r,M as i,P as a,c as o,d as s,g as c,k as l,s as u}from"./index-DBrO0zMK.js";import{t as d}from"./Time-DE-u5yL9.js";import{t as f}from"./QueryInfo-DB1HWOF1.js";import{a as p}from"./reach-BAOLd75Y.js";import{i as m,n as h,o as g,r as _}from"./SchemaBrowser-DLfU-4bj.js";var v=t(e(),1),y=n(),b=(0,v.lazy)(()=>a(()=>import(`./forms-DL4MJS43.js`).then(e=>({default:e.QueryForm})),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9]))),x={query:`Q`,collection:`C`,connection:`W`},S=[`Database`,`Created`,`Last modified`,`Last used`,`Version`],C=[`database`,`created`,`modified`,`lastUsed`,`version`],w=()=>new Date().toISOString().slice(0,10);function T(){let e=i(),{openDrawer:t}=l(),n=u(),a=o(),d=c(),x=s(),[S,C]=(0,v.useState)(m),w=e=>{let t={...S,...e};g(t),C(t)},T=x.data??[],D=e=>T.find(t=>t.name===e)?.db??null,O=h(S.conn||void 0),k=O.data?.tables??[],A=_(S.conn||void 0,D(S.conn),S.table&&!S.tableMatches?[S.table]:[]),j=S.table?S.tableMatches??(A.data?A.data[S.table]??[]:null):null,M=n.data??[],N=[...new Set(M.map(e=>D(e.connection_name)).filter(e=>!!e))].sort(),P=a.data??{},F=d.data??{},I=Object.keys(P).sort(),L=Object.keys(F).sort(),R,z=!0;S.conn?O.isPending?R=(0,y.jsx)(`option`,{value:``,children:`Loading tables…`}):O.isError?R=(0,y.jsx)(`option`,{value:``,children:`Schema unavailable`}):(z=!1,R=(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(`option`,{value:``,children:`All tables (${k.length})`}),k.map(e=>(0,y.jsx)(`option`,{value:e.name,children:`${e.name} · ${e.columns.length}${e.columns.length===1?` col`:` cols`}`},e.name))]})):R=(0,y.jsx)(`option`,{value:``,children:`Select a connection first`});let B=(e,t)=>p(e,t.name,t.collection,t.connection_name),V;if(n.isPending)V=(0,y.jsx)(r,{});else if(!M.length)V=(0,y.jsx)(`div`,{className:`empty`,children:(0,y.jsx)(`strong`,{children:`No saved queries yet`})});else if(!I.length&&!L.length)V=(0,y.jsxs)(`div`,{className:`empty`,children:[(0,y.jsx)(`strong`,{children:`No scoped API keys or roles yet`}),(0,y.jsx)(`span`,{children:`Every query is reachable by the admin key only until a scoped key is created.`})]});else{let n=S.q.trim().toLowerCase(),i=[...M].sort((e,t)=>(e.collection||``)+e.name<(t.collection||``)+t.name?-1:1),a=e=>e.toLowerCase().includes(n),o=n?i.filter(e=>a(e.name)||e.collection&&a(e.collection)):i,s=n?I.filter(a):I,c=n?L.filter(a):L,l=o.length||s.length||c.length,u=o.length?o:l?i:[],d=s.length?s:l?I:[],p=c.length?c:l?L:[];if(S.conn&&(u=u.filter(e=>e.connection_name===S.conn)),S.table&&j===null)V=(0,y.jsx)(r,{text:`Checking which queries reference ${S.table}…`});else if(j&&(u=u.filter(e=>j.includes(e.name))),u.length){let n=u.map(e=>({q:e,dbType:D(e.connection_name),reach:d.filter(t=>B(P[t],e).length).length}));S.db&&(n=n.filter(e=>e.dbType===S.db)),S.reach===`reachable`?n=n.filter(e=>e.reach>0):S.reach===`unreachable`&&(n=n.filter(e=>e.reach===0));let r=S.sort;if(r.field){let e=e=>{switch(r.field){case`query`:return e.q.name.toLowerCase();case`database`:return(e.dbType||``).toLowerCase();case`created`:return e.q.created_at||``;case`modified`:return e.q.updated_at||``;case`lastUsed`:return e.q.last_used_at||``;case`version`:return e.q.latest_version;case`connection`:return(e.q.connection_name||``).toLowerCase();case`reach`:return e.reach;case`col`:{let t=r.colType===`key`?P[r.colName]:F[r.colName];return t&&B(t,e.q).length?1:0}default:return``}};n=[...n].sort((t,n)=>{let i=e(t),a=e(n);return i<a?-r.dir:i>a?r.dir:0})}V=n.length?(0,y.jsx)(E,{rows:n,keyCols:d,roleCols:p,keyMap:P,roleMap:F,st:S,onSort:e=>w({sort:e}),onOpen:t=>e(`/queries/${encodeURIComponent(t)}`),onInfo:e=>t({title:e.q.name,kicker:`${e.q.collection||`uncollected`} · ${e.q.version_count}${e.q.version_count===1?` version`:` versions`}`,content:(0,y.jsx)(f,{name:e.q.name,dbType:e.dbType})})}):(0,y.jsx)(`div`,{className:`empty`,children:(0,y.jsx)(`span`,{children:`No queries match these filters.`})})}else{let i=n?`Nothing matches “${n}”.`:S.table?`“${S.table}” on ${S.conn} is not exposed by any saved query yet.`:S.conn?`No saved query uses “${S.conn}” yet.`:`Nothing matches these filters.`;V=(0,y.jsxs)(`div`,{className:`empty`,children:[(0,y.jsx)(`span`,{children:i}),!n&&S.conn?(0,y.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:()=>{let n=D(S.conn)===`mongo`,i=S.table?n?JSON.stringify({collection:S.table,filter:{}},null,2):`SELECT * FROM `+S.table:void 0;e(`/queries`),t({title:`New API`,kicker:`POST /api/v1/queries`,content:(0,y.jsx)(v.Suspense,{fallback:(0,y.jsx)(r,{}),children:(0,y.jsx)(b,{starter:{connection:S.conn,sql:i}})})})},children:`New API`+(S.table?` on `+S.table:``)}):null]})}}return(0,y.jsxs)(y.Fragment,{children:[(0,y.jsxs)(`div`,{className:`page-head`,children:[(0,y.jsxs)(`div`,{className:`titles`,children:[(0,y.jsx)(`h1`,{children:`Access map`}),(0,y.jsxs)(`span`,{className:`sub`,children:[`Which API keys can reach which saved queries - the whole point of collections and connection grants, in one place.`,` `,(0,y.jsxs)(`span`,{className:`legend`,children:[(0,y.jsx)(`span`,{className:`amap-dot`,children:`Q`}),` named query \xA0`,(0,y.jsx)(`span`,{className:`amap-dot`,children:`C`}),` `,`collection \xA0`,(0,y.jsx)(`span`,{className:`amap-dot conn`,children:`W`}),` whole connection`]})]})]}),(0,y.jsx)(`span`,{className:`spacer`}),(0,y.jsxs)(`select`,{id:`accessmap-conn-filter`,"aria-label":`Connection`,value:T.some(e=>e.name===S.conn)?S.conn:``,onChange:e=>w({conn:e.target.value,table:``,tableMatches:null}),children:[(0,y.jsx)(`option`,{value:``,children:`All connections`}),[...T].sort((e,t)=>e.name<t.name?-1:1).map(e=>(0,y.jsx)(`option`,{value:e.name,children:e.name},e.name))]}),(0,y.jsx)(`select`,{id:`accessmap-table-filter`,"aria-label":`Table`,disabled:z,value:k.some(e=>e.name===S.table)?S.table:``,onChange:e=>w({table:e.target.value,tableMatches:null}),children:R}),(0,y.jsxs)(`select`,{id:`accessmap-db-filter`,"aria-label":`Database`,value:N.includes(S.db)?S.db:``,onChange:e=>w({db:e.target.value}),children:[(0,y.jsx)(`option`,{value:``,children:`All databases`}),N.map(e=>(0,y.jsx)(`option`,{value:e,children:e},e))]}),(0,y.jsxs)(`select`,{id:`accessmap-reach-filter`,"aria-label":`Reach`,value:S.reach,onChange:e=>w({reach:e.target.value}),children:[(0,y.jsx)(`option`,{value:``,children:`Any reach`}),(0,y.jsx)(`option`,{value:`reachable`,children:`Reachable by a key`}),(0,y.jsx)(`option`,{value:`unreachable`,children:`Reachable by no key`})]}),(0,y.jsx)(`input`,{id:`accessmap-filter`,className:`search`,type:`search`,placeholder:`Filter by query or key…`,value:S.q,onChange:e=>w({q:e.target.value})})]}),(0,y.jsx)(`div`,{id:`accessmap-body`,className:`panel`,children:V})]})}function E({rows:e,keyCols:t,roleCols:n,keyMap:r,roleMap:i,st:a,onSort:o,onOpen:s,onInfo:c}){let l=a.sort,u=(e,t=null,n=null)=>l.field===e&&l.colType===t&&l.colName===n,f=(e,t=null,n=null)=>u(e,t,n)?l.dir===1?` ▲`:` ▼`:``,m=(e,t=null,n=null)=>{let r=u(e,t,n);o(r&&l.dir===1?{field:e,colType:t,colName:n,dir:-1}:r?{field:null,colType:null,colName:null,dir:1}:{field:e,colType:t,colName:n,dir:1})},h=(e,t,n)=>(0,y.jsx)(`th`,{className:t,onClick:()=>m(n),children:e+f(n)},n),g=(e,t,n,r)=>(0,y.jsxs)(`th`,{className:`amap-key-head amap-sortable`+(n?` revoked`:``),title:n?e+n:e,onClick:()=>m(`col`,r,e),children:[(0,y.jsxs)(`span`,{className:`amap-key-name`,children:[e,f(`col`,r,e),n?(0,y.jsx)(`span`,{className:`dim`,children:n}):null]}),(0,y.jsxs)(`span`,{className:`amap-key-sub`,children:[(t.allow_writes?`Read/write`:`Read-only`)+` · `+(t.rate_limit||`server default`),t.mask?.length?(0,y.jsx)(`span`,{className:`tag`,title:t.mask.map(e=>`${e.column}: ${e.action}`).join(`, `),children:`masked`}):null]})]},r+`:`+e),_=(e,t,n,r)=>{let i=p(e,t.name,t.collection,t.connection_name);if(!i.length)return(0,y.jsx)(`td`,{className:`amap-cell`},r);let a=[...new Set(i.map(e=>x[e.kind]))],o=a.length===1&&a[0]===`W`;return(0,y.jsx)(`td`,{className:`amap-cell`,title:i.map(e=>e.label).join(`, `),children:(0,y.jsx)(`span`,{className:`amap-dot`+(n?` role`:``)+(o?` conn`:``),children:a.join(`·`)})},r)},v=w();return(0,y.jsx)(`div`,{style:{overflow:`auto`},children:(0,y.jsxs)(`table`,{className:`amap`,children:[(0,y.jsxs)(`thead`,{children:[(0,y.jsxs)(`tr`,{children:[(0,y.jsx)(`th`,{colSpan:3+S.length}),t.length?(0,y.jsx)(`th`,{colSpan:t.length,className:`amap-group`,children:`API KEYS`}):null,n.length?(0,y.jsx)(`th`,{colSpan:n.length,className:`amap-group role`,title:`What a key created from each role would reach - a role grants nothing on its own`,children:`ROLES`}):null]}),(0,y.jsxs)(`tr`,{children:[h(`Query`,`amap-query amap-sortable`,`query`),C.map((e,t)=>h(S[t],`amap-sortable`,e)),h(`Connection`,`amap-sortable`,`connection`),h(`Reach`,`num amap-sortable`,`reach`),t.map(e=>{let t=r[e],n=!!(t.expires_at&&t.expires_at<v);return g(e,t,t.active?n?` (expired ${t.expires_at})`:``:` (revoked)`,`key`)}),n.map(e=>g(e,i[e],``,`role`))]})]}),(0,y.jsx)(`tbody`,{children:e.map(e=>{let a=e.q;return(0,y.jsxs)(`tr`,{children:[(0,y.jsxs)(`td`,{className:`amap-query`,children:[(0,y.jsxs)(`div`,{className:`amap-q-row`,children:[(0,y.jsx)(`button`,{type:`button`,className:`amap-q-name`,title:`Open ${a.name} in API Repository`,onClick:()=>s(a.name),children:a.name}),(0,y.jsx)(`button`,{type:`button`,className:`amap-info`,title:`Details for ${a.name}`,"aria-label":`Details for ${a.name}`,onClick:()=>c(e),children:`i`})]}),a.collection?(0,y.jsx)(`span`,{className:`amap-coll`,children:a.collection}):null]}),(0,y.jsx)(`td`,{children:e.dbType?(0,y.jsx)(`span`,{className:`tag`,children:e.dbType}):(0,y.jsx)(`span`,{className:`dim`,children:`—`})}),(0,y.jsx)(`td`,{className:`mono dim`,children:(0,y.jsx)(d,{value:a.created_at})}),(0,y.jsx)(`td`,{className:`mono dim`,children:(0,y.jsx)(d,{value:a.updated_at})}),(0,y.jsx)(`td`,{className:a.last_used_at?`mono`:`mono dim`,children:(0,y.jsx)(d,{value:a.last_used_at,fallback:`never`})}),(0,y.jsx)(`td`,{className:`mono`,children:`v`+a.latest_version}),(0,y.jsx)(`td`,{className:`mono dim`,children:a.connection_name||`—`}),(0,y.jsx)(`td`,{className:`num`,title:`Reachable by an actual API key (roles alone reach nothing)`,children:e.reach?String(e.reach):`—`}),t.map(e=>_(r[e],a,!1,`k:`+e)),n.map(e=>_(i[e],a,!0,`r:`+e))]},a.name)})}),(0,y.jsx)(`tfoot`,{children:(0,y.jsxs)(`tr`,{children:[(0,y.jsx)(`td`,{className:`amap-query`,children:`Reaches`}),S.map(e=>(0,y.jsx)(`td`,{},e)),(0,y.jsx)(`td`,{}),(0,y.jsx)(`td`,{}),t.map(t=>(0,y.jsx)(`td`,{className:`num`,children:`${e.filter(e=>p(r[t],e.q.name,e.q.collection,e.q.connection_name).length).length} / ${e.length}`},t)),n.map(t=>(0,y.jsx)(`td`,{className:`num`,children:`${e.filter(e=>p(i[t],e.q.name,e.q.collection,e.q.connection_name).length).length} / ${e.length}`},t))]})})]})})}export{T as AccessMapPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{B as e,H as t,R as n,c as r,h as i,l as a,n as o,s,t as c,v as l,z as u}from"./client-ZHNiMG8H.js";import{t as d}from"./useMutation-Cheq0sq0.js";import{D as f,E as p,T as m,f as h,h as g,k as _,l as v}from"./index-uRArh6IJ.js";import{t as y}from"./Time-USlo65Ii.js";import{t as b}from"./forms-Bn5QJbOg.js";import{n as x,t as S}from"./icons-CFXE8hAz.js";var C=t(e(),1),w=u(),T=[{value:`owner`,label:`Owner`,text:`everything, including administrators`},{value:`admin`,label:`Admin`,text:`connections, API keys and roles, queries, cache, settings`},{value:`developer`,label:`Developer`,text:`saved queries and SQL; no keys, no connection changes`},{value:`auditor`,label:`Auditor`,text:`read only - audit log, history, configuration; no SQL`}],E=e=>e===null?`Workspaces only`:T.find(t=>t.value===e)?.label??e,D=90,O=e=>new Date(Date.now()+e*864e5).toISOString().slice(0,10),k=`Store this now — it cannot be shown again. Paste it into the API key box at the bottom of the sidebar, or send it as the X-API-Key header.`;function A(){let{showError:e}=_();return t=>t instanceof c?e(t.message,{status:t.status}):e(t.message)}function j(e){let t=h(`admins.read`,`other administrators`);return l({queryKey:[`administrators`],queryFn:async()=>(await t.check(),s(await o.GET(`/api/v1/administrators`)).items),enabled:e,retry:!1})}function M(e){return l({queryKey:[`administrators`,e,`tokens`],queryFn:async()=>s(await o.GET(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:e}}})).items,retry:!1})}function N(){let e=g(),t=v(),n=!!e.data&&t(`admins.read`),r=j(n),{openDrawer:i}=_(),a=()=>i({title:`New administrator`,content:(0,w.jsx)(I,{})}),o;return o=e.isPending?(0,w.jsx)(f,{}):e.isError?(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`strong`,{children:`Not signed in as an administrator`}),(0,w.jsx)(`span`,{children:`Apply an admin token (qagadm_…) or the shared admin key in the box at the bottom of the sidebar.`})]}):n?r.isPending?(0,w.jsx)(f,{text:`Loading administrators…`}):r.isError?(0,w.jsx)(`div`,{className:`hint`,children:r.error.message}):r.data.length?(0,w.jsx)(P,{items:r.data,self:e.data.via===`token`||e.data.via===`sso`?e.data.name:null}):(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`strong`,{children:`No administrators yet`}),(0,w.jsx)(`span`,{children:`Everyone shares QUERYAPIGATE_API_KEY today, so the audit log can only say “admin”. Give each person their own sign-in, with a role that fits what they do — then the shared key becomes a break-glass key.`}),(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:a,children:`New administrator`})]}):(0,w.jsx)(F,{name:e.data.name??``,role:e.data.role}),(0,w.jsxs)(w.Fragment,{children:[(0,w.jsxs)(`div`,{className:`page-head`,children:[(0,w.jsxs)(`div`,{className:`titles`,children:[(0,w.jsx)(`h1`,{children:`Administrators`}),(0,w.jsx)(`span`,{className:`sub`,id:`administrators-sub`,children:n?`The people and pipelines that manage this server, each with a role and their own tokens.`:`Your account and your admin tokens.`})]}),(0,w.jsx)(`span`,{className:`spacer`}),n&&(0,w.jsx)(`button`,{id:`new-administrator`,type:`button`,className:`btn primary`,onClick:a,children:`New administrator`})]}),(0,w.jsx)(`div`,{id:`administrators-table`,className:`panel`,children:o})]})}function P({items:e,self:t}){let{openDrawer:i,toast:a}=_(),s=A(),c=n(),l=async e=>{if(window.confirm(`Remove administrator '${e}'? Every token of theirs stops working immediately.`))try{r(await o.DELETE(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),a(`Removed `+e),c.invalidateQueries({queryKey:[`administrators`]})}catch(e){s(e)}};return(0,w.jsx)(`div`,{style:{overflowX:`auto`},children:(0,w.jsxs)(`table`,{className:`grid`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsx)(`tr`,{children:[`Name`,`Role`,`Email`,`Last seen`,`Tokens`,``].map((e,t)=>(0,w.jsx)(`th`,{children:e},t))})}),(0,w.jsx)(`tbody`,{children:e.map(e=>(0,w.jsxs)(`tr`,{"data-name":e.name,children:[(0,w.jsx)(`td`,{children:(0,w.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,w.jsx)(`span`,{className:`dot ${e.active?`ok`:`off`}`,title:e.active?`Active`:`Inactive`}),(0,w.jsx)(`span`,{className:`name`,children:e.name}),e.name===t&&(0,w.jsx)(`span`,{className:`tag`,children:`you`}),e.sso?.status===`linked`&&(0,w.jsx)(`span`,{className:`tag`,title:`Signs in through ${e.sso.issuer??`SSO`}; role from their groups`,children:`SSO`}),e.sso?.status===`pending`&&(0,w.jsx)(`span`,{className:`tag`,title:`Linked at their next SSO sign-in, by their verified email`,children:`SSO pending`})]})}),(0,w.jsx)(`td`,{children:E(e.role)}),(0,w.jsx)(`td`,{className:e.email?void 0:`dim`,children:e.email??`—`}),(0,w.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,w.jsx)(y,{value:e.last_seen_at,fallback:`never`})}),(0,w.jsx)(`td`,{className:`mono`,children:e.tokens}),(0,w.jsx)(`td`,{children:(0,w.jsxs)(`div`,{className:`actions`,children:[(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>i({title:`Admin tokens`,kicker:e.name,wide:!0,content:(0,w.jsx)(R,{name:e.name})}),children:`Tokens`}),(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm icon`,title:`Edit ${e.name}`,"aria-label":`Edit`,onClick:()=>i({title:`Edit administrator`,kicker:e.name,content:(0,w.jsx)(I,{name:e.name})}),children:(0,w.jsx)(S,{})}),(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm icon danger`,title:`Remove ${e.name}`,"aria-label":`Remove`,onClick:()=>void l(e.name),children:(0,w.jsx)(x,{})})]})})]},e.name))})]})})}function F({name:e,role:t}){return(0,w.jsxs)(`div`,{className:`form`,style:{padding:16},children:[(0,w.jsxs)(`div`,{children:[`Signed in as `,(0,w.jsx)(`strong`,{children:e}),` · `,E(t),` — `,T.find(e=>e.value===t)?.text,`.`]}),(0,w.jsx)(R,{name:e})]})}function I({name:e}){let t=l({queryKey:[`administrators`,`detail`,e],queryFn:async()=>a(await o.GET(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),enabled:!!e,retry:!1,gcTime:0});return e&&t.isPending?(0,w.jsx)(f,{}):e&&t.isError?(0,w.jsx)(`div`,{className:`hint`,children:t.error.message}):(0,w.jsx)(L,{name:e,existing:t.data?.data,etag:t.data?.etag??null},e??``)}function L({name:e,existing:t,etag:r}){let a=!!e,{closeDrawer:c,openDrawer:l,showError:u,toast:f}=_(),h=A(),g=n(),[v,y]=(0,C.useState)(e??``),[x,S]=(0,C.useState)(t?.role??`developer`),[E,j]=(0,C.useState)(t?.email??``),[M,N]=(0,C.useState)(!t||t.active),P=i(),F=t?.sso?.status===`linked`,[I,L]=(0,C.useState)(t?.sso?.status??`none`),R=d({mutationFn:async()=>{let n={role:x,email:E.trim()||null};if(a)return s(await o.PATCH(`/api/v1/administrators/{name}`,{params:{path:{name:e},header:r?{"If-Match":r}:{}},body:{...F?{email:n.email}:n,active:M,...I===(t?.sso?.status??`none`)?{}:{sso:I===`pending`?`pending`:null}}})),null;let i=s(await o.POST(`/api/v1/administrators`,{body:{name:v.trim(),...n}}));return s(await o.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:i.name}},body:{label:`first token`,expires_at:O(D)}}))},onSuccess:t=>{if(u(``),g.invalidateQueries({queryKey:[`administrators`]}),!t){c(),f(`Updated `+e);return}l({title:`Administrator created`,kicker:t.admin,content:(0,w.jsx)(b,{secret:t.secret,label:`Admin token for ${t.admin} (expires ${t.expires_at})`,hint:k})})},onError:h});return(0,w.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{if(e.preventDefault(),!a&&!v.trim()){u(`A name is required.`,{errors:{name:`This field is required`}});return}R.mutate()},children:[(0,w.jsx)(m,{id:`a-name`,label:`Name`,hint:a?void 0:`A person (alice) or a pipeline (ci-deploy). Shown in the audit log.`,children:(0,w.jsx)(`input`,{id:`a-name`,value:v,placeholder:`alice`,autoComplete:`off`,spellCheck:!1,disabled:a,autoFocus:!a,onChange:e=>y(e.target.value)})}),(0,w.jsx)(m,{id:`a-role`,label:`Role`,hint:F?`From their groups at the identity provider, at every sign-in - change it there.`:T.find(e=>e.value===x)?.text,children:(0,w.jsx)(`select`,{id:`a-role`,value:x,disabled:F,onChange:e=>S(e.target.value),children:T.map(e=>(0,w.jsx)(`option`,{value:e.value,children:e.label},e.value))})}),(0,w.jsx)(m,{id:`a-email`,label:`Email`,hint:`Optional.`,children:(0,w.jsx)(`input`,{id:`a-email`,type:`email`,value:E,autoComplete:`off`,onChange:e=>j(e.target.value)})}),a&&(0,w.jsxs)(`label`,{className:`switch`,children:[(0,w.jsx)(`input`,{id:`a-active`,type:`checkbox`,checked:M,onChange:e=>N(e.target.checked)}),`Active`,(0,w.jsx)(`span`,{className:`hint`,children:`— unchecking stops every token of theirs immediately`})]}),a&&P.data?.sso&&(0,w.jsx)(m,{id:`a-sso`,label:`Sign-in through SSO`,hint:I===`linked`?`They sign in through the identity provider. Unlinking ends their sessions; their next SSO sign-in makes a new administrator.`:`Pending: their next SSO sign-in, with a verified email matching the one above, is bound to this account - once.`,children:(0,w.jsxs)(`select`,{id:`a-sso`,value:I,onChange:e=>L(e.target.value),children:[F&&(0,w.jsx)(`option`,{value:`linked`,children:`Linked`}),!F&&(0,w.jsx)(`option`,{value:`pending`,children:`Link at their next sign-in`}),(0,w.jsx)(`option`,{value:`none`,children:F?`Unlink`:`Not through SSO`})]})}),!a&&(0,w.jsxs)(`div`,{className:`hint`,children:[`Their first admin token, valid for `,D,` days, is shown once after you create them.`]}),(0,w.jsx)(p,{onCancel:c,children:(0,w.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:R.isPending,children:a?`Save`:`Create`})})]})}function R({name:e}){let t=M(e),i=n(),{toast:a}=_(),c=A(),[l,u]=(0,C.useState)(``),[p,h]=(0,C.useState)(O(D)),[g,v]=(0,C.useState)(null),x=()=>void i.invalidateQueries({queryKey:[`administrators`]}),S=d({mutationFn:async()=>s(await o.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:e}},body:{label:l.trim()||null,expires_at:p||null}})),onSuccess:e=>{v(e),u(``),x()},onError:c}),T=async t=>{if(window.confirm(`Revoke ${t.label?`'${t.label}'`:t.id}? Anything using it stops working now.`))try{r(await o.DELETE(`/api/v1/administrators/{name}/tokens/{token_id}`,{params:{path:{name:e,token_id:t.id}}})),a(`Revoked `+(t.label??t.id)),x()}catch(e){c(e)}},E=new Date().toISOString().slice(0,10);return(0,w.jsxs)(`div`,{className:`form`,id:`admin-tokens`,children:[t.isPending?(0,w.jsx)(f,{text:`Loading tokens…`}):t.isError?(0,w.jsx)(`div`,{className:`hint`,children:t.error.message}):t.data.length?(0,w.jsxs)(`table`,{className:`grid`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsx)(`tr`,{children:[`Label`,`Expires`,`Last used`,``].map((e,t)=>(0,w.jsx)(`th`,{children:e},t))})}),(0,w.jsx)(`tbody`,{children:t.data.map(e=>(0,w.jsxs)(`tr`,{"data-token":e.id,children:[(0,w.jsx)(`td`,{title:e.id,children:e.label??(0,w.jsx)(`span`,{className:`dim mono`,children:e.id})}),(0,w.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:e.expires_at?(0,w.jsx)(`span`,{style:e.expired?{color:`var(--danger)`}:void 0,children:e.expires_at+(e.expired?` (expired)`:e.expires_at===E?` (today)`:``)}):(0,w.jsx)(`span`,{className:`dim`,children:`never`})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(y,{value:e.last_used_at,fallback:`never`})}),(0,w.jsx)(`td`,{children:(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm danger`,onClick:()=>void T(e),children:`Revoke`})})]},e.id))})]}):(0,w.jsx)(`div`,{className:`hint`,children:`No tokens. Issue one below to sign in with.`}),g?(0,w.jsx)(b,{secret:g.secret,label:`New token${g.label?` '${g.label}'`:``} (${g.expires_at?`expires ${g.expires_at}`:`never expires`})`,hint:k,onDone:()=>v(null)}):(0,w.jsxs)(`form`,{className:`grid2`,noValidate:!0,onSubmit:e=>{e.preventDefault(),S.mutate()},children:[(0,w.jsx)(m,{id:`t-label`,label:`New token: label`,hint:`What it's for - laptop, ci.`,children:(0,w.jsx)(`input`,{id:`t-label`,value:l,autoComplete:`off`,onChange:e=>u(e.target.value)})}),(0,w.jsx)(m,{id:`t-expires`,label:`Expires`,hint:`Leave blank for no expiry.`,children:(0,w.jsx)(`input`,{id:`t-expires`,type:`date`,value:p,onChange:e=>h(e.target.value)})}),(0,w.jsx)(`div`,{children:(0,w.jsx)(`button`,{type:`submit`,className:`btn`,disabled:S.isPending,children:`Issue token`})})]})]})}export{N as AdministratorsPage};
|
|
1
|
+
import{B as e,H as t,R as n,c as r,h as i,l as a,n as o,s,t as c,v as l,z as u}from"./client-ZHNiMG8H.js";import{t as d}from"./useMutation-BHDQeA9M.js";import{D as f,E as p,T as m,f as h,h as g,k as _,l as v}from"./index-DBrO0zMK.js";import{t as y}from"./Time-DE-u5yL9.js";import{t as b}from"./forms-BOFRAueI.js";import{n as x,t as S}from"./icons-CFXE8hAz.js";var C=t(e(),1),w=u(),T=[{value:`owner`,label:`Owner`,text:`everything, including administrators`},{value:`admin`,label:`Admin`,text:`connections, API keys and roles, queries, cache, settings`},{value:`developer`,label:`Developer`,text:`saved queries and SQL; no keys, no connection changes`},{value:`auditor`,label:`Auditor`,text:`read only - audit log, history, configuration; no SQL`}],E=e=>e===null?`Workspaces only`:T.find(t=>t.value===e)?.label??e,D=90,O=e=>new Date(Date.now()+e*864e5).toISOString().slice(0,10),k=`Store this now — it cannot be shown again. Paste it into the API key box at the bottom of the sidebar, or send it as the X-API-Key header.`;function A(){let{showError:e}=_();return t=>t instanceof c?e(t.message,{status:t.status}):e(t.message)}function j(e){let t=h(`admins.read`,`other administrators`);return l({queryKey:[`administrators`],queryFn:async()=>(await t.check(),s(await o.GET(`/api/v1/administrators`)).items),enabled:e,retry:!1})}function M(e){return l({queryKey:[`administrators`,e,`tokens`],queryFn:async()=>s(await o.GET(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:e}}})).items,retry:!1})}function N(){let e=g(),t=v(),n=!!e.data&&t(`admins.read`),r=j(n),{openDrawer:i}=_(),a=()=>i({title:`New administrator`,content:(0,w.jsx)(I,{})}),o;return o=e.isPending?(0,w.jsx)(f,{}):e.isError?(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`strong`,{children:`Not signed in as an administrator`}),(0,w.jsx)(`span`,{children:`Apply an admin token (qagadm_…) or the shared admin key in the box at the bottom of the sidebar.`})]}):n?r.isPending?(0,w.jsx)(f,{text:`Loading administrators…`}):r.isError?(0,w.jsx)(`div`,{className:`hint`,children:r.error.message}):r.data.length?(0,w.jsx)(P,{items:r.data,self:e.data.via===`token`||e.data.via===`sso`?e.data.name:null}):(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`strong`,{children:`No administrators yet`}),(0,w.jsx)(`span`,{children:`Everyone shares QUERYAPIGATE_API_KEY today, so the audit log can only say “admin”. Give each person their own sign-in, with a role that fits what they do — then the shared key becomes a break-glass key.`}),(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:a,children:`New administrator`})]}):(0,w.jsx)(F,{name:e.data.name??``,role:e.data.role}),(0,w.jsxs)(w.Fragment,{children:[(0,w.jsxs)(`div`,{className:`page-head`,children:[(0,w.jsxs)(`div`,{className:`titles`,children:[(0,w.jsx)(`h1`,{children:`Administrators`}),(0,w.jsx)(`span`,{className:`sub`,id:`administrators-sub`,children:n?`The people and pipelines that manage this server, each with a role and their own tokens.`:`Your account and your admin tokens.`})]}),(0,w.jsx)(`span`,{className:`spacer`}),n&&(0,w.jsx)(`button`,{id:`new-administrator`,type:`button`,className:`btn primary`,onClick:a,children:`New administrator`})]}),(0,w.jsx)(`div`,{id:`administrators-table`,className:`panel`,children:o})]})}function P({items:e,self:t}){let{openDrawer:i,toast:a}=_(),s=A(),c=n(),l=async e=>{if(window.confirm(`Remove administrator '${e}'? Every token of theirs stops working immediately.`))try{r(await o.DELETE(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),a(`Removed `+e),c.invalidateQueries({queryKey:[`administrators`]})}catch(e){s(e)}};return(0,w.jsx)(`div`,{style:{overflowX:`auto`},children:(0,w.jsxs)(`table`,{className:`grid`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsx)(`tr`,{children:[`Name`,`Role`,`Email`,`Last seen`,`Tokens`,``].map((e,t)=>(0,w.jsx)(`th`,{children:e},t))})}),(0,w.jsx)(`tbody`,{children:e.map(e=>(0,w.jsxs)(`tr`,{"data-name":e.name,children:[(0,w.jsx)(`td`,{children:(0,w.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,w.jsx)(`span`,{className:`dot ${e.active?`ok`:`off`}`,title:e.active?`Active`:`Inactive`}),(0,w.jsx)(`span`,{className:`name`,children:e.name}),e.name===t&&(0,w.jsx)(`span`,{className:`tag`,children:`you`}),e.sso?.status===`linked`&&(0,w.jsx)(`span`,{className:`tag`,title:`Signs in through ${e.sso.issuer??`SSO`}; role from their groups`,children:`SSO`}),e.sso?.status===`pending`&&(0,w.jsx)(`span`,{className:`tag`,title:`Linked at their next SSO sign-in, by their verified email`,children:`SSO pending`})]})}),(0,w.jsx)(`td`,{children:E(e.role)}),(0,w.jsx)(`td`,{className:e.email?void 0:`dim`,children:e.email??`—`}),(0,w.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,w.jsx)(y,{value:e.last_seen_at,fallback:`never`})}),(0,w.jsx)(`td`,{className:`mono`,children:e.tokens}),(0,w.jsx)(`td`,{children:(0,w.jsxs)(`div`,{className:`actions`,children:[(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>i({title:`Admin tokens`,kicker:e.name,wide:!0,content:(0,w.jsx)(R,{name:e.name})}),children:`Tokens`}),(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm icon`,title:`Edit ${e.name}`,"aria-label":`Edit`,onClick:()=>i({title:`Edit administrator`,kicker:e.name,content:(0,w.jsx)(I,{name:e.name})}),children:(0,w.jsx)(S,{})}),(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm icon danger`,title:`Remove ${e.name}`,"aria-label":`Remove`,onClick:()=>void l(e.name),children:(0,w.jsx)(x,{})})]})})]},e.name))})]})})}function F({name:e,role:t}){return(0,w.jsxs)(`div`,{className:`form`,style:{padding:16},children:[(0,w.jsxs)(`div`,{children:[`Signed in as `,(0,w.jsx)(`strong`,{children:e}),` · `,E(t),` — `,T.find(e=>e.value===t)?.text,`.`]}),(0,w.jsx)(R,{name:e})]})}function I({name:e}){let t=l({queryKey:[`administrators`,`detail`,e],queryFn:async()=>a(await o.GET(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),enabled:!!e,retry:!1,gcTime:0});return e&&t.isPending?(0,w.jsx)(f,{}):e&&t.isError?(0,w.jsx)(`div`,{className:`hint`,children:t.error.message}):(0,w.jsx)(L,{name:e,existing:t.data?.data,etag:t.data?.etag??null},e??``)}function L({name:e,existing:t,etag:r}){let a=!!e,{closeDrawer:c,openDrawer:l,showError:u,toast:f}=_(),h=A(),g=n(),[v,y]=(0,C.useState)(e??``),[x,S]=(0,C.useState)(t?.role??`developer`),[E,j]=(0,C.useState)(t?.email??``),[M,N]=(0,C.useState)(!t||t.active),P=i(),F=t?.sso?.status===`linked`,[I,L]=(0,C.useState)(t?.sso?.status??`none`),R=d({mutationFn:async()=>{let n={role:x,email:E.trim()||null};if(a)return s(await o.PATCH(`/api/v1/administrators/{name}`,{params:{path:{name:e},header:r?{"If-Match":r}:{}},body:{...F?{email:n.email}:n,active:M,...I===(t?.sso?.status??`none`)?{}:{sso:I===`pending`?`pending`:null}}})),null;let i=s(await o.POST(`/api/v1/administrators`,{body:{name:v.trim(),...n}}));return s(await o.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:i.name}},body:{label:`first token`,expires_at:O(D)}}))},onSuccess:t=>{if(u(``),g.invalidateQueries({queryKey:[`administrators`]}),!t){c(),f(`Updated `+e);return}l({title:`Administrator created`,kicker:t.admin,content:(0,w.jsx)(b,{secret:t.secret,label:`Admin token for ${t.admin} (expires ${t.expires_at})`,hint:k})})},onError:h});return(0,w.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{if(e.preventDefault(),!a&&!v.trim()){u(`A name is required.`,{errors:{name:`This field is required`}});return}R.mutate()},children:[(0,w.jsx)(m,{id:`a-name`,label:`Name`,hint:a?void 0:`A person (alice) or a pipeline (ci-deploy). Shown in the audit log.`,children:(0,w.jsx)(`input`,{id:`a-name`,value:v,placeholder:`alice`,autoComplete:`off`,spellCheck:!1,disabled:a,autoFocus:!a,onChange:e=>y(e.target.value)})}),(0,w.jsx)(m,{id:`a-role`,label:`Role`,hint:F?`From their groups at the identity provider, at every sign-in - change it there.`:T.find(e=>e.value===x)?.text,children:(0,w.jsx)(`select`,{id:`a-role`,value:x,disabled:F,onChange:e=>S(e.target.value),children:T.map(e=>(0,w.jsx)(`option`,{value:e.value,children:e.label},e.value))})}),(0,w.jsx)(m,{id:`a-email`,label:`Email`,hint:`Optional.`,children:(0,w.jsx)(`input`,{id:`a-email`,type:`email`,value:E,autoComplete:`off`,onChange:e=>j(e.target.value)})}),a&&(0,w.jsxs)(`label`,{className:`switch`,children:[(0,w.jsx)(`input`,{id:`a-active`,type:`checkbox`,checked:M,onChange:e=>N(e.target.checked)}),`Active`,(0,w.jsx)(`span`,{className:`hint`,children:`— unchecking stops every token of theirs immediately`})]}),a&&P.data?.sso&&(0,w.jsx)(m,{id:`a-sso`,label:`Sign-in through SSO`,hint:I===`linked`?`They sign in through the identity provider. Unlinking ends their sessions; their next SSO sign-in makes a new administrator.`:`Pending: their next SSO sign-in, with a verified email matching the one above, is bound to this account - once.`,children:(0,w.jsxs)(`select`,{id:`a-sso`,value:I,onChange:e=>L(e.target.value),children:[F&&(0,w.jsx)(`option`,{value:`linked`,children:`Linked`}),!F&&(0,w.jsx)(`option`,{value:`pending`,children:`Link at their next sign-in`}),(0,w.jsx)(`option`,{value:`none`,children:F?`Unlink`:`Not through SSO`})]})}),!a&&(0,w.jsxs)(`div`,{className:`hint`,children:[`Their first admin token, valid for `,D,` days, is shown once after you create them.`]}),(0,w.jsx)(p,{onCancel:c,children:(0,w.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:R.isPending,children:a?`Save`:`Create`})})]})}function R({name:e}){let t=M(e),i=n(),{toast:a}=_(),c=A(),[l,u]=(0,C.useState)(``),[p,h]=(0,C.useState)(O(D)),[g,v]=(0,C.useState)(null),x=()=>void i.invalidateQueries({queryKey:[`administrators`]}),S=d({mutationFn:async()=>s(await o.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:e}},body:{label:l.trim()||null,expires_at:p||null}})),onSuccess:e=>{v(e),u(``),x()},onError:c}),T=async t=>{if(window.confirm(`Revoke ${t.label?`'${t.label}'`:t.id}? Anything using it stops working now.`))try{r(await o.DELETE(`/api/v1/administrators/{name}/tokens/{token_id}`,{params:{path:{name:e,token_id:t.id}}})),a(`Revoked `+(t.label??t.id)),x()}catch(e){c(e)}},E=new Date().toISOString().slice(0,10);return(0,w.jsxs)(`div`,{className:`form`,id:`admin-tokens`,children:[t.isPending?(0,w.jsx)(f,{text:`Loading tokens…`}):t.isError?(0,w.jsx)(`div`,{className:`hint`,children:t.error.message}):t.data.length?(0,w.jsxs)(`table`,{className:`grid`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsx)(`tr`,{children:[`Label`,`Expires`,`Last used`,``].map((e,t)=>(0,w.jsx)(`th`,{children:e},t))})}),(0,w.jsx)(`tbody`,{children:t.data.map(e=>(0,w.jsxs)(`tr`,{"data-token":e.id,children:[(0,w.jsx)(`td`,{title:e.id,children:e.label??(0,w.jsx)(`span`,{className:`dim mono`,children:e.id})}),(0,w.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:e.expires_at?(0,w.jsx)(`span`,{style:e.expired?{color:`var(--danger)`}:void 0,children:e.expires_at+(e.expired?` (expired)`:e.expires_at===E?` (today)`:``)}):(0,w.jsx)(`span`,{className:`dim`,children:`never`})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(y,{value:e.last_used_at,fallback:`never`})}),(0,w.jsx)(`td`,{children:(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm danger`,onClick:()=>void T(e),children:`Revoke`})})]},e.id))})]}):(0,w.jsx)(`div`,{className:`hint`,children:`No tokens. Issue one below to sign in with.`}),g?(0,w.jsx)(b,{secret:g.secret,label:`New token${g.label?` '${g.label}'`:``} (${g.expires_at?`expires ${g.expires_at}`:`never expires`})`,hint:k,onDone:()=>v(null)}):(0,w.jsxs)(`form`,{className:`grid2`,noValidate:!0,onSubmit:e=>{e.preventDefault(),S.mutate()},children:[(0,w.jsx)(m,{id:`t-label`,label:`New token: label`,hint:`What it's for - laptop, ci.`,children:(0,w.jsx)(`input`,{id:`t-label`,value:l,autoComplete:`off`,onChange:e=>u(e.target.value)})}),(0,w.jsx)(m,{id:`t-expires`,label:`Expires`,hint:`Leave blank for no expiry.`,children:(0,w.jsx)(`input`,{id:`t-expires`,type:`date`,value:p,onChange:e=>h(e.target.value)})}),(0,w.jsx)(`div`,{children:(0,w.jsx)(`button`,{type:`submit`,className:`btn`,disabled:S.isPending,children:`Issue token`})})]})]})}export{N as AdministratorsPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{B as e,H as t,n,s as r,v as i,z as a}from"./client-ZHNiMG8H.js";import{D as o}from"./index-
|
|
1
|
+
import{B as e,H as t,n,s as r,v as i,z as a}from"./client-ZHNiMG8H.js";import{D as o}from"./index-DBrO0zMK.js";import{t as s}from"./Time-DE-u5yL9.js";import{t as c}from"./CodeBox-Cwh5KHLp.js";var l=t(e(),1),u=a(),d=[{id:`1h`,label:`Last hour`,hours:1},{id:`24h`,label:`Last 24 hours`,hours:24},{id:`7d`,label:`Last 7 days`,hours:168},{id:`30d`,label:`Last 30 days`,hours:720}],f=e=>e.toLocaleString();function p({refusals:e}){let t=Object.entries(e);return t.length?(0,u.jsx)(`span`,{style:{display:`inline-flex`,flexWrap:`wrap`,gap:4},children:t.map(([e,t])=>(0,u.jsx)(`span`,{className:`tag`,title:`${t} call${t===1?``:`s`} failed with ${e}`,children:`${e} × ${t}`},e))}):(0,u.jsx)(`span`,{className:`dim`,children:`none`})}function m({writes:e}){let t=Object.entries(e??{}).filter(([,e])=>e).map(([e,t])=>`${t} ${e}`);return(0,u.jsx)(`div`,{className:`hint`,children:t.length?t.join(` · `):`None.`})}function h({agent:e}){let t=Object.entries(e.queries);return(0,u.jsxs)(`div`,{className:`agent-detail`,children:[(0,u.jsx)(`p`,{className:`sub-h`,children:`Writes asked for`}),(0,u.jsx)(m,{writes:e.writes}),(0,u.jsx)(`p`,{className:`sub-h`,style:{marginTop:12},children:`Saved queries`}),t.length?(0,u.jsx)(`div`,{className:`hint`,children:t.map(([e,t])=>`${e} × ${t}`).join(` · `)}):(0,u.jsx)(`div`,{className:`hint`,children:`None - only ad-hoc SQL.`}),(0,u.jsx)(`p`,{className:`sub-h`,style:{marginTop:12},children:`Most frequent ad-hoc statements`}),e.top_statements.length?e.top_statements.map((e,t)=>(0,u.jsxs)(`div`,{className:`agent-statement`,children:[(0,u.jsx)(`div`,{className:`hint`,children:`${e.count} call${e.count===1?``:`s`} · ${f(e.rows)} rows`+(e.errors?` · ${e.errors} failed`:``)}),e.sql?(0,u.jsx)(c,{sql:e.sql}):(0,u.jsx)(`span`,{className:`mono dim`,children:`sha256 ${e.sql_sha256}`})]},t)):(0,u.jsx)(`div`,{className:`hint`,children:e.adhoc_calls?`Run history keeps no SQL text here (QUERYAPIGATE_HISTORY_ADHOC_SQL is none).`:`No ad-hoc SQL.`})]})}function g(){let[e,t]=(0,l.useState)(`24h`),[a,c]=(0,l.useState)(null),m=d.find(t=>t.id===e).hours,g=i({queryKey:[`usage`,`agents`,e],queryFn:async()=>r(await n.GET(`/api/v1/usage/agents`,{params:{query:{hours:m}}})),retry:!1}),_=g.data?.items??[],v;v=g.isPending?(0,u.jsx)(o,{text:`Loading agents…`}):g.isError?(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`Couldn’t load agent usage`}),(0,u.jsx)(`span`,{children:g.error.message})]}):_.length?(0,u.jsx)(`div`,{style:{overflowX:`auto`},children:(0,u.jsxs)(`table`,{className:`grid`,children:[(0,u.jsx)(`thead`,{children:(0,u.jsx)(`tr`,{children:[`Agent`,`Calls`,`Rows`,`Failed`,`Masked`,`Refused or failed with`,`Last seen`].map(e=>(0,u.jsx)(`th`,{children:e},e))})}),(0,u.jsx)(`tbody`,{children:_.map(e=>(0,u.jsxs)(l.Fragment,{children:[(0,u.jsxs)(`tr`,{children:[(0,u.jsxs)(`td`,{children:[(0,u.jsx)(`button`,{type:`button`,className:`agent-name`,"aria-expanded":a===e.agent,onClick:()=>c(a===e.agent?null:e.agent),children:e.agent}),(0,u.jsx)(`div`,{className:`dim`,style:{fontSize:12},children:e.connections.join(`, `)})]}),(0,u.jsx)(`td`,{className:`num`,children:f(e.calls)}),(0,u.jsx)(`td`,{className:`num`,children:f(e.rows)}),(0,u.jsx)(`td`,{className:`num`,children:f(e.errors)}),(0,u.jsx)(`td`,{className:`num`,children:f(e.masked_calls)}),(0,u.jsx)(`td`,{children:(0,u.jsx)(p,{refusals:e.refusals})}),(0,u.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,u.jsx)(s,{value:e.last_seen,fallback:``})})]}),a===e.agent&&(0,u.jsx)(`tr`,{children:(0,u.jsx)(`td`,{colSpan:7,children:(0,u.jsx)(h,{agent:e})})})]},e.agent))})]})}):(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`No agent called the MCP server in this window`}),(0,u.jsx)(`span`,{children:`Runs over MCP - saved-query tools and execute_sql - appear here, per API key or signed-in user.`})]});let y=[];return g.data&&g.data.sample_rate<1&&y.push(`Run history keeps ${Math.round(g.data.sample_rate*100)}% of successful runs, so successful calls and rows are a sample; failures are all here.`),g.data?.truncated&&y.push(`More runs than one summary reads (100,000) - narrow the window.`),(0,u.jsxs)(u.Fragment,{children:[(0,u.jsxs)(`div`,{className:`page-head`,children:[(0,u.jsxs)(`div`,{className:`titles`,children:[(0,u.jsx)(`h1`,{children:`Agents`}),(0,u.jsx)(`span`,{className:`sub`,children:`What each AI agent did over MCP - per API key or signed-in user, from run history.`})]}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>void g.refetch(),children:`Refresh`})]}),(0,u.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,u.jsxs)(`div`,{className:`panel-bar`,children:[(0,u.jsx)(`select`,{"aria-label":`Window`,value:e,onChange:e=>t(e.target.value),children:d.map(e=>(0,u.jsx)(`option`,{value:e.id,children:e.label},e.id))}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`span`,{className:`panel-count`,children:g.data?`${_.length} agent${_.length===1?``:`s`}`:``})]}),y.map(e=>(0,u.jsx)(`div`,{className:`hint`,style:{padding:`6px 12px`},children:e},e)),v]})]})}export{g as AgentsPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{B as e,H as t,z as n}from"./client-ZHNiMG8H.js";import{C as r,D as i,M as a,N as o,S as s,x as c,y as l}from"./index-
|
|
1
|
+
import{B as e,H as t,z as n}from"./client-ZHNiMG8H.js";import{C as r,D as i,M as a,N as o,S as s,x as c,y as l}from"./index-DBrO0zMK.js";import{t as u}from"./Time-DE-u5yL9.js";import{n as d}from"./state-O13cl7cO.js";var f=t(e(),1),p=n(),m=[{id:`critical`,label:`Critical`},{id:`warning`,label:`Warning`},{id:`info`,label:`For your information`}],h={key:`Open API keys`,query:`Open query`,connection:`Open connections`,settings:`Open settings`,export:`Open exports`,agent:`Open agents`,approvals:`Open approvals`,audit:`Open audit log`};function g(){let e=a();return t=>{t.type===`query`?e(`/queries/${encodeURIComponent(t.name)}`):t.type===`key`?e(`/api-keys`):t.type===`connection`?e(`/connections`):t.type===`export`?e(`/exports`):t.type===`agent`?e(`/agents`):t.type===`approvals`?e(`/approvals`):t.type===`audit`?e(`/audit-log`):(d(t.name),e(`/settings`))}}function _({alert:e,dismissed:t}){let n=g();return(0,p.jsxs)(`div`,{className:`alert-row `+e.severity,"data-alert":e.id,children:[(0,p.jsx)(`span`,{className:`sev `+e.severity,children:e.severity}),(0,p.jsxs)(`div`,{className:`alert-what`,children:[(0,p.jsx)(`strong`,{children:e.title}),(0,p.jsx)(`span`,{className:`alert-detail`,children:e.detail}),e.since&&!e.detail.includes(e.since)&&(0,p.jsxs)(`span`,{className:`alert-since`,children:[e.kind===`key_expiring`?`Expires `:`Since `,(0,p.jsx)(u,{value:e.since})]})]}),(0,p.jsxs)(`div`,{className:`actions`,children:[e.target&&(0,p.jsx)(`button`,{type:`button`,className:`btn sm outlined`,onClick:()=>n(e.target),children:h[e.target.type]??`Open`}),t?(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>s(e.id),children:`Restore`}):(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,title:`Hide it in this browser until it clears`,onClick:()=>c(e.id),children:`Dismiss`})]})]})}function v({alerts:e}){return(0,p.jsx)(p.Fragment,{children:m.map(({id:t,label:n})=>{let r=e.filter(e=>e.severity===t);return r.length?(0,p.jsxs)(`div`,{className:`panel alerts-group`,id:`alerts-`+t,children:[(0,p.jsxs)(`h2`,{children:[n,` `,(0,p.jsx)(`span`,{className:`count`,children:r.length})]}),r.map(e=>(0,p.jsx)(_,{alert:e},e.id))]},t):null})})}function y(){let e=a(),t=o(),n=l.find(e=>e.id===t.tab),{query:s,active:c,dismissed:d}=r(),[m,h]=(0,f.useState)(!1),g=e=>!n||n.kinds.includes(e.kind),y=c.filter(g),b=d.filter(g),x=e=>c.filter(t=>!e||e.includes(t.kind)),S;S=s.isPending?(0,p.jsx)(i,{text:`Checking…`}):s.isError?(0,p.jsx)(`div`,{className:`panel`,children:(0,p.jsxs)(`div`,{className:`empty`,children:[(0,p.jsx)(`strong`,{children:`Couldn’t check for alerts`}),(0,p.jsx)(`span`,{children:s.error.message}),(0,p.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>void s.refetch(),children:`Retry`})]})}):(0,p.jsxs)(p.Fragment,{children:[n&&(0,p.jsx)(`p`,{className:`alerts-about`,children:n.about}),y.length?(0,p.jsx)(v,{alerts:y}):(0,p.jsx)(`div`,{className:`panel`,id:`alerts-clear`,children:(0,p.jsxs)(`div`,{className:`empty`,children:[(0,p.jsx)(`strong`,{children:`All clear`}),(0,p.jsx)(`span`,{children:n?`Nothing to report from this check right now.`:`Nothing needs your attention right now.`})]})}),b.length>0&&(0,p.jsxs)(`div`,{className:`alerts-dismissed`,children:[(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>h(e=>!e),children:`${b.length} dismissed in this browser · ${m?`Hide`:`Show`}`}),m&&(0,p.jsx)(`div`,{className:`panel alerts-group`,id:`alerts-dismissed`,children:b.map(e=>(0,p.jsx)(_,{alert:e,dismissed:!0},e.id))})]})]});let C=(t,r,i)=>(0,p.jsxs)(`button`,{type:`button`,role:`tab`,"aria-selected":(n?.id??`all`)===t,className:`minitab`+((n?.id??`all`)===t?` active`:``),onClick:()=>e(t===`all`?`/alerts`:`/alerts/`+t),children:[r,s.data?(0,p.jsx)(`span`,{className:`n`+(i.length?` on`:``)+(i.some(e=>e.severity===`critical`)?` critical`:``),children:i.length}):null]},t);return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)(`div`,{className:`page-head`,children:[(0,p.jsxs)(`div`,{className:`titles`,children:[(0,p.jsx)(`h1`,{children:`Alerts`}),(0,p.jsxs)(`span`,{className:`sub`,children:[`What needs attention now, checked every minute. Each clears by itself once its cause does.`,s.data?(0,p.jsxs)(p.Fragment,{children:[` Last checked `,(0,p.jsx)(u,{value:s.data.checked_at}),`.`]}):null]})]}),(0,p.jsx)(`span`,{className:`spacer`}),(0,p.jsx)(`button`,{id:`refresh-alerts`,type:`button`,className:`btn`,onClick:()=>void s.refetch(),children:`Check now`})]}),(0,p.jsxs)(`div`,{className:`minitabs alerts-tabs`,role:`tablist`,"aria-label":`Checks`,children:[C(`all`,`All`,x()),l.map(e=>C(e.id,e.label,x(e.kinds)))]}),S]})}export{y as AlertsPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e,s as t,v as n,z as r}from"./client-ZHNiMG8H.js";import{A as i,D as a,N as o,c as s,d as c,k as l,s as u}from"./index-
|
|
1
|
+
import{n as e,s as t,v as n,z as r}from"./client-ZHNiMG8H.js";import{A as i,D as a,N as o,c as s,d as c,k as l,s as u}from"./index-DBrO0zMK.js";import{t as d}from"./Time-DE-u5yL9.js";import{t as f}from"./QueryInfo-DB1HWOF1.js";import{a as p}from"./reach-BAOLd75Y.js";import{i as m,t as h}from"./KeyActivity-BC6RPPpH.js";var g=r(),_={query:`named query`,collection:`collection`,connection:`connection`};function v({term:e,children:t}){return(0,g.jsxs)(`div`,{children:[(0,g.jsx)(`dt`,{children:e}),(0,g.jsx)(`dd`,{children:t})]})}function y({title:e,sub:t,children:n}){return(0,g.jsxs)(`section`,{className:`panel`,style:{marginTop:16},children:[(0,g.jsxs)(`div`,{className:`panel-bar`,children:[(0,g.jsx)(`strong`,{children:e}),t?(0,g.jsx)(`span`,{className:`dim`,children:t}):null]}),n]})}function b(){let{name:r=``}=o(),b=s(),x=u(),S=c(),C=m(r),{openDrawer:w}=l(),T=new Date().toISOString().slice(0,10),E=n({queryKey:[`apikey-history`,r],queryFn:async()=>t(await e.GET(`/api/v1/history`,{params:{query:{key:r,limit:50}}})).items,retry:!1});if(b.isPending)return(0,g.jsx)(a,{text:`Loading API key…`});let D=b.data?.[r];if(!D)return(0,g.jsxs)(g.Fragment,{children:[(0,g.jsx)(`div`,{className:`page-head`,children:(0,g.jsxs)(`div`,{className:`titles`,children:[(0,g.jsx)(i,{to:`/api-keys`,className:`sub`,children:`← API keys`}),(0,g.jsx)(`h1`,{children:r||`API key`})]})}),(0,g.jsx)(`div`,{className:`panel`,children:(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`No such API key`}),(0,g.jsx)(`span`,{children:`It may have been revoked and removed, or the name is wrong.`}),(0,g.jsx)(i,{to:`/api-keys`,className:`btn sm`,children:`Back to API keys`})]})})]});let O=!!(D.expires_at&&D.expires_at<T),k=D.active?O?`Expired`:`Active`:`Revoked`,A=(x.data??[]).map(e=>({q:e,via:p(D,e.name,e.collection,e.connection_name)})).filter(e=>e.via.length),j=(S.data??[]).map(e=>e.name),M=D.connections===`*`?j:D.connections??[],N=A.map(e=>e.q.connection_name).filter(e=>e!=null&&!M.includes(e)),P=[...new Set([...M,...N])].sort(),F=e=>(S.data??[]).find(t=>t.name===e)?.db??null,I=e=>w({title:e.name,kicker:`${e.collection||`uncollected`} · ${e.connection_name||`no connection`}`,content:(0,g.jsx)(f,{name:e.name,dbType:e.connection_name?F(e.connection_name):null})}),L=D.allowed_tables??[],R=D.allowed_ips??[],z=D.usage_this_period;return(0,g.jsxs)(g.Fragment,{children:[(0,g.jsxs)(`div`,{className:`page-head`,children:[(0,g.jsxs)(`div`,{className:`titles`,children:[(0,g.jsx)(i,{to:`/api-keys`,className:`sub`,children:`← API keys`}),(0,g.jsxs)(`h1`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,g.jsx)(`span`,{className:`dot ${D.active&&!O?`ok`:`off`}`,title:k}),r,D.example?(0,g.jsx)(`span`,{className:`tag`,children:`example`}):null]}),(0,g.jsxs)(`span`,{className:`sub`,children:[k,D.created_from_role?` · created from role ${D.created_from_role}`:``]})]}),(0,g.jsx)(`span`,{className:`spacer`}),(0,g.jsx)(i,{to:`/api-keys`,className:`btn sm`,children:`Back`})]}),(0,g.jsxs)(`dl`,{className:`meta`,children:[(0,g.jsx)(v,{term:`status`,children:k}),(0,g.jsx)(v,{term:`access`,children:D.allow_writes?`Read / write`:`Read-only`}),(0,g.jsx)(v,{term:`created`,children:(0,g.jsx)(d,{value:D.created_at})}),(0,g.jsx)(v,{term:`last used`,children:(0,g.jsx)(d,{value:D.last_used_at,fallback:`never`})}),(0,g.jsx)(v,{term:`expires`,children:D.expires_at?D.expires_at+(O?` (expired)`:``):`never`}),(0,g.jsx)(v,{term:`rate limit`,children:D.rate_limit||`server default`}),(0,g.jsx)(v,{term:`query timeout`,children:D.effective_limits?.query_timeout==null?`server default`:`${D.effective_limits.query_timeout}s`}),(0,g.jsx)(v,{term:`row limit (page)`,children:D.effective_limits?.max_rows?.page??`—`}),(0,g.jsx)(v,{term:`max concurrent`,children:D.effective_limits?.max_concurrent??`unlimited`}),(0,g.jsx)(v,{term:`allowed IPs`,children:R.length?R.join(`, `):`any`})]}),(0,g.jsx)(y,{title:`APIs it can reach`,sub:D.queries===`*`?`all saved queries`:`${A.length} saved ${A.length===1?`query`:`queries`}`,children:D.queries===`*`?(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`Every saved query`}),(0,g.jsx)(`span`,{children:`This key carries a wildcard query grant, so it can run any saved query.`})]}):A.length?(0,g.jsx)(`div`,{style:{overflowX:`auto`},children:(0,g.jsxs)(`table`,{className:`grid`,children:[(0,g.jsx)(`thead`,{children:(0,g.jsx)(`tr`,{children:[`API endpoint`,`Query`,`Connection`,`Collection`,`Granted via`,`Activity (30d)`].map(e=>(0,g.jsx)(`th`,{children:e},e))})}),(0,g.jsx)(`tbody`,{children:A.map(({q:e,via:t})=>(0,g.jsxs)(`tr`,{"data-query":e.name,children:[(0,g.jsx)(`td`,{className:`mono`,children:e.published_version?e.endpoint:(0,g.jsxs)(`span`,{className:`dim`,title:`No published version yet`,children:[e.endpoint,` (draft)`]})}),(0,g.jsx)(`td`,{children:(0,g.jsx)(`button`,{type:`button`,className:`as-link name`,title:`Show the SQL for ${e.name}`,onClick:()=>I(e),children:e.name})}),(0,g.jsx)(`td`,{className:`dim`,children:e.connection_name||`—`}),(0,g.jsx)(`td`,{className:`dim`,children:e.collection||`—`}),(0,g.jsx)(`td`,{children:t.map((e,t)=>(0,g.jsx)(`span`,{className:`tag`,style:{marginRight:4},children:e.kind===`query`?_.query:e.label},t))}),(0,g.jsx)(`td`,{className:`spark-cell`,children:(0,g.jsx)(h,{label:e.name,items:C.by[e.name],isPending:C.isPending,isError:C.isError})})]},e.name))})]})}):(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`No saved queries`}),(0,g.jsx)(`span`,{children:`This key reaches no saved query by name or collection. It may still run ad-hoc SQL on the connections below.`})]})}),(0,g.jsx)(y,{title:`Databases it can reach`,sub:D.connections===`*`?`all connections`:`${P.length} connection(s)`,children:P.length?(0,g.jsx)(`div`,{style:{overflowX:`auto`},children:(0,g.jsxs)(`table`,{className:`grid`,children:[(0,g.jsx)(`thead`,{children:(0,g.jsx)(`tr`,{children:[`Connection`,`Type`,`Reached by`].map(e=>(0,g.jsx)(`th`,{children:e},e))})}),(0,g.jsx)(`tbody`,{children:P.map(e=>(0,g.jsxs)(`tr`,{"data-conn":e,children:[(0,g.jsx)(`td`,{className:`name`,children:e}),(0,g.jsx)(`td`,{children:F(e)?(0,g.jsx)(`span`,{className:`tag`,children:F(e)}):(0,g.jsx)(`span`,{className:`dim`,children:`—`})}),(0,g.jsx)(`td`,{className:`dim`,children:D.connections===`*`?`all connections`:M.includes(e)?`direct grant (ad-hoc SQL)`:`saved query only`})]},e))})]})}):(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`No connections`}),(0,g.jsx)(`span`,{children:`This key cannot reach any connection directly or through a saved query.`})]})}),(0,g.jsxs)(y,{title:`Tables it can reach`,sub:L.length?`${L.length} allowed`:`all tables on reachable connections`,children:[L.length?(0,g.jsx)(`div`,{style:{padding:`12px 14px`,display:`flex`,flexWrap:`wrap`,gap:6},children:L.map(e=>(0,g.jsx)(`span`,{className:`tag mono`,children:e},e))}):(0,g.jsx)(`div`,{style:{padding:`12px 14px`},className:`dim`,children:`No table allow-list — this key may read every table on the connections it reaches.`}),D.allow_writes?(0,g.jsx)(`div`,{style:{padding:`0 14px 12px`},children:(0,g.jsxs)(`span`,{className:`tag`,children:[`writes: `,D.allowed_write_ops?.length?D.allowed_write_ops.join(`, `):`all operations`]})}):null,D.mask?.length?(0,g.jsx)(`div`,{style:{padding:`0 14px 12px`,display:`flex`,flexWrap:`wrap`,gap:6},children:D.mask.map((e,t)=>(0,g.jsxs)(`span`,{className:`tag`,title:`${e.column}: ${e.action}`,children:[`masked `,e.column]},t))}):null]}),(0,g.jsx)(y,{title:`Usage history`,sub:z?`${D.usage.queries} queries total · ${D.usage.rows.toLocaleString()} rows`:`${D.usage.queries} queries · ${D.usage.errors} failed · since this process started`,children:E.isPending?(0,g.jsx)(a,{text:`Loading usage…`}):E.isError?(0,g.jsx)(`div`,{style:{padding:`12px 14px`},className:`dim`,children:`Usage history isn’t available for this key (the run log may be off, or your role can’t read it).`}):E.data.length?(0,g.jsx)(`div`,{style:{overflowX:`auto`},children:(0,g.jsxs)(`table`,{className:`grid`,children:[(0,g.jsx)(`thead`,{children:(0,g.jsx)(`tr`,{children:[`When`,`What`,`Connection`,`Via`,`Rows`,`Duration`,`Result`].map(e=>(0,g.jsx)(`th`,{children:e},e))})}),(0,g.jsx)(`tbody`,{children:E.data.map((e,t)=>(0,g.jsxs)(`tr`,{children:[(0,g.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:(0,g.jsx)(d,{value:e.executed_at})}),(0,g.jsx)(`td`,{className:`name`,children:e.kind===`adhoc`?(0,g.jsx)(`span`,{className:`dim`,children:`ad-hoc SQL`}):e.query}),(0,g.jsx)(`td`,{className:`dim`,children:e.connection_name||`—`}),(0,g.jsx)(`td`,{className:`dim`,children:e.transport||`—`}),(0,g.jsx)(`td`,{className:`mono`,children:e.rows??`—`}),(0,g.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:e.duration_ms==null?`—`:`${e.duration_ms} ms`}),(0,g.jsx)(`td`,{style:e.status===`error`?{color:`var(--danger)`}:void 0,children:e.status===`error`?(0,g.jsx)(`span`,{title:e.error??void 0,children:e.code||`error`}):`ok`})]},e.request_id??t))})]})}):(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`No runs recorded`}),(0,g.jsx)(`span`,{children:`This key hasn’t run a query yet, or its runs have aged out of the log.`})]})})]})}export{b as ApiKeyDetailPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{z as e}from"./client-ZHNiMG8H.js";import{A as t,D as n,c as r,l as i,u as a}from"./index-
|
|
1
|
+
import{z as e}from"./client-ZHNiMG8H.js";import{A as t,D as n,c as r,l as i,u as a}from"./index-DBrO0zMK.js";import{t as o}from"./Time-DE-u5yL9.js";import{t as s}from"./reach-BAOLd75Y.js";import{r as c,t as l}from"./KeyActivity-BC6RPPpH.js";import{i as u,n as d,o as f}from"./forms-BOFRAueI.js";import{t as p}from"./icons-CFXE8hAz.js";import{t as m}from"./Managed-B1rh-_gw.js";var h=e();function g(e,t){let n=new Date(Date.now()+6048e5).toISOString().slice(0,10);return!!(e&&e>=t&&e<=n)}function _({usage:e}){let t=[`calls`,`rows`].map(t=>({what:t,used:e[t],limit:e[`${t}_limit`]})).filter(e=>e.limit!=null),n=t.reduce((e,t)=>t.used/t.limit>e.used/e.limit?t:e,t[0]),r=Math.min(1,n.used/n.limit),i=e.per===`day`?`today`:`this month`;return(0,h.jsxs)(`div`,{className:`quota-usage`,title:t.map(e=>`${e.used.toLocaleString()} of ${e.limit.toLocaleString()} ${e.what}`).join(` · `)+` ${i} · resets ${e.resets_at}`,children:[(0,h.jsxs)(`span`,{className:`dim`,children:[n.used.toLocaleString(),` / `,n.limit.toLocaleString(),` `,n.what,` `,i]}),(0,h.jsx)(`div`,{className:`quota-bar${r>=1?` full`:r>=.8?` near`:``}`,children:(0,h.jsx)(`span`,{style:{width:`${Math.round(r*100)}%`}})})]})}function v(e){if(!e.queries)return null;let t=[e.queries+(e.queries===1?` query`:` queries`)];return e.errors&&t.push(e.errors+` failed`),t.join(` · `)}function y(){let e=i(),y=r(),b=a(),x=c(),S=d(),C=u(),w=Object.keys(y.data??{}).sort(),T=new Date().toISOString().slice(0,10),E;return E=y.isPending?(0,h.jsx)(n,{text:`Loading API keys…`}):y.isError?(0,h.jsxs)(`div`,{className:`empty`,children:[(0,h.jsx)(`strong`,{children:`Couldn’t load API keys`}),(0,h.jsx)(`span`,{children:y.error.message}),(0,h.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>y.refetch(),children:`Retry`})]}):w.length?(0,h.jsx)(`div`,{style:{overflowX:`auto`},children:(0,h.jsxs)(`table`,{className:`grid`,children:[(0,h.jsx)(`thead`,{children:(0,h.jsx)(`tr`,{children:[`Name`,`Scope`,`Access`,`Rate limit`,`IPs`,`Expires`,`Last used`,`Usage`,`Activity (30d)`,``].map((e,t)=>(0,h.jsx)(`th`,{children:e},t))})}),(0,h.jsx)(`tbody`,{children:w.map(n=>{let r=y.data[n],i=!!(r.expires_at&&r.expires_at<T),a=!i&&g(r.expires_at,T),c=r.allowed_ips??[],u=v(r.usage);return(0,h.jsxs)(`tr`,{"data-name":n,children:[(0,h.jsx)(`td`,{children:(0,h.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,h.jsx)(`span`,{className:`dot ${r.active&&!i?`ok`:`off`}`,title:r.active?i?`Expired`:`Active`:`Revoked`}),(0,h.jsx)(`span`,{className:`name`,title:r.created_at?`Created `+r.created_at+(r.created_from_role?` from role `+r.created_from_role:``):void 0,children:n}),(0,h.jsx)(m,{kind:`api_keys`,name:n}),r.previous_expires_at?(0,h.jsx)(`span`,{className:`tag`,title:`Rotated: the old secret works until then too`,children:`rotating`}):null,r.mask?.length?(0,h.jsx)(`span`,{className:`tag`,title:r.mask.map(e=>`${e.column}: ${e.action}`).join(`, `),children:`masked`}):null]})}),(0,h.jsx)(`td`,{children:(0,h.jsx)(f,{entry:r,collections:b.data?.collections})}),(0,h.jsx)(s,{entry:r}),(0,h.jsx)(`td`,{className:r.rate_limit?`mono`:`mono dim`,style:{whiteSpace:`nowrap`},children:r.rate_limit||`server default`}),(0,h.jsx)(`td`,{className:`dim`,children:c.length?(0,h.jsx)(`span`,{title:c.join(`, `),children:c.length+(c.length===1?` IP`:` IPs`)}):(0,h.jsx)(`span`,{className:`dim`,children:`any`})}),(0,h.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:r.expires_at?(0,h.jsx)(`span`,{style:i?{color:`var(--danger)`}:a?{color:`var(--warn)`}:void 0,children:r.expires_at+(i?` (expired)`:a?` (expires soon)`:``)}):(0,h.jsx)(`span`,{className:`dim`,children:`never`})}),(0,h.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,h.jsx)(o,{value:r.last_used_at,fallback:`never`})}),r.usage_this_period?(0,h.jsxs)(`td`,{style:{whiteSpace:`nowrap`},children:[u,(0,h.jsx)(_,{usage:r.usage_this_period})]}):u?(0,h.jsx)(`td`,{title:`${r.usage.rows}${r.usage.rows===1?` row`:` rows`} returned in total · since this process started`,style:{whiteSpace:`nowrap`},children:u}):(0,h.jsx)(`td`,{className:`dim`,style:{whiteSpace:`nowrap`},children:`No activity yet`}),(0,h.jsx)(`td`,{className:`spark-cell`,children:(0,h.jsx)(l,{label:n,items:x.by[n],isPending:x.isPending,isError:x.isError,to:`/api-keys/${encodeURIComponent(n)}`})}),(0,h.jsx)(`td`,{children:(0,h.jsxs)(`div`,{className:`actions`,children:[(0,h.jsx)(t,{to:`/api-keys/${encodeURIComponent(n)}`,className:`btn ghost sm`,title:`Full details of ${n}`,children:`Details`}),(0,h.jsx)(`button`,{hidden:!e(`access.write`),type:`button`,className:`btn ghost sm icon`,title:`Edit ${n}`,"aria-label":`Edit`,onClick:()=>S.editKey(n),children:(0,h.jsx)(p,{})}),(0,h.jsx)(`button`,{hidden:!e(`access.write`),type:`button`,className:`btn ghost sm danger`,onClick:()=>void C(n),children:`Revoke`})]})})]},n)})})]})}):(0,h.jsxs)(`div`,{className:`empty`,children:[(0,h.jsx)(`strong`,{children:`No scoped API keys yet`}),(0,h.jsx)(`span`,{children:`QUERYAPIGATE_API_KEY is a full-access admin key. Create a scoped one to limit a caller to specific connections, read-only.`}),(0,h.jsx)(`button`,{hidden:!e(`access.write`),type:`button`,className:`btn primary`,onClick:()=>S.newKey(),children:`New API key`})]}),(0,h.jsxs)(h.Fragment,{children:[(0,h.jsxs)(`div`,{className:`page-head`,children:[(0,h.jsxs)(`div`,{className:`titles`,children:[(0,h.jsx)(`h1`,{children:`API keys`}),(0,h.jsx)(`span`,{className:`sub`,id:`apikeys-sub`,children:(w.length?w.length+(w.length===1?` key. `:` keys. `):``)+`Scope each one to connections, collections or roles.`})]}),(0,h.jsx)(`span`,{className:`spacer`}),(0,h.jsx)(`button`,{hidden:!e(`access.write`),id:`new-apikey`,type:`button`,className:`btn primary`,onClick:()=>S.newKey(),children:`New API key`})]}),(0,h.jsx)(`div`,{id:`apikeys-table`,className:`panel`,children:E})]})}export{y as ApiKeysPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{B as e,H as t,R as n,n as r,s as i,t as a,v as o,z as s}from"./client-ZHNiMG8H.js";import{t as c}from"./useMutation-
|
|
1
|
+
import{B as e,H as t,R as n,n as r,s as i,t as a,v as o,z as s}from"./client-ZHNiMG8H.js";import{t as c}from"./useMutation-BHDQeA9M.js";import{D as l,M as u,N as d,T as f,f as p,k as m}from"./index-DBrO0zMK.js";import{t as h}from"./Time-DE-u5yL9.js";import{t as g}from"./CodeBox-Cwh5KHLp.js";var _=t(e(),1),v=s(),y={pending:`Waiting`,approved:`Approved - not run yet`,running:`Running`,done:`Done`,failed:`Failed`,rejected:`Rejected`,expired:`Expired`};function b(e){let t=p(`writes.approve`,`write requests`);return o({queryKey:[`write-requests`,e||`all`],queryFn:async()=>(await t.check(),i(await r.GET(`/api/v1/write-requests`,{params:{query:e?{status:e}:{}}})).items),enabled:t.ready,retry:!1})}var x=e=>JSON.stringify(e);function S({args:e}){let t=Object.entries(e);return t.length?(0,v.jsx)(`table`,{className:`grid approval-args`,children:(0,v.jsx)(`tbody`,{children:t.map(([e,t])=>(0,v.jsxs)(`tr`,{children:[(0,v.jsx)(`td`,{className:`mono`,children:e}),(0,v.jsx)(`td`,{className:`mono`,children:x(t)})]},e))})}):(0,v.jsx)(`span`,{className:`dim`,children:`none`})}function C({id:e}){let{closeDrawer:t,showError:s,toast:u}=m(),d=n(),[p,b]=(0,_.useState)(``),C=o({queryKey:[`write-requests`,`one`,e],queryFn:async()=>i(await r.GET(`/api/v1/write-requests/{id}`,{params:{path:{id:e}}})),retry:!1}),w=c({mutationFn:async t=>i(t?await r.POST(`/api/v1/write-requests/{id}/approve`,{params:{path:{id:e}}}):await r.POST(`/api/v1/write-requests/{id}/reject`,{params:{path:{id:e}},body:p.trim()?{reason:p.trim()}:{}})),onSuccess:e=>{s(``),d.invalidateQueries({queryKey:[`write-requests`]}),d.invalidateQueries({queryKey:[`alerts`]}),t(),u(e.status===`approved`?`Approved - ${e.caller} may run it now`:`Rejected`)},onError:e=>s(e.message,e instanceof a?{status:e.status}:{})});if(C.isPending)return(0,v.jsx)(l,{});if(C.isError)return(0,v.jsx)(`div`,{className:`hint`,children:C.error.message});let T=C.data,E=T.status===`pending`;return(0,v.jsxs)(`div`,{className:`form`,children:[(0,v.jsxs)(`p`,{className:`hint`,children:[(0,v.jsx)(`strong`,{children:T.caller}),` asked to run `,(0,v.jsx)(`strong`,{children:T.query}),` (version `,T.version,`) on`,` `,(0,v.jsx)(`span`,{className:`mono`,children:T.connection}),`. It changes at most `,T.max_affected_rows,` row`,T.max_affected_rows===1?``:`s`,`; past that it is rolled back.`]}),(0,v.jsx)(f,{label:`SQL`,children:(0,v.jsx)(g,{sql:T.sql})}),(0,v.jsx)(f,{label:`With exactly these arguments`,hint:`An approval is for these alone - no other write.`,children:(0,v.jsx)(S,{args:T.arguments})}),(0,v.jsxs)(`div`,{className:`hint`,children:[`Asked `,(0,v.jsx)(h,{value:T.created_at}),` · `,y[T.status]??T.status,E||T.status===`approved`?(0,v.jsxs)(v.Fragment,{children:[` `,`· expires `,(0,v.jsx)(h,{value:T.expires_at})]}):null,T.decided_by?` · ${T.status===`rejected`?`rejected`:`approved`} by ${T.decided_by}`:``,T.reason?` - ${T.reason}`:``]}),T.result?(0,v.jsx)(f,{label:`Result`,children:(0,v.jsx)(`span`,{className:`mono`,children:x(T.result)})}):null,E?(0,v.jsxs)(v.Fragment,{children:[(0,v.jsx)(f,{id:`approval-reason`,label:`Reason`,hint:`Optional for a rejection - the agent is told.`,children:(0,v.jsx)(`textarea`,{id:`approval-reason`,rows:2,value:p,onChange:e=>b(e.target.value)})}),(0,v.jsxs)(`div`,{className:`form-actions`,children:[(0,v.jsx)(`button`,{type:`button`,className:`btn primary`,disabled:w.isPending,onClick:()=>w.mutate(!0),children:`Approve`}),(0,v.jsx)(`button`,{type:`button`,className:`btn danger`,disabled:w.isPending,onClick:()=>w.mutate(!1),children:`Reject`}),(0,v.jsx)(`button`,{type:`button`,className:`btn`,onClick:t,children:`Close`})]})]}):(0,v.jsx)(`div`,{className:`form-actions`,children:(0,v.jsx)(`button`,{type:`button`,className:`btn`,onClick:t,children:`Close`})})]})}function w(){let{id:e}=d(),t=u(),{openDrawer:n}=m(),[r,i]=(0,_.useState)(`pending`),a=b(r),o=a.data??[],s=e=>n({title:`Approve a write`,kicker:e,wide:!0,content:(0,v.jsx)(C,{id:e})});(0,_.useEffect)(()=>{e&&(s(e),t(`/approvals`,{replace:!0}))},[e]);let c;return c=a.isPending?(0,v.jsx)(l,{text:`Loading write requests…`}):a.isError?(0,v.jsxs)(`div`,{className:`empty`,children:[(0,v.jsx)(`strong`,{children:`Couldn’t load write requests`}),(0,v.jsx)(`span`,{children:a.error.message})]}):o.length?(0,v.jsx)(`div`,{style:{overflowX:`auto`},children:(0,v.jsxs)(`table`,{className:`grid`,children:[(0,v.jsx)(`thead`,{children:(0,v.jsx)(`tr`,{children:[`Asked`,`Agent`,`Tool`,`Arguments`,r===`pending`?`Expires`:`Status`].map(e=>(0,v.jsx)(`th`,{children:e},e))})}),(0,v.jsx)(`tbody`,{children:o.map(e=>(0,v.jsxs)(`tr`,{"data-request":e.id,children:[(0,v.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,v.jsx)(h,{value:e.created_at,fallback:``})}),(0,v.jsx)(`td`,{className:`mono`,children:e.caller}),(0,v.jsx)(`td`,{children:(0,v.jsx)(`button`,{type:`button`,className:`agent-name`,onClick:()=>s(e.id),children:e.query})}),(0,v.jsx)(`td`,{className:`mono dim approval-args-cell`,children:x(e.arguments)}),(0,v.jsx)(`td`,{className:`dim`,style:{whiteSpace:`nowrap`},children:r===`pending`?(0,v.jsx)(h,{value:e.expires_at}):y[e.status]??e.status})]},e.id))})]})}):(0,v.jsxs)(`div`,{className:`empty`,children:[(0,v.jsx)(`strong`,{children:r===`pending`?`No writes are waiting for approval`:`No write requests yet`}),(0,v.jsx)(`span`,{children:`When an AI agent calls a write tool, its write waits here for a person to approve it - the agent is given a link to this screen.`})]}),(0,v.jsxs)(v.Fragment,{children:[(0,v.jsxs)(`div`,{className:`page-head`,children:[(0,v.jsxs)(`div`,{className:`titles`,children:[(0,v.jsx)(`h1`,{children:`Approvals`}),(0,v.jsx)(`span`,{className:`sub`,children:`Writes AI agents asked to make over MCP. Each runs only once a person approves it - with exactly the arguments shown.`})]}),(0,v.jsx)(`span`,{className:`spacer`}),(0,v.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>void a.refetch(),children:`Refresh`})]}),(0,v.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,v.jsxs)(`div`,{className:`panel-bar`,children:[(0,v.jsxs)(`select`,{"aria-label":`Show`,value:r,onChange:e=>i(e.target.value),children:[(0,v.jsx)(`option`,{value:`pending`,children:`Waiting`}),(0,v.jsx)(`option`,{value:``,children:`All`}),(0,v.jsx)(`option`,{value:`done`,children:`Done`}),(0,v.jsx)(`option`,{value:`rejected`,children:`Rejected`}),(0,v.jsx)(`option`,{value:`expired`,children:`Expired`}),(0,v.jsx)(`option`,{value:`failed`,children:`Failed`})]}),(0,v.jsx)(`span`,{className:`spacer`}),(0,v.jsx)(`span`,{className:`panel-count`,children:a.data?`${o.length} request${o.length===1?``:`s`}`:``})]}),c]})]})}export{w as ApprovalsPage};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{B as e,H as t,n,s as r,v as i,z as a}from"./client-ZHNiMG8H.js";import{D as o,k as s}from"./index-
|
|
1
|
+
import{B as e,H as t,n,s as r,v as i,z as a}from"./client-ZHNiMG8H.js";import{D as o,k as s}from"./index-DBrO0zMK.js";import{t as c}from"./Time-DE-u5yL9.js";var l=t(e(),1),u=a();function d(e){return/^(create|load|save)/.test(e)?`ok`:/^(delete|unload|revoke)/.test(e)?`bad`:``}function f(e){if(e==null||e===``)return`none`;let t=e=>e&&typeof e==`object`?JSON.stringify(e):String(e);return Array.isArray(e)?e.length?e.map(t).join(`, `):`none`:t(e)}var p=e=>e==null||e===``||Array.isArray(e)&&!e.length,m=e=>!!e&&typeof e==`object`&&!Array.isArray(e)&&(`from`in e||`to`in e);function h({changes:e}){let t=e&&typeof e==`object`?e:null,n=t?Object.keys(t).sort():[],r=n.filter(e=>m(t[e])||!p(t[e]));return r.length?(0,u.jsx)(`div`,{children:r.map(e=>{let n=t[e];return(0,u.jsx)(`div`,{className:`mono`,style:{fontSize:12,color:`var(--ink-2)`},children:m(n)?`${e}: ${f(n.from)} → ${f(n.to)}`:`${e}: ${f(n)}`},e)})}):(0,u.jsx)(`span`,{className:`dim`,children:n.length?`no other fields set`:`—`})}function g(e){let t=new Blob([JSON.stringify(e,null,2)+`
|
|
2
2
|
`],{type:`application/json`}),n=document.createElement(`a`);n.href=URL.createObjectURL(t),n.download=`queryapigate-audit-log.json`,n.click(),setTimeout(()=>URL.revokeObjectURL(n.href),0)}function _(){let{toast:e}=s(),t=i({queryKey:[`audit`],queryFn:async()=>r(await n.GET(`/api/v1/audit`)),retry:!1}),[a,f]=(0,l.useState)(``),[p,m]=(0,l.useState)(``),_=t.data?.items??[],v=p.trim().toLowerCase(),y=_.filter(e=>(!a||e.action===a)&&(!v||[e.timestamp,e.actor,e.target].join(` `).toLowerCase().includes(v))),b=t.data?.actions??[],x,S=``;return t.isPending?x=(0,u.jsx)(o,{text:`Loading audit log…`}):t.isError?x=(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`Couldn’t load the audit log`}),(0,u.jsx)(`span`,{children:t.error.message}),(0,u.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>t.refetch(),children:`Retry`})]}):_.length?(S=y.length===_.length?_.length+(_.length===1?` entry`:` entries`):`${y.length} of ${_.length} entries`,x=y.length?(0,u.jsx)(`div`,{style:{overflowX:`auto`},children:(0,u.jsxs)(`table`,{className:`grid`,children:[(0,u.jsx)(`thead`,{children:(0,u.jsx)(`tr`,{children:[`Time`,`Actor`,`Action`,`Target`,`Changes`].map(e=>(0,u.jsx)(`th`,{children:e},e))})}),(0,u.jsx)(`tbody`,{children:y.map((e,t)=>(0,u.jsxs)(`tr`,{children:[(0,u.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,u.jsx)(c,{value:e.timestamp,fallback:``})}),(0,u.jsx)(`td`,{className:`mono`,children:e.actor||`-`}),(0,u.jsx)(`td`,{children:(0,u.jsx)(`span`,{className:`tag act `+d(e.action),children:e.action||``})}),(0,u.jsx)(`td`,{children:(0,u.jsx)(`span`,{className:`name`,children:e.target||``})}),(0,u.jsx)(`td`,{children:(0,u.jsx)(h,{changes:e.changes})})]},t))})]})}):(0,u.jsx)(`div`,{className:`empty`,children:(0,u.jsx)(`span`,{children:`No entries match this filter.`})})):x=(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`No administrative changes recorded yet`}),(0,u.jsx)(`span`,{children:`Creating or changing a connection, saved query or API key will appear here.`})]}),(0,u.jsxs)(u.Fragment,{children:[(0,u.jsxs)(`div`,{className:`page-head`,children:[(0,u.jsxs)(`div`,{className:`titles`,children:[(0,u.jsx)(`h1`,{children:`Audit log`}),(0,u.jsx)(`span`,{className:`sub`,id:`auditlog-sub`,children:`Administrative actions, newest first.`+(t.data?` Keeps the last ${t.data.retention} entries.`:``)})]}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`button`,{id:`refresh-auditlog`,type:`button`,className:`btn`,onClick:()=>void t.refetch(),children:`Refresh`}),(0,u.jsx)(`button`,{id:`export-auditlog`,type:`button`,className:`btn`,onClick:()=>{g(y),e(`Exported `+y.length+(y.length===1?` entry`:` entries`))},children:`Export`})]}),(0,u.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,u.jsxs)(`div`,{className:`panel-bar`,children:[(0,u.jsxs)(`select`,{id:`auditlog-action-filter`,"aria-label":`Action`,value:b.includes(a)?a:``,onChange:e=>f(e.target.value),children:[(0,u.jsx)(`option`,{value:``,children:`All actions`}),b.map(e=>(0,u.jsx)(`option`,{value:e,children:e},e))]}),(0,u.jsx)(`input`,{id:`auditlog-filter`,className:`search`,style:{width:260},type:`search`,placeholder:`Filter by actor, target, time…`,value:p,onChange:e=>m(e.target.value)}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`span`,{className:`panel-count`,id:`auditlog-count`,children:S})]}),(0,u.jsx)(`div`,{id:`auditlog-table`,children:x})]})]})}export{_ as AuditLogPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{B as e,H as t,R as n,c as r,n as i,r as a,s as o,v as s,z as c}from"./client-ZHNiMG8H.js";import{M as l,_ as u,k as d,s as f}from"./index-
|
|
1
|
+
import{B as e,H as t,R as n,c as r,n as i,r as a,s as o,v as s,z as c}from"./client-ZHNiMG8H.js";import{M as l,_ as u,k as d,s as f}from"./index-DBrO0zMK.js";import{a as p,t as m}from"./KeyActivity-BC6RPPpH.js";import{i as h,n as g,t as _}from"./StatTile-Cft4kJPH.js";import{n as v,t as y}from"./Results-BsH71FdD.js";var b=t(e(),1),x=c();function S(){return s({queryKey:[`cache`,`entries`],queryFn:async()=>o(await i.GET(`/api/v1/cache/entries`)).items,retry:!1})}function C(){let e=l(),t=n(),r=u(),i=g(),a=f(),o=p(),s=i.data??[],c=r.data?.find(e=>e.id===`cache`)?.rows[0]?.value??`…`,d=h(s,`queryapigate_cache_hits_total`),v=h(s,`queryapigate_cache_misses_total`),y=d+v,b=(a.data??[]).filter(e=>(e.cache_ttl??0)>0);return(0,x.jsxs)(x.Fragment,{children:[(0,x.jsxs)(`div`,{className:`page-head`,children:[(0,x.jsxs)(`div`,{className:`titles`,children:[(0,x.jsx)(`h1`,{children:`Caching`}),(0,x.jsxs)(`span`,{className:`sub`,children:[`The response cache backing saved queries' `,(0,x.jsx)(`code`,{children:`cache_ttl`}),` - see the Response cache row in Settings for the backend itself.`]})]}),(0,x.jsx)(`span`,{className:`spacer`}),(0,x.jsx)(`button`,{id:`refresh-caching`,type:`button`,className:`btn`,onClick:()=>{i.refetch(),t.invalidateQueries({queryKey:[`cache`,`entries`]})},children:`Refresh`})]}),(0,x.jsxs)(`div`,{id:`caching-body`,children:[(0,x.jsxs)(`div`,{className:`stat-tiles`,children:[(0,x.jsx)(_,{label:`Cache backend`,value:c}),(0,x.jsx)(_,{label:`Entries`,value:h(s,`queryapigate_cache_entries`)}),(0,x.jsx)(_,{label:`Hit rate`,value:y?(100*d/y).toFixed(1)+`%`:`—`}),(0,x.jsx)(_,{label:`Hits`,value:d}),(0,x.jsx)(_,{label:`Misses`,value:v})]}),(0,x.jsx)(`p`,{className:`sub-h`,style:{marginTop:14},children:`Saved queries with cache_ttl set`}),b.length?(0,x.jsx)(`div`,{className:`panel`,style:{overflowX:`auto`},children:(0,x.jsxs)(`table`,{className:`grid`,children:[(0,x.jsx)(`thead`,{children:(0,x.jsx)(`tr`,{children:[`Query`,`Collection`,`Connection`,`cache_ttl`,`Activity (30d)`].map((e,t)=>(0,x.jsx)(`th`,{className:t===3?`num`:``,children:e},e))})}),(0,x.jsx)(`tbody`,{children:b.map(t=>(0,x.jsxs)(`tr`,{children:[(0,x.jsx)(`td`,{children:(0,x.jsx)(`button`,{type:`button`,className:`target`,onClick:()=>e(`/queries/${encodeURIComponent(t.name)}`),children:t.name})}),(0,x.jsx)(`td`,{children:t.collection||(0,x.jsx)(`span`,{className:`dim`,children:`—`})}),(0,x.jsx)(`td`,{className:`mono`,children:t.connection_name||`—`}),(0,x.jsx)(`td`,{className:`mono num`,children:t.cache_ttl+`s`}),(0,x.jsx)(`td`,{className:`spark-cell`,children:(0,x.jsx)(m,{label:t.name,items:o.by[t.name],isPending:o.isPending,isError:o.isError})})]},t.name))})]})}):(0,x.jsx)(`div`,{className:`hint`,children:`No saved query declares a cache_ttl yet - set one on a read-only query to start caching its responses.`})]}),(0,x.jsx)(`div`,{id:`cache-entries-panel`,children:(0,x.jsx)(T,{})})]})}function w(e){return e<1024?e+` B`:(e/1024).toFixed(1)+` KB`}function T(){let e=n(),{openDrawer:t,showError:a,toast:o}=d(),s=S().data??[],c=()=>void e.invalidateQueries({queryKey:[`cache`,`entries`]}),l=e=>a(e.message),u=async()=>{if(window.confirm(`Evict all ${s.length} cache entries? The next call to each query will re-run for real.`))try{r(await i.DELETE(`/api/v1/cache/entries`)),o(`Cache cleared`),c()}catch(e){l(e)}},f=async e=>{try{r(await i.DELETE(`/api/v1/cache/entries/{key}`,{params:{path:{key:e}}})),o(`Entry deleted`),c()}catch(e){l(e)}};return(0,x.jsxs)(x.Fragment,{children:[(0,x.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:12},children:[(0,x.jsxs)(`div`,{className:`set-head`,style:{marginTop:14},children:[(0,x.jsx)(`h2`,{children:`Cache entries`}),(0,x.jsx)(`span`,{children:`${s.length}${s.length===1?` live entry`:` live entries`} right now.`})]}),(0,x.jsx)(`span`,{className:`spacer`}),(0,x.jsx)(`button`,{type:`button`,className:`btn sm`,disabled:!s.length,onClick:()=>void u(),children:`Clear cache`})]}),s.length?(0,x.jsx)(`div`,{className:`panel`,style:{overflowX:`auto`,marginTop:8},children:(0,x.jsxs)(`table`,{className:`grid`,children:[(0,x.jsx)(`thead`,{children:(0,x.jsx)(`tr`,{children:[`Query`,`Version`,`Connection`,`Format`,`Content type`,`Size`,`TTL left`,``].map((e,t)=>(0,x.jsx)(`th`,{className:t>=4&&t<=6?`num`:``,children:e},t))})}),(0,x.jsx)(`tbody`,{children:s.map(e=>{let n=e.meta??{};return(0,x.jsxs)(`tr`,{children:[(0,x.jsx)(`td`,{children:(0,x.jsx)(`button`,{type:`button`,className:`target`,onClick:()=>t({title:n.name||`Cache entry`,kicker:e.key,content:(0,x.jsx)(D,{entry:e})}),children:n.name||e.key.slice(0,12)+`…`})}),(0,x.jsx)(`td`,{className:`mono`,children:n.version===void 0?`—`:`v`+n.version}),(0,x.jsx)(`td`,{className:`mono`,children:n.connection||`—`}),(0,x.jsx)(`td`,{className:`mono`,children:n.format||`—`}),(0,x.jsx)(`td`,{className:`mono num`,children:e.content_type}),(0,x.jsx)(`td`,{className:`mono num`,children:w(e.size_bytes)}),(0,x.jsx)(`td`,{className:`mono num`,children:Math.max(0,Math.round(e.ttl_remaining_s))+`s`}),(0,x.jsx)(`td`,{children:(0,x.jsx)(`button`,{type:`button`,className:`btn sm ghost`,onClick:()=>void f(e.key),children:`Delete`})})]},e.key)})})]})}):(0,x.jsx)(`div`,{className:`hint`,children:`Nothing cached right now - call a query with a cache_ttl set, then check back.`})]})}var E=e=>({ok:!1,status:0,statusText:``,contentType:``,headers:[],page:1,size:0,hasMore:!1,elapsed:0,format:``,error:e});function D({entry:e}){let{showError:t}=d(),n=e.meta?.name||`cache-entry`,r=e.meta?.format||`json`,[i,o]=(0,b.useState)(null);return(0,b.useEffect)(()=>{let i=!0,s=performance.now();return a(`/api/v1/cache/entries/${encodeURIComponent(e.key)}`).then(e=>v(e,{page:1,size:0,format:r,elapsed:Math.round(performance.now()-s),filename:n})).then(e=>{i&&(e.error&&t(e.error,{status:e.status}),o(e))},e=>i&&o(E(`Network error: `+e.message))),()=>{i=!1}},[e.key,r,n,t]),(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(`div`,{className:`hint`,children:`The exact cached body, served with its real Content-Type - what a caller receives on a hit.`}),(0,x.jsx)(y,{result:i,running:!i,filename:n,onPage:()=>{},onPageSize:()=>{},layout:`drawer`})]})}export{C as CachingPage};
|