queryapigate 0.30.0__tar.gz → 0.32.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.30.0/queryapigate.egg-info → queryapigate-0.32.0}/PKG-INFO +3 -3
- {queryapigate-0.30.0 → queryapigate-0.32.0}/README.md +2 -2
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/__init__.py +1 -1
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/auditchain.py +49 -10
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/auditstream.py +2 -1
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/cli.py +8 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/AccessMapPage-CU9b_-fo.js → queryapigate-0.32.0/queryapigate/console_dist/assets/AccessMapPage-CFAKvNHS.js +3 -3
- queryapigate-0.30.0/queryapigate/console_dist/assets/AdministratorsPage-Bwjtg36U.js → queryapigate-0.32.0/queryapigate/console_dist/assets/AdministratorsPage-O9o_8T8Q.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/AgentsPage-BmXNCpQ0.js → queryapigate-0.32.0/queryapigate/console_dist/assets/AgentsPage-CJL7R9ML.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/AlertsPage-C7tvw3Hr.js → queryapigate-0.32.0/queryapigate/console_dist/assets/AlertsPage-Rhnx0WE5.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/ApiKeysPage-B-TKdW-V.js → queryapigate-0.32.0/queryapigate/console_dist/assets/ApiKeysPage-LSfT7OFf.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/ApprovalsPage-Bqsr5dHt.js → queryapigate-0.32.0/queryapigate/console_dist/assets/ApprovalsPage-CxyRU_om.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/AuditLogPage-hAKiebHz.js → queryapigate-0.32.0/queryapigate/console_dist/assets/AuditLogPage-CfozUklt.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/CachingPage-WFUpWs_W.js → queryapigate-0.32.0/queryapigate/console_dist/assets/CachingPage-8TY7yUvl.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/CodeBox-DuaKuoNl.js → queryapigate-0.32.0/queryapigate/console_dist/assets/CodeBox-B8iq2Oop.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/ConnectionsPage-DwdiiWsf.js → queryapigate-0.32.0/queryapigate/console_dist/assets/ConnectionsPage-CrgOBe8i.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/DesignerPage-BfhC69pJ.js → queryapigate-0.32.0/queryapigate/console_dist/assets/DesignerPage-C7m2WgjH.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/DestinationsPage-yaGxeOXB.js → queryapigate-0.32.0/queryapigate/console_dist/assets/DestinationsPage-hjd4ejdt.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/ExportsPage-DlkNfYMZ.js → queryapigate-0.32.0/queryapigate/console_dist/assets/ExportsPage-D70vuQNl.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/HelpPage-Bey5KI_-.js → queryapigate-0.32.0/queryapigate/console_dist/assets/HelpPage-C7vakDSS.js +2 -2
- queryapigate-0.30.0/queryapigate/console_dist/assets/HomePage-DeF8Zaq6.js → queryapigate-0.32.0/queryapigate/console_dist/assets/HomePage-BuExjOpj.js +2 -2
- queryapigate-0.30.0/queryapigate/console_dist/assets/Managed-CQVNTWrU.js → queryapigate-0.32.0/queryapigate/console_dist/assets/Managed-DjB6h9Kc.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/MetricsPage-DqeoN2tZ.js → queryapigate-0.32.0/queryapigate/console_dist/assets/MetricsPage-BAQxP5-4.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/RepositoryPage-QqouaqLm.js → queryapigate-0.32.0/queryapigate/console_dist/assets/RepositoryPage-8CBmviv-.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/Results-BhwS6Q_T.js → queryapigate-0.32.0/queryapigate/console_dist/assets/Results-DofTMPkj.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/RolesPage-DhkAkZ1v.js → queryapigate-0.32.0/queryapigate/console_dist/assets/RolesPage-CH_Dr1DP.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/SchemaBrowser-DQ0mi4Vm.js → queryapigate-0.32.0/queryapigate/console_dist/assets/SchemaBrowser-D57iE2Kg.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/SettingsPage-4T0vN6dU.js → queryapigate-0.32.0/queryapigate/console_dist/assets/SettingsPage-CHmKfYHu.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/StatTile-DHKvOsXg.js → queryapigate-0.32.0/queryapigate/console_dist/assets/StatTile-BBd2i5mC.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/Time-KeJDv2te.js → queryapigate-0.32.0/queryapigate/console_dist/assets/Time-CR17Ozn9.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/forms-StJt-xsd.js → queryapigate-0.32.0/queryapigate/console_dist/assets/forms-C3dw2wG6.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/forms-bSOovYin.js → queryapigate-0.32.0/queryapigate/console_dist/assets/forms-DR5CXITG.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/icons-CeIi0TUG.js → queryapigate-0.32.0/queryapigate/console_dist/assets/icons-C-i_90Gs.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/index-DvCopLMA.js → queryapigate-0.32.0/queryapigate/console_dist/assets/index-BNMV1syF.js +2 -2
- queryapigate-0.30.0/queryapigate/console_dist/assets/maskRules-9C_A2JIu.js → queryapigate-0.32.0/queryapigate/console_dist/assets/maskRules-DyD2pxp7.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/reach-DWK2PD5I.js → queryapigate-0.32.0/queryapigate/console_dist/assets/reach-0Yvn3NAt.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/shared-rt98BmTW.js → queryapigate-0.32.0/queryapigate/console_dist/assets/shared-BebZOZXs.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/useMutation-BcyO6Hjg.js → queryapigate-0.32.0/queryapigate/console_dist/assets/useMutation-DCzNFnov.js +1 -1
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/console_dist/index.html +1 -1
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/experimental.py +2 -10
- {queryapigate-0.30.0 → queryapigate-0.32.0/queryapigate.egg-info}/PKG-INFO +3 -3
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate.egg-info/SOURCES.txt +34 -32
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_audit_chain.py +64 -0
- queryapigate-0.32.0/tests/test_audit_siem.py +246 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_experimental.py +12 -8
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_helm.py +11 -0
- queryapigate-0.32.0/tests/test_supply_chain.py +104 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/LICENSE +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/pyproject.toml +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/__main__.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/accesslog.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/adminroles.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/admins.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/agentwrites.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/alerts.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/apikeys.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/app.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/aws.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/broadcast.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/bundle.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/cache.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/collection_admin.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/config.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/configcode.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/console.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/console_dist/assets/index-BLd0t6vZ.css +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/console_dist/assets/state-O13cl7cO.js +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/console_dist/favicon.svg +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/cors.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/databases.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/db.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/definitions.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/deprecations.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/descriptions.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/destinations.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/duckfiles.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/engine.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/errors.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/events.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/examples.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/exporting.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/exports.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/formats.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/governance.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/gunicorn_conf.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/history.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/instances.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/jwtauth.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/lib/h2-2.2.224.jar +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/limits.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/logging_setup.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/masking.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/mcp_server.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/metrics.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/mongotools.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/oidc.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/openapi.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/params.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/parquet.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/pool.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/postman.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/quotas.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/ratelimit.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/rediscache.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/rekey.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/runners.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/schema.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/secheaders.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/secretrefs.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/services/__init__.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/services/access.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/services/administrators.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/services/audit.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/services/collections.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/services/connections.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/services/descriptions.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/services/destinations.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/services/exports.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/services/mcp.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/services/queries.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/sqlflow.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/sqltools.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/sso.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/store.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/tableguard.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/tracing.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/usage.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/v1.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/v1_spec.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate/vault.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate.egg-info/dependency_links.txt +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate.egg-info/entry_points.txt +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate.egg-info/requires.txt +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/queryapigate.egg-info/top_level.txt +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/setup.cfg +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_adhoc_history.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_adminroles.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_admins.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_agent_writes.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_alerts.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_api_administrators.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_api_keys.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_api_v1.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_audit_log.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_audit_stream.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_aws.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_backup_restore.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_broadcast.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_cli.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_collection_bundle.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_collection_postman.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_collections.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_configcode.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_connection_encryption.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_console.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_cors_and_rate_limit.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_data_protection.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_databases.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_db.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_dependency_floors.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_deprecations.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_descriptions.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_destinations.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_docs_site.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_duckdb.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_duckdb_files.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_errors.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_events.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_events_server.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_examples.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_export_security.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_exporting.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_exports.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_fips.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_history.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_howto.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_instances.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_integration.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_jdbc.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_jwt.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_key_rotation.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_limits.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_masking.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_masking_requests.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_mcp.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_mcp_keycloak.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_mcp_oauth.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_mongotools.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_multinode.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_observability.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_oidc.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_params.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_parquet.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_pool.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_publishing.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_query_window.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_queryapigate.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_quotas.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_rekey.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_release_check.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_response_cache.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_roles.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_rolling_upgrade.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_secretrefs.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_security_headers.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_settings.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_shared_rate_limit.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_sql_guard_fuzz.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_sql_guard_writes.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_sqlflow.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_sso.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_sso_keycloak.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_tableguard.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_tracing.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_upgrades.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_usage.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.32.0}/tests/test_vault.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: queryapigate
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.32.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: FSL-1.1-MIT
|
|
@@ -667,8 +667,8 @@ Everything is configured through environment variables (all optional):
|
|
|
667
667
|
| `QUERYAPIGATE_CORS_ORIGINS` | unset | Websites allowed to call the API from a browser: comma-separated origins such as `https://app.example.com`, or `*`. Off by default. |
|
|
668
668
|
| `QUERYAPIGATE_RATE_LIMIT` | unset | Requests allowed per client address, e.g. `60/minute` (also `second`, `hour`, `day`). Off by default; a malformed value stops startup. |
|
|
669
669
|
| `QUERYAPIGATE_TRUST_PROXY` | `0` | Number of reverse proxies in front of the app whose `X-Forwarded-*` headers are trusted. Set it (usually `1`) behind nginx, a load balancer or a platform router, or every client looks like the proxy. |
|
|
670
|
-
| `QUERYAPIGATE_AUDIT_SYSLOG_URL` | *(unset)* | Stream every audit entry to a SIEM over syslog: `tls://host:6514` or `tcp://host:514
|
|
671
|
-
| `QUERYAPIGATE_AUDIT_OTLP` | `false` | Stream every audit entry as OTLP log records to the OTLP logs endpoint
|
|
670
|
+
| `QUERYAPIGATE_AUDIT_SYSLOG_URL` | *(unset)* | Stream every audit entry to a SIEM over syslog: `tls://host:6514` or `tcp://host:514`. With `QUERYAPIGATE_AUDIT_SYSLOG_CA`, and `_CERT`/`_KEY` for mutual TLS. |
|
|
671
|
+
| `QUERYAPIGATE_AUDIT_OTLP` | `false` | Stream every audit entry as OTLP log records to the OTLP logs endpoint. |
|
|
672
672
|
| `QUERYAPIGATE_AUDIT_KEY` | *(unset)* | HMAC key (32+ characters) the audit log's hash chain is signed with, so an edit made with only the store can't be re-signed. Several, comma-separated, while rotating it. Unset: plain SHA-256. |
|
|
673
673
|
| `QUERYAPIGATE_HSTS_MAX_AGE` | *(unset)* | Seconds browsers should use only HTTPS for this host (`Strict-Transport-Security`), sent only on requests that arrived over HTTPS. Unset: not sent - set it once every client reaches the server over HTTPS. |
|
|
674
674
|
| `QUERYAPIGATE_POOL_SIZE` | `5` | Idle connections kept per distinct connection setting. `0` turns pooling off. |
|
|
@@ -589,8 +589,8 @@ Everything is configured through environment variables (all optional):
|
|
|
589
589
|
| `QUERYAPIGATE_CORS_ORIGINS` | unset | Websites allowed to call the API from a browser: comma-separated origins such as `https://app.example.com`, or `*`. Off by default. |
|
|
590
590
|
| `QUERYAPIGATE_RATE_LIMIT` | unset | Requests allowed per client address, e.g. `60/minute` (also `second`, `hour`, `day`). Off by default; a malformed value stops startup. |
|
|
591
591
|
| `QUERYAPIGATE_TRUST_PROXY` | `0` | Number of reverse proxies in front of the app whose `X-Forwarded-*` headers are trusted. Set it (usually `1`) behind nginx, a load balancer or a platform router, or every client looks like the proxy. |
|
|
592
|
-
| `QUERYAPIGATE_AUDIT_SYSLOG_URL` | *(unset)* | Stream every audit entry to a SIEM over syslog: `tls://host:6514` or `tcp://host:514
|
|
593
|
-
| `QUERYAPIGATE_AUDIT_OTLP` | `false` | Stream every audit entry as OTLP log records to the OTLP logs endpoint
|
|
592
|
+
| `QUERYAPIGATE_AUDIT_SYSLOG_URL` | *(unset)* | Stream every audit entry to a SIEM over syslog: `tls://host:6514` or `tcp://host:514`. With `QUERYAPIGATE_AUDIT_SYSLOG_CA`, and `_CERT`/`_KEY` for mutual TLS. |
|
|
593
|
+
| `QUERYAPIGATE_AUDIT_OTLP` | `false` | Stream every audit entry as OTLP log records to the OTLP logs endpoint. |
|
|
594
594
|
| `QUERYAPIGATE_AUDIT_KEY` | *(unset)* | HMAC key (32+ characters) the audit log's hash chain is signed with, so an edit made with only the store can't be re-signed. Several, comma-separated, while rotating it. Unset: plain SHA-256. |
|
|
595
595
|
| `QUERYAPIGATE_HSTS_MAX_AGE` | *(unset)* | Seconds browsers should use only HTTPS for this host (`Strict-Transport-Security`), sent only on requests that arrived over HTTPS. Unset: not sent - set it once every client reaches the server over HTTPS. |
|
|
596
596
|
| `QUERYAPIGATE_POOL_SIZE` | `5` | Idle connections kept per distinct connection setting. `0` turns pooling off. |
|
|
@@ -165,25 +165,64 @@ def verify(conn):
|
|
|
165
165
|
return report
|
|
166
166
|
|
|
167
167
|
|
|
168
|
+
def _entry_in(line):
|
|
169
|
+
"""The JSON object a line holds: the whole line (an export file), or the message at its end - a syslog line, as
|
|
170
|
+
rsyslog or syslog-ng write what the stream sent (`<110>1 ... [meta sequenceId="7"] {...}`). None if it has none."""
|
|
171
|
+
try:
|
|
172
|
+
data = json.loads(line)
|
|
173
|
+
return data if isinstance(data, dict) else None
|
|
174
|
+
except ValueError:
|
|
175
|
+
pass
|
|
176
|
+
decoder = json.JSONDecoder()
|
|
177
|
+
start = line.find('{')
|
|
178
|
+
while start != -1:
|
|
179
|
+
try:
|
|
180
|
+
data, end = decoder.raw_decode(line, start)
|
|
181
|
+
except ValueError:
|
|
182
|
+
start = line.find('{', start + 1)
|
|
183
|
+
continue
|
|
184
|
+
if isinstance(data, dict) and not line[end:].strip():
|
|
185
|
+
return data
|
|
186
|
+
start = line.find('{', start + 1)
|
|
187
|
+
return None
|
|
188
|
+
|
|
189
|
+
|
|
168
190
|
def verify_file(path):
|
|
169
|
-
"""
|
|
170
|
-
|
|
171
|
-
|
|
191
|
+
"""A copy of the chain, one entry per line - an export file (QUERYAPIGATE_AUDIT_LOG_EXPORT_FILE), or what a SIEM
|
|
192
|
+
received (ADR 0019): JSON lines, or syslog lines whose message is the entry. Walked in `seq` order from its first
|
|
193
|
+
entry, whose `prev_hash` is taken as given (the copy may have started after the chain did).
|
|
194
|
+
|
|
195
|
+
Delivery is at least once, so a SIEM's copy may hold an entry twice: an exact repeat - the same `seq` and `hash` -
|
|
196
|
+
is dropped, and counted (`repeats`). A repeat whose `hash` differs is never dropped: `conflicting_copies`, naming
|
|
197
|
+
both - one of them isn't what was written."""
|
|
198
|
+
by_seq = {}
|
|
199
|
+
repeats = 0
|
|
172
200
|
with open(path) as f:
|
|
173
201
|
for number, line in enumerate(f, 1):
|
|
174
202
|
if not line.strip():
|
|
175
203
|
continue
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
except ValueError:
|
|
204
|
+
data = _entry_in(line)
|
|
205
|
+
if data is None:
|
|
179
206
|
return {'intact': False, 'break': {'line': number, 'problem': 'not_json'}}
|
|
180
207
|
if 'seq' not in data:
|
|
181
208
|
continue # written before the chain existed
|
|
182
|
-
|
|
183
|
-
|
|
209
|
+
seq, prev_hash, digest = data.pop('seq'), data.pop('prev_hash', None), data.pop('hash', None)
|
|
210
|
+
seen = by_seq.get(seq)
|
|
211
|
+
if seen is None:
|
|
212
|
+
by_seq[seq] = (seq, prev_hash, digest, data, number)
|
|
213
|
+
continue
|
|
214
|
+
if (seen[1], seen[2], seen[3]) == (prev_hash, digest, data):
|
|
215
|
+
repeats += 1
|
|
216
|
+
continue
|
|
217
|
+
return {'intact': False, 'break': {'seq': seq, 'timestamp': data.get('timestamp'),
|
|
218
|
+
'action': data.get('action'), 'problem': 'conflicting_copies',
|
|
219
|
+
'lines': [seen[4], number]}}
|
|
220
|
+
links = [link[:4] for _seq, link in sorted(by_seq.items())]
|
|
184
221
|
if not links:
|
|
185
|
-
return {'intact': True, 'entries': 0}
|
|
186
|
-
|
|
222
|
+
return {'intact': True, 'entries': 0, 'repeats': repeats}
|
|
223
|
+
report = _walk(links, (links[0][0] - 1, links[0][1]))
|
|
224
|
+
report['repeats'] = repeats
|
|
225
|
+
return report
|
|
187
226
|
|
|
188
227
|
|
|
189
228
|
def head_keyed(conn):
|
|
@@ -716,12 +716,17 @@ def _audit_verify(args):
|
|
|
716
716
|
how = ('keyed with QUERYAPIGATE_AUDIT_KEY' if report.get('keyed')
|
|
717
717
|
else 'unkeyed (plain SHA-256: set QUERYAPIGATE_AUDIT_KEY so an edit cannot be re-signed)')
|
|
718
718
|
print(f"Intact: entries {report['first_seq']} to {report['last_seq']}, {how}.")
|
|
719
|
+
if report.get('repeats'):
|
|
720
|
+
print(f" {report['repeats']} repeated entries dropped - the same seq and hash, as at-least-once delivery "
|
|
721
|
+
'can send them')
|
|
719
722
|
if report.get('unchecked_without_key'):
|
|
720
723
|
print(f" {report['unchecked_without_key']} keyed entries not checked - QUERYAPIGATE_AUDIT_KEY is not set")
|
|
721
724
|
return 0
|
|
722
725
|
found = report['break']
|
|
723
726
|
where = f"line {found['line']}" if 'line' in found else (
|
|
724
727
|
f"entry {found['seq']} ({found.get('action')}, {found.get('timestamp')})")
|
|
728
|
+
if found['problem'] == 'conflicting_copies':
|
|
729
|
+
where += f" - lines {found['lines'][0]} and {found['lines'][1]} differ"
|
|
725
730
|
print(f"BROKEN at {where}: {found['problem'].replace('_', ' ')}", file=sys.stderr)
|
|
726
731
|
return 1
|
|
727
732
|
|
|
@@ -915,7 +920,9 @@ def build_parser():
|
|
|
915
920
|
audit_actions = audit_parser.add_subparsers(dest='action')
|
|
916
921
|
audit_verify = audit_actions.add_parser('verify', help='walk the chain and say whether it is intact, or where it '
|
|
917
922
|
'first breaks (exit 0 or 1)')
|
|
918
|
-
audit_verify.add_argument('--file', help='
|
|
923
|
+
audit_verify.add_argument('--file', help='a copy instead of the store: an export file '
|
|
924
|
+
'(QUERYAPIGATE_AUDIT_LOG_EXPORT_FILE), or what a SIEM received - JSON or '
|
|
925
|
+
'syslog lines, repeats allowed')
|
|
919
926
|
audit_verify.set_defaults(func=_audit_verify)
|
|
920
927
|
|
|
921
928
|
history_parser = commands.add_parser('history', help='run history: forget a person (ADR 0016)')
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/forms-
|
|
2
|
-
import{$ as e,C as t,D as n,F as r,H as i,I as a,L as o,Q as s,V as c,Z as l,h as u,o as d,s as f,u as p,z as m}from"./index-
|
|
3
|
-
`)),staleTime:1/0});if(d.isPending)return(0,w.jsx)(r,{});if(!f||!p)return(0,w.jsx)(`div`,{className:`hint`,children:`Could not load the SQL.`});let y=_.data?.formatted||h,b=f.created_at,x=f.last_used_at;return(0,w.jsxs)(`div`,{className:`form`,children:[p.description?(0,w.jsx)(`p`,{className:`d-desc`,children:p.description}):null,(0,w.jsxs)(`dl`,{className:`meta`,children:[(0,w.jsx)(T,{term:`connection`,children:p.connection_name||`—`}),(0,w.jsx)(T,{term:`database`,children:s?(0,w.jsx)(`span`,{className:`tag`,children:s}):(0,w.jsx)(`span`,{className:`dim`,children:`—`})}),(0,w.jsx)(T,{term:`collection`,children:f.collection||`—`}),(0,w.jsx)(T,{term:`status`,children:p.status||`—`}),(0,w.jsx)(T,{term:`author`,children:p.author||`—`}),(0,w.jsx)(T,{term:`created`,children:(0,w.jsx)(g,{value:b})}),(0,w.jsx)(T,{term:`last modified`,children:(0,w.jsx)(g,{value:p.last_modified_at||p.created_at})}),(0,w.jsx)(T,{term:`last used`,children:(0,w.jsx)(g,{value:x,fallback:`never`})}),p.tags.length?(0,w.jsx)(T,{term:`tags`,children:p.tags.join(`, `)}):null]}),(0,w.jsx)(`p`,{className:`sub-h`,style:{marginTop:6},children:`SQL · v${p.version}${f.versions.length>1?` (latest)`:``}`}),(0,w.jsx)(`div`,{children:_.isFetching?(0,w.jsx)(r,{text:`Loading SQL…`}):(0,w.jsxs)(w.Fragment,{children:[(0,w.jsx)(S,{sql:y}),(0,w.jsx)(`div`,{style:{marginTop:6},children:(0,w.jsx)(`button`,{type:`button`,className:`btn sm ghost`,onClick:()=>a(y,u),children:`Copy SQL`})})]})}),(0,w.jsx)(`p`,{className:`sub-h`,style:{marginTop:6},children:`Versions`}),(0,w.jsx)(`div`,{className:`panel`,style:{overflow:`auto`},children:(0,w.jsxs)(`table`,{className:`grid qi-versions`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsx)(`tr`,{children:[`Version`,`Created`,`Last modified`,`Runs`].map(e=>(0,w.jsx)(`th`,{children:e},e))})}),(0,w.jsx)(`tbody`,{children:[...f.versions].reverse().map(e=>(0,w.jsxs)(`tr`,{children:[(0,w.jsx)(`td`,{className:`mono`,children:`v`+e.version}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(g,{value:e.created_at})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(g,{value:e.last_modified_at})}),(0,w.jsx)(`td`,{className:`mono`,style:{textAlign:`right`},children:String(e.run_count??0)})]},e.version))})]})}),(0,w.jsxs)(`div`,{className:`form-actions`,children:[(0,w.jsx)(`button`,{type:`button`,className:`btn`,onClick:l,children:`Close`}),(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:()=>{l(),c(`/queries/${encodeURIComponent(e)}`)},children:`Open in API Repository`})]})]})}var D=(0,C.lazy)(()=>c(()=>import(`./forms-bSOovYin.js`).then(e=>({default:e.QueryForm})),__vite__mapDeps([0,1,2,3,4,5,6]))),O={query:`Q`,collection:`C`,connection:`W`},k=[`Database`,`Created`,`Last modified`,`Last used`,`Version`],A=[`database`,`created`,`modified`,`lastUsed`,`version`],j=()=>new Date().toISOString().slice(0,10);function M(){let e=m(),{openDrawer:t}=o(),n=d(),i=f(),a=u(),s=p(),[c,l]=(0,C.useState)(x),g=e=>{let t={...c,...e};_(t),l(t)},v=s.data??[],S=e=>v.find(t=>t.name===e)?.db??null,T=y(c.conn||void 0),O=T.data?.tables??[],k=b(c.conn||void 0,S(c.conn),c.table&&!c.tableMatches?[c.table]:[]),A=c.table?c.tableMatches??(k.data?k.data[c.table]??[]:null):null,j=n.data??[],M=[...new Set(j.map(e=>S(e.connection_name)).filter(e=>!!e))].sort(),P=i.data??{},F=a.data??{},I=Object.keys(P).sort(),L=Object.keys(F).sort(),R,z=!0;c.conn?T.isPending?R=(0,w.jsx)(`option`,{value:``,children:`Loading tables…`}):T.isError?R=(0,w.jsx)(`option`,{value:``,children:`Schema unavailable`}):(z=!1,R=(0,w.jsxs)(w.Fragment,{children:[(0,w.jsx)(`option`,{value:``,children:`All tables (${O.length})`}),O.map(e=>(0,w.jsx)(`option`,{value:e.name,children:`${e.name} · ${e.columns.length}${e.columns.length===1?` col`:` cols`}`},e.name))]})):R=(0,w.jsx)(`option`,{value:``,children:`Select a connection first`});let B=(e,t)=>h(e,t.name,t.collection,t.connection_name),V;if(n.isPending)V=(0,w.jsx)(r,{});else if(!j.length)V=(0,w.jsx)(`div`,{className:`empty`,children:(0,w.jsx)(`strong`,{children:`No saved queries yet`})});else if(!I.length&&!L.length)V=(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`strong`,{children:`No scoped API keys or roles yet`}),(0,w.jsx)(`span`,{children:`Every query is reachable by the admin key only until a scoped key is created.`})]});else{let n=c.q.trim().toLowerCase(),i=[...j].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,l=n?L.filter(a):L,u=o.length||s.length||l.length,d=o.length?o:u?i:[],f=s.length?s:u?I:[],p=l.length?l:u?L:[];if(c.conn&&(d=d.filter(e=>e.connection_name===c.conn)),c.table&&A===null)V=(0,w.jsx)(r,{text:`Checking which queries reference ${c.table}…`});else if(A&&(d=d.filter(e=>A.includes(e.name))),d.length){let n=d.map(e=>({q:e,dbType:S(e.connection_name),reach:f.filter(t=>B(P[t],e).length).length}));c.db&&(n=n.filter(e=>e.dbType===c.db)),c.reach===`reachable`?n=n.filter(e=>e.reach>0):c.reach===`unreachable`&&(n=n.filter(e=>e.reach===0));let r=c.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,w.jsx)(N,{rows:n,keyCols:f,roleCols:p,keyMap:P,roleMap:F,st:c,onSort:e=>g({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,w.jsx)(E,{name:e.q.name,dbType:e.dbType})})}):(0,w.jsx)(`div`,{className:`empty`,children:(0,w.jsx)(`span`,{children:`No queries match these filters.`})})}else{let i=n?`Nothing matches “${n}”.`:c.table?`“${c.table}” on ${c.conn} is not exposed by any saved query yet.`:c.conn?`No saved query uses “${c.conn}” yet.`:`Nothing matches these filters.`;V=(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`span`,{children:i}),!n&&c.conn?(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:()=>{let n=S(c.conn)===`mongo`,i=c.table?n?JSON.stringify({collection:c.table,filter:{}},null,2):`SELECT * FROM `+c.table:void 0;e(`/queries`),t({title:`New API`,kicker:`POST /api/v1/queries`,content:(0,w.jsx)(C.Suspense,{fallback:(0,w.jsx)(r,{}),children:(0,w.jsx)(D,{starter:{connection:c.conn,sql:i}})})})},children:`New API`+(c.table?` on `+c.table:``)}):null]})}}return(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:`Access map`}),(0,w.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,w.jsxs)(`span`,{className:`legend`,children:[(0,w.jsx)(`span`,{className:`amap-dot`,children:`Q`}),` named query \xA0`,(0,w.jsx)(`span`,{className:`amap-dot`,children:`C`}),` `,`collection \xA0`,(0,w.jsx)(`span`,{className:`amap-dot conn`,children:`W`}),` whole connection`]})]})]}),(0,w.jsx)(`span`,{className:`spacer`}),(0,w.jsxs)(`select`,{id:`accessmap-conn-filter`,"aria-label":`Connection`,value:v.some(e=>e.name===c.conn)?c.conn:``,onChange:e=>g({conn:e.target.value,table:``,tableMatches:null}),children:[(0,w.jsx)(`option`,{value:``,children:`All connections`}),[...v].sort((e,t)=>e.name<t.name?-1:1).map(e=>(0,w.jsx)(`option`,{value:e.name,children:e.name},e.name))]}),(0,w.jsx)(`select`,{id:`accessmap-table-filter`,"aria-label":`Table`,disabled:z,value:O.some(e=>e.name===c.table)?c.table:``,onChange:e=>g({table:e.target.value,tableMatches:null}),children:R}),(0,w.jsxs)(`select`,{id:`accessmap-db-filter`,"aria-label":`Database`,value:M.includes(c.db)?c.db:``,onChange:e=>g({db:e.target.value}),children:[(0,w.jsx)(`option`,{value:``,children:`All databases`}),M.map(e=>(0,w.jsx)(`option`,{value:e,children:e},e))]}),(0,w.jsxs)(`select`,{id:`accessmap-reach-filter`,"aria-label":`Reach`,value:c.reach,onChange:e=>g({reach:e.target.value}),children:[(0,w.jsx)(`option`,{value:``,children:`Any reach`}),(0,w.jsx)(`option`,{value:`reachable`,children:`Reachable by a key`}),(0,w.jsx)(`option`,{value:`unreachable`,children:`Reachable by no key`})]}),(0,w.jsx)(`input`,{id:`accessmap-filter`,className:`search`,type:`search`,placeholder:`Filter by query or key…`,value:c.q,onChange:e=>g({q:e.target.value})})]}),(0,w.jsx)(`div`,{id:`accessmap-body`,className:`panel`,children:V})]})}function N({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,d=(e,t=null,n=null)=>u(e,t,n)?l.dir===1?` ▲`:` ▼`:``,f=(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})},p=(e,t,n)=>(0,w.jsx)(`th`,{className:t,onClick:()=>f(n),children:e+d(n)},n),m=(e,t,n,r)=>(0,w.jsxs)(`th`,{className:`amap-key-head amap-sortable`+(n?` revoked`:``),title:n?e+n:e,onClick:()=>f(`col`,r,e),children:[(0,w.jsxs)(`span`,{className:`amap-key-name`,children:[e,d(`col`,r,e),n?(0,w.jsx)(`span`,{className:`dim`,children:n}):null]}),(0,w.jsxs)(`span`,{className:`amap-key-sub`,children:[(t.allow_writes?`Read/write`:`Read-only`)+` · `+(t.rate_limit||`server default`),t.mask?.length?(0,w.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=h(e,t.name,t.collection,t.connection_name);if(!i.length)return(0,w.jsx)(`td`,{className:`amap-cell`},r);let a=[...new Set(i.map(e=>O[e.kind]))],o=a.length===1&&a[0]===`W`;return(0,w.jsx)(`td`,{className:`amap-cell`,title:i.map(e=>e.label).join(`, `),children:(0,w.jsx)(`span`,{className:`amap-dot`+(n?` role`:``)+(o?` conn`:``),children:a.join(`·`)})},r)},v=j();return(0,w.jsx)(`div`,{style:{overflow:`auto`},children:(0,w.jsxs)(`table`,{className:`amap`,children:[(0,w.jsxs)(`thead`,{children:[(0,w.jsxs)(`tr`,{children:[(0,w.jsx)(`th`,{colSpan:3+k.length}),t.length?(0,w.jsx)(`th`,{colSpan:t.length,className:`amap-group`,children:`API KEYS`}):null,n.length?(0,w.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,w.jsxs)(`tr`,{children:[p(`Query`,`amap-query amap-sortable`,`query`),A.map((e,t)=>p(k[t],`amap-sortable`,e)),p(`Connection`,`amap-sortable`,`connection`),p(`Reach`,`num amap-sortable`,`reach`),t.map(e=>{let t=r[e],n=!!(t.expires_at&&t.expires_at<v);return m(e,t,t.active?n?` (expired ${t.expires_at})`:``:` (revoked)`,`key`)}),n.map(e=>m(e,i[e],``,`role`))]})]}),(0,w.jsx)(`tbody`,{children:e.map(e=>{let a=e.q;return(0,w.jsxs)(`tr`,{children:[(0,w.jsxs)(`td`,{className:`amap-query`,children:[(0,w.jsxs)(`div`,{className:`amap-q-row`,children:[(0,w.jsx)(`button`,{type:`button`,className:`amap-q-name`,title:`Open ${a.name} in API Repository`,onClick:()=>s(a.name),children:a.name}),(0,w.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,w.jsx)(`span`,{className:`amap-coll`,children:a.collection}):null]}),(0,w.jsx)(`td`,{children:e.dbType?(0,w.jsx)(`span`,{className:`tag`,children:e.dbType}):(0,w.jsx)(`span`,{className:`dim`,children:`—`})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(g,{value:a.created_at})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(g,{value:a.updated_at})}),(0,w.jsx)(`td`,{className:a.last_used_at?`mono`:`mono dim`,children:(0,w.jsx)(g,{value:a.last_used_at,fallback:`never`})}),(0,w.jsx)(`td`,{className:`mono`,children:`v`+a.latest_version}),(0,w.jsx)(`td`,{className:`mono dim`,children:a.connection_name||`—`}),(0,w.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,w.jsx)(`tfoot`,{children:(0,w.jsxs)(`tr`,{children:[(0,w.jsx)(`td`,{className:`amap-query`,children:`Reaches`}),k.map(e=>(0,w.jsx)(`td`,{},e)),(0,w.jsx)(`td`,{}),(0,w.jsx)(`td`,{}),t.map(t=>(0,w.jsx)(`td`,{className:`num`,children:`${e.filter(e=>h(r[t],e.q.name,e.q.collection,e.q.connection_name).length).length} / ${e.length}`},t)),n.map(t=>(0,w.jsx)(`td`,{className:`num`,children:`${e.filter(e=>h(i[t],e.q.name,e.q.collection,e.q.connection_name).length).length} / ${e.length}`},t))]})})]})})}export{M as AccessMapPage};
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/forms-DR5CXITG.js","assets/index-BNMV1syF.js","assets/index-BLd0t6vZ.css","assets/SchemaBrowser-D57iE2Kg.js","assets/useMutation-DCzNFnov.js","assets/reach-0Yvn3NAt.js","assets/CodeBox-B8iq2Oop.js"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{$ as e,C as t,D as n,F as r,H as i,I as a,L as o,Q as s,V as c,Z as l,h as u,o as d,s as f,u as p,z as m}from"./index-BNMV1syF.js";import{a as h}from"./reach-0Yvn3NAt.js";import{t as g}from"./Time-CR17Ozn9.js";import{f as _,i as v,n as y,r as b,u as x}from"./SchemaBrowser-D57iE2Kg.js";import{t as S}from"./CodeBox-B8iq2Oop.js";var C=e(s(),1),w=l();function T({term:e,children:t}){return(0,w.jsxs)(`div`,{children:[(0,w.jsx)(`dt`,{children:e}),(0,w.jsx)(`dd`,{title:typeof t==`string`?t:void 0,children:t})]})}function E({name:e,dbType:s}){let c=m(),{closeDrawer:l,toast:u}=o(),d=i({queryKey:[`queries`,`detail`,e,`info`],queryFn:async()=>n(await t.GET(`/api/v1/queries/{name}`,{params:{path:{name:e}}})),retry:!1}),f=d.data,p=f?.versions[f.versions.length-1],h=p?.query_type===`mongo`?JSON.stringify(p.mongo??{},null,2):p?.sql??``,_=i({queryKey:[`queries`,`flow`,e,p?.version],queryFn:()=>v(e,p.version),enabled:!(!p||p.query_type!==`sql`||h.includes(`
|
|
3
|
+
`)),staleTime:1/0});if(d.isPending)return(0,w.jsx)(r,{});if(!f||!p)return(0,w.jsx)(`div`,{className:`hint`,children:`Could not load the SQL.`});let y=_.data?.formatted||h,b=f.created_at,x=f.last_used_at;return(0,w.jsxs)(`div`,{className:`form`,children:[p.description?(0,w.jsx)(`p`,{className:`d-desc`,children:p.description}):null,(0,w.jsxs)(`dl`,{className:`meta`,children:[(0,w.jsx)(T,{term:`connection`,children:p.connection_name||`—`}),(0,w.jsx)(T,{term:`database`,children:s?(0,w.jsx)(`span`,{className:`tag`,children:s}):(0,w.jsx)(`span`,{className:`dim`,children:`—`})}),(0,w.jsx)(T,{term:`collection`,children:f.collection||`—`}),(0,w.jsx)(T,{term:`status`,children:p.status||`—`}),(0,w.jsx)(T,{term:`author`,children:p.author||`—`}),(0,w.jsx)(T,{term:`created`,children:(0,w.jsx)(g,{value:b})}),(0,w.jsx)(T,{term:`last modified`,children:(0,w.jsx)(g,{value:p.last_modified_at||p.created_at})}),(0,w.jsx)(T,{term:`last used`,children:(0,w.jsx)(g,{value:x,fallback:`never`})}),p.tags.length?(0,w.jsx)(T,{term:`tags`,children:p.tags.join(`, `)}):null]}),(0,w.jsx)(`p`,{className:`sub-h`,style:{marginTop:6},children:`SQL · v${p.version}${f.versions.length>1?` (latest)`:``}`}),(0,w.jsx)(`div`,{children:_.isFetching?(0,w.jsx)(r,{text:`Loading SQL…`}):(0,w.jsxs)(w.Fragment,{children:[(0,w.jsx)(S,{sql:y}),(0,w.jsx)(`div`,{style:{marginTop:6},children:(0,w.jsx)(`button`,{type:`button`,className:`btn sm ghost`,onClick:()=>a(y,u),children:`Copy SQL`})})]})}),(0,w.jsx)(`p`,{className:`sub-h`,style:{marginTop:6},children:`Versions`}),(0,w.jsx)(`div`,{className:`panel`,style:{overflow:`auto`},children:(0,w.jsxs)(`table`,{className:`grid qi-versions`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsx)(`tr`,{children:[`Version`,`Created`,`Last modified`,`Runs`].map(e=>(0,w.jsx)(`th`,{children:e},e))})}),(0,w.jsx)(`tbody`,{children:[...f.versions].reverse().map(e=>(0,w.jsxs)(`tr`,{children:[(0,w.jsx)(`td`,{className:`mono`,children:`v`+e.version}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(g,{value:e.created_at})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(g,{value:e.last_modified_at})}),(0,w.jsx)(`td`,{className:`mono`,style:{textAlign:`right`},children:String(e.run_count??0)})]},e.version))})]})}),(0,w.jsxs)(`div`,{className:`form-actions`,children:[(0,w.jsx)(`button`,{type:`button`,className:`btn`,onClick:l,children:`Close`}),(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:()=>{l(),c(`/queries/${encodeURIComponent(e)}`)},children:`Open in API Repository`})]})]})}var D=(0,C.lazy)(()=>c(()=>import(`./forms-DR5CXITG.js`).then(e=>({default:e.QueryForm})),__vite__mapDeps([0,1,2,3,4,5,6]))),O={query:`Q`,collection:`C`,connection:`W`},k=[`Database`,`Created`,`Last modified`,`Last used`,`Version`],A=[`database`,`created`,`modified`,`lastUsed`,`version`],j=()=>new Date().toISOString().slice(0,10);function M(){let e=m(),{openDrawer:t}=o(),n=d(),i=f(),a=u(),s=p(),[c,l]=(0,C.useState)(x),g=e=>{let t={...c,...e};_(t),l(t)},v=s.data??[],S=e=>v.find(t=>t.name===e)?.db??null,T=y(c.conn||void 0),O=T.data?.tables??[],k=b(c.conn||void 0,S(c.conn),c.table&&!c.tableMatches?[c.table]:[]),A=c.table?c.tableMatches??(k.data?k.data[c.table]??[]:null):null,j=n.data??[],M=[...new Set(j.map(e=>S(e.connection_name)).filter(e=>!!e))].sort(),P=i.data??{},F=a.data??{},I=Object.keys(P).sort(),L=Object.keys(F).sort(),R,z=!0;c.conn?T.isPending?R=(0,w.jsx)(`option`,{value:``,children:`Loading tables…`}):T.isError?R=(0,w.jsx)(`option`,{value:``,children:`Schema unavailable`}):(z=!1,R=(0,w.jsxs)(w.Fragment,{children:[(0,w.jsx)(`option`,{value:``,children:`All tables (${O.length})`}),O.map(e=>(0,w.jsx)(`option`,{value:e.name,children:`${e.name} · ${e.columns.length}${e.columns.length===1?` col`:` cols`}`},e.name))]})):R=(0,w.jsx)(`option`,{value:``,children:`Select a connection first`});let B=(e,t)=>h(e,t.name,t.collection,t.connection_name),V;if(n.isPending)V=(0,w.jsx)(r,{});else if(!j.length)V=(0,w.jsx)(`div`,{className:`empty`,children:(0,w.jsx)(`strong`,{children:`No saved queries yet`})});else if(!I.length&&!L.length)V=(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`strong`,{children:`No scoped API keys or roles yet`}),(0,w.jsx)(`span`,{children:`Every query is reachable by the admin key only until a scoped key is created.`})]});else{let n=c.q.trim().toLowerCase(),i=[...j].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,l=n?L.filter(a):L,u=o.length||s.length||l.length,d=o.length?o:u?i:[],f=s.length?s:u?I:[],p=l.length?l:u?L:[];if(c.conn&&(d=d.filter(e=>e.connection_name===c.conn)),c.table&&A===null)V=(0,w.jsx)(r,{text:`Checking which queries reference ${c.table}…`});else if(A&&(d=d.filter(e=>A.includes(e.name))),d.length){let n=d.map(e=>({q:e,dbType:S(e.connection_name),reach:f.filter(t=>B(P[t],e).length).length}));c.db&&(n=n.filter(e=>e.dbType===c.db)),c.reach===`reachable`?n=n.filter(e=>e.reach>0):c.reach===`unreachable`&&(n=n.filter(e=>e.reach===0));let r=c.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,w.jsx)(N,{rows:n,keyCols:f,roleCols:p,keyMap:P,roleMap:F,st:c,onSort:e=>g({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,w.jsx)(E,{name:e.q.name,dbType:e.dbType})})}):(0,w.jsx)(`div`,{className:`empty`,children:(0,w.jsx)(`span`,{children:`No queries match these filters.`})})}else{let i=n?`Nothing matches “${n}”.`:c.table?`“${c.table}” on ${c.conn} is not exposed by any saved query yet.`:c.conn?`No saved query uses “${c.conn}” yet.`:`Nothing matches these filters.`;V=(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`span`,{children:i}),!n&&c.conn?(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:()=>{let n=S(c.conn)===`mongo`,i=c.table?n?JSON.stringify({collection:c.table,filter:{}},null,2):`SELECT * FROM `+c.table:void 0;e(`/queries`),t({title:`New API`,kicker:`POST /api/v1/queries`,content:(0,w.jsx)(C.Suspense,{fallback:(0,w.jsx)(r,{}),children:(0,w.jsx)(D,{starter:{connection:c.conn,sql:i}})})})},children:`New API`+(c.table?` on `+c.table:``)}):null]})}}return(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:`Access map`}),(0,w.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,w.jsxs)(`span`,{className:`legend`,children:[(0,w.jsx)(`span`,{className:`amap-dot`,children:`Q`}),` named query \xA0`,(0,w.jsx)(`span`,{className:`amap-dot`,children:`C`}),` `,`collection \xA0`,(0,w.jsx)(`span`,{className:`amap-dot conn`,children:`W`}),` whole connection`]})]})]}),(0,w.jsx)(`span`,{className:`spacer`}),(0,w.jsxs)(`select`,{id:`accessmap-conn-filter`,"aria-label":`Connection`,value:v.some(e=>e.name===c.conn)?c.conn:``,onChange:e=>g({conn:e.target.value,table:``,tableMatches:null}),children:[(0,w.jsx)(`option`,{value:``,children:`All connections`}),[...v].sort((e,t)=>e.name<t.name?-1:1).map(e=>(0,w.jsx)(`option`,{value:e.name,children:e.name},e.name))]}),(0,w.jsx)(`select`,{id:`accessmap-table-filter`,"aria-label":`Table`,disabled:z,value:O.some(e=>e.name===c.table)?c.table:``,onChange:e=>g({table:e.target.value,tableMatches:null}),children:R}),(0,w.jsxs)(`select`,{id:`accessmap-db-filter`,"aria-label":`Database`,value:M.includes(c.db)?c.db:``,onChange:e=>g({db:e.target.value}),children:[(0,w.jsx)(`option`,{value:``,children:`All databases`}),M.map(e=>(0,w.jsx)(`option`,{value:e,children:e},e))]}),(0,w.jsxs)(`select`,{id:`accessmap-reach-filter`,"aria-label":`Reach`,value:c.reach,onChange:e=>g({reach:e.target.value}),children:[(0,w.jsx)(`option`,{value:``,children:`Any reach`}),(0,w.jsx)(`option`,{value:`reachable`,children:`Reachable by a key`}),(0,w.jsx)(`option`,{value:`unreachable`,children:`Reachable by no key`})]}),(0,w.jsx)(`input`,{id:`accessmap-filter`,className:`search`,type:`search`,placeholder:`Filter by query or key…`,value:c.q,onChange:e=>g({q:e.target.value})})]}),(0,w.jsx)(`div`,{id:`accessmap-body`,className:`panel`,children:V})]})}function N({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,d=(e,t=null,n=null)=>u(e,t,n)?l.dir===1?` ▲`:` ▼`:``,f=(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})},p=(e,t,n)=>(0,w.jsx)(`th`,{className:t,onClick:()=>f(n),children:e+d(n)},n),m=(e,t,n,r)=>(0,w.jsxs)(`th`,{className:`amap-key-head amap-sortable`+(n?` revoked`:``),title:n?e+n:e,onClick:()=>f(`col`,r,e),children:[(0,w.jsxs)(`span`,{className:`amap-key-name`,children:[e,d(`col`,r,e),n?(0,w.jsx)(`span`,{className:`dim`,children:n}):null]}),(0,w.jsxs)(`span`,{className:`amap-key-sub`,children:[(t.allow_writes?`Read/write`:`Read-only`)+` · `+(t.rate_limit||`server default`),t.mask?.length?(0,w.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=h(e,t.name,t.collection,t.connection_name);if(!i.length)return(0,w.jsx)(`td`,{className:`amap-cell`},r);let a=[...new Set(i.map(e=>O[e.kind]))],o=a.length===1&&a[0]===`W`;return(0,w.jsx)(`td`,{className:`amap-cell`,title:i.map(e=>e.label).join(`, `),children:(0,w.jsx)(`span`,{className:`amap-dot`+(n?` role`:``)+(o?` conn`:``),children:a.join(`·`)})},r)},v=j();return(0,w.jsx)(`div`,{style:{overflow:`auto`},children:(0,w.jsxs)(`table`,{className:`amap`,children:[(0,w.jsxs)(`thead`,{children:[(0,w.jsxs)(`tr`,{children:[(0,w.jsx)(`th`,{colSpan:3+k.length}),t.length?(0,w.jsx)(`th`,{colSpan:t.length,className:`amap-group`,children:`API KEYS`}):null,n.length?(0,w.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,w.jsxs)(`tr`,{children:[p(`Query`,`amap-query amap-sortable`,`query`),A.map((e,t)=>p(k[t],`amap-sortable`,e)),p(`Connection`,`amap-sortable`,`connection`),p(`Reach`,`num amap-sortable`,`reach`),t.map(e=>{let t=r[e],n=!!(t.expires_at&&t.expires_at<v);return m(e,t,t.active?n?` (expired ${t.expires_at})`:``:` (revoked)`,`key`)}),n.map(e=>m(e,i[e],``,`role`))]})]}),(0,w.jsx)(`tbody`,{children:e.map(e=>{let a=e.q;return(0,w.jsxs)(`tr`,{children:[(0,w.jsxs)(`td`,{className:`amap-query`,children:[(0,w.jsxs)(`div`,{className:`amap-q-row`,children:[(0,w.jsx)(`button`,{type:`button`,className:`amap-q-name`,title:`Open ${a.name} in API Repository`,onClick:()=>s(a.name),children:a.name}),(0,w.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,w.jsx)(`span`,{className:`amap-coll`,children:a.collection}):null]}),(0,w.jsx)(`td`,{children:e.dbType?(0,w.jsx)(`span`,{className:`tag`,children:e.dbType}):(0,w.jsx)(`span`,{className:`dim`,children:`—`})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(g,{value:a.created_at})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(g,{value:a.updated_at})}),(0,w.jsx)(`td`,{className:a.last_used_at?`mono`:`mono dim`,children:(0,w.jsx)(g,{value:a.last_used_at,fallback:`never`})}),(0,w.jsx)(`td`,{className:`mono`,children:`v`+a.latest_version}),(0,w.jsx)(`td`,{className:`mono dim`,children:a.connection_name||`—`}),(0,w.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,w.jsx)(`tfoot`,{children:(0,w.jsxs)(`tr`,{children:[(0,w.jsx)(`td`,{className:`amap-query`,children:`Reaches`}),k.map(e=>(0,w.jsx)(`td`,{},e)),(0,w.jsx)(`td`,{}),(0,w.jsx)(`td`,{}),t.map(t=>(0,w.jsx)(`td`,{className:`num`,children:`${e.filter(e=>h(r[t],e.q.name,e.q.collection,e.q.connection_name).length).length} / ${e.length}`},t)),n.map(t=>(0,w.jsx)(`td`,{className:`num`,children:`${e.filter(e=>h(i[t],e.q.name,e.q.collection,e.q.connection_name).length).length} / ${e.length}`},t))]})})]})})}export{M as AccessMapPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./useMutation-BcyO6Hjg.js";import{$ as t,A as n,C as r,D as i,F as a,H as o,L as s,N as c,O as l,P as u,Q as d,S as f,X as p,Z as m,c as h,d as g,k as _,m as v}from"./index-DvCopLMA.js";import{t as y}from"./Time-KeJDv2te.js";import{t as b}from"./forms-StJt-xsd.js";import{n as x,t as S}from"./icons-CeIi0TUG.js";var C=t(d(),1),w=m(),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=>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}=s();return t=>t instanceof f?e(t.message,{status:t.status}):e(t.message)}function j(e){let t=g(`admins.read`,`other administrators`);return o({queryKey:[`administrators`],queryFn:async()=>(await t.check(),i(await r.GET(`/api/v1/administrators`)).items),enabled:e,retry:!1})}function M(e){return o({queryKey:[`administrators`,e,`tokens`],queryFn:async()=>i(await r.GET(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:e}}})).items,retry:!1})}function N(){let e=v(),t=h(),n=!!e.data&&t(`admins.read`),r=j(n),{openDrawer:i}=s(),o=()=>i({title:`New administrator`,content:(0,w.jsx)(I,{})}),c;return c=e.isPending?(0,w.jsx)(a,{}):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)(a,{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:o,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:o,children:`New administrator`})]}),(0,w.jsx)(`div`,{id:`administrators-table`,className:`panel`,children:c})]})}function P({items:e,self:t}){let{openDrawer:n,toast:i}=s(),a=A(),o=p(),c=async e=>{if(window.confirm(`Remove administrator '${e}'? Every token of theirs stops working immediately.`))try{l(await r.DELETE(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),i(`Removed `+e),o.invalidateQueries({queryKey:[`administrators`]})}catch(e){a(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:()=>n({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:()=>n({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 c(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=o({queryKey:[`administrators`,`detail`,e],queryFn:async()=>_(await r.GET(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),enabled:!!e,retry:!1,gcTime:0});return e&&t.isPending?(0,w.jsx)(a,{}):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:t,existing:a,etag:o}){let l=!!t,{closeDrawer:d,openDrawer:f,showError:m,toast:h}=s(),g=A(),_=p(),[v,y]=(0,C.useState)(t??``),[x,S]=(0,C.useState)(a?.role??`developer`),[E,j]=(0,C.useState)(a?.email??``),[M,N]=(0,C.useState)(!a||a.active),P=n(),F=a?.sso?.status===`linked`,[I,L]=(0,C.useState)(a?.sso?.status??`none`),R=e({mutationFn:async()=>{let e={role:x,email:E.trim()||null};if(l)return i(await r.PATCH(`/api/v1/administrators/{name}`,{params:{path:{name:t},header:o?{"If-Match":o}:{}},body:{...F?{email:e.email}:e,active:M,...I===(a?.sso?.status??`none`)?{}:{sso:I===`pending`?`pending`:null}}})),null;let n=i(await r.POST(`/api/v1/administrators`,{body:{name:v.trim(),...e}}));return i(await r.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:n.name}},body:{label:`first token`,expires_at:O(D)}}))},onSuccess:e=>{if(m(``),_.invalidateQueries({queryKey:[`administrators`]}),!e){d(),h(`Updated `+t);return}f({title:`Administrator created`,kicker:e.admin,content:(0,w.jsx)(b,{secret:e.secret,label:`Admin token for ${e.admin} (expires ${e.expires_at})`,hint:k})})},onError:g});return(0,w.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{if(e.preventDefault(),!l&&!v.trim()){m(`A name is required.`,{errors:{name:`This field is required`}});return}R.mutate()},children:[(0,w.jsx)(c,{id:`a-name`,label:`Name`,hint:l?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:l,autoFocus:!l,onChange:e=>y(e.target.value)})}),(0,w.jsx)(c,{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)(c,{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)})}),l&&(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`})]}),l&&P.data?.sso&&(0,w.jsx)(c,{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`})]})}),!l&&(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)(u,{onCancel:d,children:(0,w.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:R.isPending,children:l?`Save`:`Create`})})]})}function R({name:t}){let n=M(t),o=p(),{toast:u}=s(),d=A(),[f,m]=(0,C.useState)(``),[h,g]=(0,C.useState)(O(D)),[_,v]=(0,C.useState)(null),x=()=>void o.invalidateQueries({queryKey:[`administrators`]}),S=e({mutationFn:async()=>i(await r.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:t}},body:{label:f.trim()||null,expires_at:h||null}})),onSuccess:e=>{v(e),m(``),x()},onError:d}),T=async e=>{if(window.confirm(`Revoke ${e.label?`'${e.label}'`:e.id}? Anything using it stops working now.`))try{l(await r.DELETE(`/api/v1/administrators/{name}/tokens/{token_id}`,{params:{path:{name:t,token_id:e.id}}})),u(`Revoked `+(e.label??e.id)),x()}catch(e){d(e)}},E=new Date().toISOString().slice(0,10);return(0,w.jsxs)(`div`,{className:`form`,id:`admin-tokens`,children:[n.isPending?(0,w.jsx)(a,{text:`Loading tokens…`}):n.isError?(0,w.jsx)(`div`,{className:`hint`,children:n.error.message}):n.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:n.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.`}),_?(0,w.jsx)(b,{secret:_.secret,label:`New token${_.label?` '${_.label}'`:``} (${_.expires_at?`expires ${_.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)(c,{id:`t-label`,label:`New token: label`,hint:`What it's for - laptop, ci.`,children:(0,w.jsx)(`input`,{id:`t-label`,value:f,autoComplete:`off`,onChange:e=>m(e.target.value)})}),(0,w.jsx)(c,{id:`t-expires`,label:`Expires`,hint:`Leave blank for no expiry.`,children:(0,w.jsx)(`input`,{id:`t-expires`,type:`date`,value:h,onChange:e=>g(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{t as e}from"./useMutation-DCzNFnov.js";import{$ as t,A as n,C as r,D as i,F as a,H as o,L as s,N as c,O as l,P as u,Q as d,S as f,X as p,Z as m,c as h,d as g,k as _,m as v}from"./index-BNMV1syF.js";import{t as y}from"./Time-CR17Ozn9.js";import{t as b}from"./forms-C3dw2wG6.js";import{n as x,t as S}from"./icons-C-i_90Gs.js";var C=t(d(),1),w=m(),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=>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}=s();return t=>t instanceof f?e(t.message,{status:t.status}):e(t.message)}function j(e){let t=g(`admins.read`,`other administrators`);return o({queryKey:[`administrators`],queryFn:async()=>(await t.check(),i(await r.GET(`/api/v1/administrators`)).items),enabled:e,retry:!1})}function M(e){return o({queryKey:[`administrators`,e,`tokens`],queryFn:async()=>i(await r.GET(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:e}}})).items,retry:!1})}function N(){let e=v(),t=h(),n=!!e.data&&t(`admins.read`),r=j(n),{openDrawer:i}=s(),o=()=>i({title:`New administrator`,content:(0,w.jsx)(I,{})}),c;return c=e.isPending?(0,w.jsx)(a,{}):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)(a,{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:o,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:o,children:`New administrator`})]}),(0,w.jsx)(`div`,{id:`administrators-table`,className:`panel`,children:c})]})}function P({items:e,self:t}){let{openDrawer:n,toast:i}=s(),a=A(),o=p(),c=async e=>{if(window.confirm(`Remove administrator '${e}'? Every token of theirs stops working immediately.`))try{l(await r.DELETE(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),i(`Removed `+e),o.invalidateQueries({queryKey:[`administrators`]})}catch(e){a(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:()=>n({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:()=>n({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 c(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=o({queryKey:[`administrators`,`detail`,e],queryFn:async()=>_(await r.GET(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),enabled:!!e,retry:!1,gcTime:0});return e&&t.isPending?(0,w.jsx)(a,{}):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:t,existing:a,etag:o}){let l=!!t,{closeDrawer:d,openDrawer:f,showError:m,toast:h}=s(),g=A(),_=p(),[v,y]=(0,C.useState)(t??``),[x,S]=(0,C.useState)(a?.role??`developer`),[E,j]=(0,C.useState)(a?.email??``),[M,N]=(0,C.useState)(!a||a.active),P=n(),F=a?.sso?.status===`linked`,[I,L]=(0,C.useState)(a?.sso?.status??`none`),R=e({mutationFn:async()=>{let e={role:x,email:E.trim()||null};if(l)return i(await r.PATCH(`/api/v1/administrators/{name}`,{params:{path:{name:t},header:o?{"If-Match":o}:{}},body:{...F?{email:e.email}:e,active:M,...I===(a?.sso?.status??`none`)?{}:{sso:I===`pending`?`pending`:null}}})),null;let n=i(await r.POST(`/api/v1/administrators`,{body:{name:v.trim(),...e}}));return i(await r.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:n.name}},body:{label:`first token`,expires_at:O(D)}}))},onSuccess:e=>{if(m(``),_.invalidateQueries({queryKey:[`administrators`]}),!e){d(),h(`Updated `+t);return}f({title:`Administrator created`,kicker:e.admin,content:(0,w.jsx)(b,{secret:e.secret,label:`Admin token for ${e.admin} (expires ${e.expires_at})`,hint:k})})},onError:g});return(0,w.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{if(e.preventDefault(),!l&&!v.trim()){m(`A name is required.`,{errors:{name:`This field is required`}});return}R.mutate()},children:[(0,w.jsx)(c,{id:`a-name`,label:`Name`,hint:l?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:l,autoFocus:!l,onChange:e=>y(e.target.value)})}),(0,w.jsx)(c,{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)(c,{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)})}),l&&(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`})]}),l&&P.data?.sso&&(0,w.jsx)(c,{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`})]})}),!l&&(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)(u,{onCancel:d,children:(0,w.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:R.isPending,children:l?`Save`:`Create`})})]})}function R({name:t}){let n=M(t),o=p(),{toast:u}=s(),d=A(),[f,m]=(0,C.useState)(``),[h,g]=(0,C.useState)(O(D)),[_,v]=(0,C.useState)(null),x=()=>void o.invalidateQueries({queryKey:[`administrators`]}),S=e({mutationFn:async()=>i(await r.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:t}},body:{label:f.trim()||null,expires_at:h||null}})),onSuccess:e=>{v(e),m(``),x()},onError:d}),T=async e=>{if(window.confirm(`Revoke ${e.label?`'${e.label}'`:e.id}? Anything using it stops working now.`))try{l(await r.DELETE(`/api/v1/administrators/{name}/tokens/{token_id}`,{params:{path:{name:t,token_id:e.id}}})),u(`Revoked `+(e.label??e.id)),x()}catch(e){d(e)}},E=new Date().toISOString().slice(0,10);return(0,w.jsxs)(`div`,{className:`form`,id:`admin-tokens`,children:[n.isPending?(0,w.jsx)(a,{text:`Loading tokens…`}):n.isError?(0,w.jsx)(`div`,{className:`hint`,children:n.error.message}):n.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:n.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.`}),_?(0,w.jsx)(b,{secret:_.secret,label:`New token${_.label?` '${_.label}'`:``} (${_.expires_at?`expires ${_.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)(c,{id:`t-label`,label:`New token: label`,hint:`What it's for - laptop, ci.`,children:(0,w.jsx)(`input`,{id:`t-label`,value:f,autoComplete:`off`,onChange:e=>m(e.target.value)})}),(0,w.jsx)(c,{id:`t-expires`,label:`Expires`,hint:`Leave blank for no expiry.`,children:(0,w.jsx)(`input`,{id:`t-expires`,type:`date`,value:h,onChange:e=>g(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{$ as e,C as t,D as n,F as r,H as i,Q as a,Z as o}from"./index-
|
|
1
|
+
import{$ as e,C as t,D as n,F as r,H as i,Q as a,Z as o}from"./index-BNMV1syF.js";import{t as s}from"./Time-CR17Ozn9.js";import{t as c}from"./CodeBox-B8iq2Oop.js";var l=e(a(),1),u=o(),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,a]=(0,l.useState)(`24h`),[o,c]=(0,l.useState)(null),m=d.find(t=>t.id===e).hours,g=i({queryKey:[`usage`,`agents`,e],queryFn:async()=>n(await t.GET(`/api/v1/usage/agents`,{params:{query:{hours:m}}})),retry:!1}),_=g.data?.items??[],v;v=g.isPending?(0,u.jsx)(r,{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":o===e.agent,onClick:()=>c(o===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:``})})]}),o===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=>a(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{$ as e,B as t,F as n,Q as r,Z as i,_ as a,b as o,x as s,y as c,z as l}from"./index-
|
|
1
|
+
import{$ as e,B as t,F as n,Q as r,Z as i,_ as a,b as o,x as s,y as c,z as l}from"./index-BNMV1syF.js";import{t as u}from"./Time-CR17Ozn9.js";import{n as d}from"./state-O13cl7cO.js";var f=e(r(),1),p=i(),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=l();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:()=>o(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=l(),r=t(),i=a.find(e=>e.id===r.tab),{query:o,active:c,dismissed:d}=s(),[m,h]=(0,f.useState)(!1),g=e=>!i||i.kinds.includes(e.kind),y=c.filter(g),b=d.filter(g),x=e=>c.filter(t=>!e||e.includes(t.kind)),S;S=o.isPending?(0,p.jsx)(n,{text:`Checking…`}):o.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:o.error.message}),(0,p.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>void o.refetch(),children:`Retry`})]})}):(0,p.jsxs)(p.Fragment,{children:[i&&(0,p.jsx)(`p`,{className:`alerts-about`,children:i.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:i?`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,n,r)=>(0,p.jsxs)(`button`,{type:`button`,role:`tab`,"aria-selected":(i?.id??`all`)===t,className:`minitab`+((i?.id??`all`)===t?` active`:``),onClick:()=>e(t===`all`?`/alerts`:`/alerts/`+t),children:[n,o.data?(0,p.jsx)(`span`,{className:`n`+(r.length?` on`:``)+(r.some(e=>e.severity===`critical`)?` critical`:``),children:r.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.`,o.data?(0,p.jsxs)(p.Fragment,{children:[` Last checked `,(0,p.jsx)(u,{value:o.data.checked_at}),`.`]}):null]})]}),(0,p.jsx)(`span`,{className:`spacer`}),(0,p.jsx)(`button`,{id:`refresh-alerts`,type:`button`,className:`btn`,onClick:()=>void o.refetch(),children:`Check now`})]}),(0,p.jsxs)(`div`,{className:`minitabs alerts-tabs`,role:`tablist`,"aria-label":`Checks`,children:[C(`all`,`All`,x()),a.map(e=>C(e.id,e.label,x(e.kinds)))]}),S]})}export{y as AlertsPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{F as e,Z as t,c as n,l as r,s as i}from"./index-
|
|
1
|
+
import{F as e,Z as t,c as n,l as r,s as i}from"./index-BNMV1syF.js";import{t as a}from"./reach-0Yvn3NAt.js";import{t as o}from"./Time-CR17Ozn9.js";import{i as s,n as c,o as l}from"./forms-C3dw2wG6.js";import{t as u}from"./icons-C-i_90Gs.js";import{t as d}from"./Managed-DjB6h9Kc.js";var f=t();function p(e,t){let n=new Date(Date.now()+6048e5).toISOString().slice(0,10);return!!(e&&e>=t&&e<=n)}function m({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,f.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,f.jsxs)(`span`,{className:`dim`,children:[n.used.toLocaleString(),` / `,n.limit.toLocaleString(),` `,n.what,` `,i]}),(0,f.jsx)(`div`,{className:`quota-bar${r>=1?` full`:r>=.8?` near`:``}`,children:(0,f.jsx)(`span`,{style:{width:`${Math.round(r*100)}%`}})})]})}function h(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 g(){let t=n(),g=i(),_=r(),v=c(),y=s(),b=Object.keys(g.data??{}).sort(),x=new Date().toISOString().slice(0,10),S;return S=g.isPending?(0,f.jsx)(e,{text:`Loading API keys…`}):g.isError?(0,f.jsxs)(`div`,{className:`empty`,children:[(0,f.jsx)(`strong`,{children:`Couldn’t load API keys`}),(0,f.jsx)(`span`,{children:g.error.message}),(0,f.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>g.refetch(),children:`Retry`})]}):b.length?(0,f.jsx)(`div`,{style:{overflowX:`auto`},children:(0,f.jsxs)(`table`,{className:`grid`,children:[(0,f.jsx)(`thead`,{children:(0,f.jsx)(`tr`,{children:[`Name`,`Scope`,`Access`,`Rate limit`,`IPs`,`Expires`,`Last used`,`Usage`,``].map((e,t)=>(0,f.jsx)(`th`,{children:e},t))})}),(0,f.jsx)(`tbody`,{children:b.map(e=>{let n=g.data[e],r=!!(n.expires_at&&n.expires_at<x),i=!r&&p(n.expires_at,x),s=n.allowed_ips??[],c=h(n.usage);return(0,f.jsxs)(`tr`,{"data-name":e,children:[(0,f.jsx)(`td`,{children:(0,f.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,f.jsx)(`span`,{className:`dot ${n.active&&!r?`ok`:`off`}`,title:n.active?r?`Expired`:`Active`:`Revoked`}),(0,f.jsx)(`span`,{className:`name`,title:n.created_at?`Created `+n.created_at+(n.created_from_role?` from role `+n.created_from_role:``):void 0,children:e}),(0,f.jsx)(d,{kind:`api_keys`,name:e}),n.previous_expires_at?(0,f.jsx)(`span`,{className:`tag`,title:`Rotated: the old secret works until then too`,children:`rotating`}):null,n.mask?.length?(0,f.jsx)(`span`,{className:`tag`,title:n.mask.map(e=>`${e.column}: ${e.action}`).join(`, `),children:`masked`}):null]})}),(0,f.jsx)(`td`,{children:(0,f.jsx)(l,{entry:n,collections:_.data?.collections})}),(0,f.jsx)(a,{entry:n}),(0,f.jsx)(`td`,{className:n.rate_limit?`mono`:`mono dim`,style:{whiteSpace:`nowrap`},children:n.rate_limit||`server default`}),(0,f.jsx)(`td`,{className:`dim`,children:s.length?(0,f.jsx)(`span`,{title:s.join(`, `),children:s.length+(s.length===1?` IP`:` IPs`)}):(0,f.jsx)(`span`,{className:`dim`,children:`any`})}),(0,f.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:n.expires_at?(0,f.jsx)(`span`,{style:r?{color:`var(--danger)`}:i?{color:`var(--warn)`}:void 0,children:n.expires_at+(r?` (expired)`:i?` (expires soon)`:``)}):(0,f.jsx)(`span`,{className:`dim`,children:`never`})}),(0,f.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,f.jsx)(o,{value:n.last_used_at,fallback:`never`})}),n.usage_this_period?(0,f.jsxs)(`td`,{style:{whiteSpace:`nowrap`},children:[c,(0,f.jsx)(m,{usage:n.usage_this_period})]}):c?(0,f.jsx)(`td`,{title:`${n.usage.rows}${n.usage.rows===1?` row`:` rows`} returned in total · since this process started`,style:{whiteSpace:`nowrap`},children:c}):(0,f.jsx)(`td`,{className:`dim`,style:{whiteSpace:`nowrap`},children:`No activity yet`}),(0,f.jsx)(`td`,{children:(0,f.jsxs)(`div`,{className:`actions`,children:[(0,f.jsx)(`button`,{hidden:!t(`access.write`),type:`button`,className:`btn ghost sm icon`,title:`Edit ${e}`,"aria-label":`Edit`,onClick:()=>v.editKey(e),children:(0,f.jsx)(u,{})}),(0,f.jsx)(`button`,{hidden:!t(`access.write`),type:`button`,className:`btn ghost sm danger`,onClick:()=>void y(e),children:`Revoke`})]})})]},e)})})]})}):(0,f.jsxs)(`div`,{className:`empty`,children:[(0,f.jsx)(`strong`,{children:`No scoped API keys yet`}),(0,f.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,f.jsx)(`button`,{hidden:!t(`access.write`),type:`button`,className:`btn primary`,onClick:()=>v.newKey(),children:`New API key`})]}),(0,f.jsxs)(f.Fragment,{children:[(0,f.jsxs)(`div`,{className:`page-head`,children:[(0,f.jsxs)(`div`,{className:`titles`,children:[(0,f.jsx)(`h1`,{children:`API keys`}),(0,f.jsx)(`span`,{className:`sub`,id:`apikeys-sub`,children:(b.length?b.length+(b.length===1?` key. `:` keys. `):``)+`Scope each one to connections, collections or roles.`})]}),(0,f.jsx)(`span`,{className:`spacer`}),(0,f.jsx)(`button`,{hidden:!t(`access.write`),id:`new-apikey`,type:`button`,className:`btn primary`,onClick:()=>v.newKey(),children:`New API key`})]}),(0,f.jsx)(`div`,{id:`apikeys-table`,className:`panel`,children:S})]})}export{g as ApiKeysPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./useMutation-
|
|
1
|
+
import{t as e}from"./useMutation-DCzNFnov.js";import{$ as t,B as n,C as r,D as i,F as a,H as o,L as s,N as c,Q as l,S as u,X as d,Z as f,d as p,z as m}from"./index-BNMV1syF.js";import{t as h}from"./Time-CR17Ozn9.js";import{t as g}from"./CodeBox-B8iq2Oop.js";var _=t(l(),1),v=f(),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:t}){let{closeDrawer:n,showError:l,toast:f}=s(),p=d(),[m,b]=(0,_.useState)(``),C=o({queryKey:[`write-requests`,`one`,t],queryFn:async()=>i(await r.GET(`/api/v1/write-requests/{id}`,{params:{path:{id:t}}})),retry:!1}),w=e({mutationFn:async e=>i(e?await r.POST(`/api/v1/write-requests/{id}/approve`,{params:{path:{id:t}}}):await r.POST(`/api/v1/write-requests/{id}/reject`,{params:{path:{id:t}},body:m.trim()?{reason:m.trim()}:{}})),onSuccess:e=>{l(``),p.invalidateQueries({queryKey:[`write-requests`]}),p.invalidateQueries({queryKey:[`alerts`]}),n(),f(e.status===`approved`?`Approved - ${e.caller} may run it now`:`Rejected`)},onError:e=>l(e.message,e instanceof u?{status:e.status}:{})});if(C.isPending)return(0,v.jsx)(a,{});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)(c,{label:`SQL`,children:(0,v.jsx)(g,{sql:T.sql})}),(0,v.jsx)(c,{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)(c,{label:`Result`,children:(0,v.jsx)(`span`,{className:`mono`,children:x(T.result)})}):null,E?(0,v.jsxs)(v.Fragment,{children:[(0,v.jsx)(c,{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:m,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:n,children:`Close`})]})]}):(0,v.jsx)(`div`,{className:`form-actions`,children:(0,v.jsx)(`button`,{type:`button`,className:`btn`,onClick:n,children:`Close`})})]})}function w(){let{id:e}=n(),t=m(),{openDrawer:r}=s(),[i,o]=(0,_.useState)(`pending`),c=b(i),l=c.data??[],u=e=>r({title:`Approve a write`,kicker:e,wide:!0,content:(0,v.jsx)(C,{id:e})});(0,_.useEffect)(()=>{e&&(u(e),t(`/approvals`,{replace:!0}))},[e]);let d;return d=c.isPending?(0,v.jsx)(a,{text:`Loading write requests…`}):c.isError?(0,v.jsxs)(`div`,{className:`empty`,children:[(0,v.jsx)(`strong`,{children:`Couldn’t load write requests`}),(0,v.jsx)(`span`,{children:c.error.message})]}):l.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`,i===`pending`?`Expires`:`Status`].map(e=>(0,v.jsx)(`th`,{children:e},e))})}),(0,v.jsx)(`tbody`,{children:l.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:()=>u(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:i===`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:i===`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 c.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:i,onChange:e=>o(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:c.data?`${l.length} request${l.length===1?``:`s`}`:``})]}),d]})]})}export{w as ApprovalsPage};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{$ as e,C as t,D as n,F as r,H as i,L as a,Q as o,Z as s}from"./index-
|
|
1
|
+
import{$ as e,C as t,D as n,F as r,H as i,L as a,Q as o,Z as s}from"./index-BNMV1syF.js";import{t as c}from"./Time-CR17Ozn9.js";var l=e(o(),1),u=s();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}=a(),o=i({queryKey:[`audit`],queryFn:async()=>n(await t.GET(`/api/v1/audit`)),retry:!1}),[s,f]=(0,l.useState)(``),[p,m]=(0,l.useState)(``),_=o.data?.items??[],v=p.trim().toLowerCase(),y=_.filter(e=>(!s||e.action===s)&&(!v||[e.timestamp,e.actor,e.target].join(` `).toLowerCase().includes(v))),b=o.data?.actions??[],x,S=``;return o.isPending?x=(0,u.jsx)(r,{text:`Loading audit log…`}):o.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:o.error.message}),(0,u.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>o.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.`+(o.data?` Keeps the last ${o.data.retention} entries.`:``)})]}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`button`,{id:`refresh-auditlog`,type:`button`,className:`btn`,onClick:()=>void o.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(s)?s:``,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{$ as e,C as t,D as n,H as r,L as i,O as a,Q as o,X as s,Z as c,g as l,o as u,w as d,z as f}from"./index-
|
|
1
|
+
import{$ as e,C as t,D as n,H as r,L as i,O as a,Q as o,X as s,Z as c,g as l,o as u,w as d,z as f}from"./index-BNMV1syF.js";import{i as p,n as m,t as h}from"./StatTile-BBd2i5mC.js";import{n as g,t as _}from"./Results-DofTMPkj.js";var v=e(o(),1),y=c();function b(){return r({queryKey:[`cache`,`entries`],queryFn:async()=>n(await t.GET(`/api/v1/cache/entries`)).items,retry:!1})}function x(){let e=f(),t=s(),n=l(),r=m(),i=u(),a=r.data??[],o=n.data?.find(e=>e.id===`cache`)?.rows[0]?.value??`…`,c=p(a,`queryapigate_cache_hits_total`),d=p(a,`queryapigate_cache_misses_total`),g=c+d,_=(i.data??[]).filter(e=>(e.cache_ttl??0)>0);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:`Caching`}),(0,y.jsxs)(`span`,{className:`sub`,children:[`The response cache backing saved queries' `,(0,y.jsx)(`code`,{children:`cache_ttl`}),` - see the Response cache row in Settings for the backend itself.`]})]}),(0,y.jsx)(`span`,{className:`spacer`}),(0,y.jsx)(`button`,{id:`refresh-caching`,type:`button`,className:`btn`,onClick:()=>{r.refetch(),t.invalidateQueries({queryKey:[`cache`,`entries`]})},children:`Refresh`})]}),(0,y.jsxs)(`div`,{id:`caching-body`,children:[(0,y.jsxs)(`div`,{className:`stat-tiles`,children:[(0,y.jsx)(h,{label:`Cache backend`,value:o}),(0,y.jsx)(h,{label:`Entries`,value:p(a,`queryapigate_cache_entries`)}),(0,y.jsx)(h,{label:`Hit rate`,value:g?(100*c/g).toFixed(1)+`%`:`—`}),(0,y.jsx)(h,{label:`Hits`,value:c}),(0,y.jsx)(h,{label:`Misses`,value:d})]}),(0,y.jsx)(`p`,{className:`sub-h`,style:{marginTop:14},children:`Saved queries with cache_ttl set`}),_.length?(0,y.jsx)(`div`,{className:`panel`,style:{overflowX:`auto`},children:(0,y.jsxs)(`table`,{className:`grid`,children:[(0,y.jsx)(`thead`,{children:(0,y.jsx)(`tr`,{children:[`Query`,`Collection`,`Connection`,`cache_ttl`].map((e,t)=>(0,y.jsx)(`th`,{className:t===3?`num`:``,children:e},e))})}),(0,y.jsx)(`tbody`,{children:_.map(t=>(0,y.jsxs)(`tr`,{children:[(0,y.jsx)(`td`,{children:(0,y.jsx)(`button`,{type:`button`,className:`target`,onClick:()=>e(`/queries/${encodeURIComponent(t.name)}`),children:t.name})}),(0,y.jsx)(`td`,{children:t.collection||(0,y.jsx)(`span`,{className:`dim`,children:`—`})}),(0,y.jsx)(`td`,{className:`mono`,children:t.connection_name||`—`}),(0,y.jsx)(`td`,{className:`mono num`,children:t.cache_ttl+`s`})]},t.name))})]})}):(0,y.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,y.jsx)(`div`,{id:`cache-entries-panel`,children:(0,y.jsx)(C,{})})]})}function S(e){return e<1024?e+` B`:(e/1024).toFixed(1)+` KB`}function C(){let e=s(),{openDrawer:n,showError:r,toast:o}=i(),c=b().data??[],l=()=>void e.invalidateQueries({queryKey:[`cache`,`entries`]}),u=e=>r(e.message),d=async()=>{if(window.confirm(`Evict all ${c.length} cache entries? The next call to each query will re-run for real.`))try{a(await t.DELETE(`/api/v1/cache/entries`)),o(`Cache cleared`),l()}catch(e){u(e)}},f=async e=>{try{a(await t.DELETE(`/api/v1/cache/entries/{key}`,{params:{path:{key:e}}})),o(`Entry deleted`),l()}catch(e){u(e)}};return(0,y.jsxs)(y.Fragment,{children:[(0,y.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:12},children:[(0,y.jsxs)(`div`,{className:`set-head`,style:{marginTop:14},children:[(0,y.jsx)(`h2`,{children:`Cache entries`}),(0,y.jsx)(`span`,{children:`${c.length}${c.length===1?` live entry`:` live entries`} right now.`})]}),(0,y.jsx)(`span`,{className:`spacer`}),(0,y.jsx)(`button`,{type:`button`,className:`btn sm`,disabled:!c.length,onClick:()=>void d(),children:`Clear cache`})]}),c.length?(0,y.jsx)(`div`,{className:`panel`,style:{overflowX:`auto`,marginTop:8},children:(0,y.jsxs)(`table`,{className:`grid`,children:[(0,y.jsx)(`thead`,{children:(0,y.jsx)(`tr`,{children:[`Query`,`Version`,`Connection`,`Format`,`Content type`,`Size`,`TTL left`,``].map((e,t)=>(0,y.jsx)(`th`,{className:t>=4&&t<=6?`num`:``,children:e},t))})}),(0,y.jsx)(`tbody`,{children:c.map(e=>{let t=e.meta??{};return(0,y.jsxs)(`tr`,{children:[(0,y.jsx)(`td`,{children:(0,y.jsx)(`button`,{type:`button`,className:`target`,onClick:()=>n({title:t.name||`Cache entry`,kicker:e.key,content:(0,y.jsx)(T,{entry:e})}),children:t.name||e.key.slice(0,12)+`…`})}),(0,y.jsx)(`td`,{className:`mono`,children:t.version===void 0?`—`:`v`+t.version}),(0,y.jsx)(`td`,{className:`mono`,children:t.connection||`—`}),(0,y.jsx)(`td`,{className:`mono`,children:t.format||`—`}),(0,y.jsx)(`td`,{className:`mono num`,children:e.content_type}),(0,y.jsx)(`td`,{className:`mono num`,children:S(e.size_bytes)}),(0,y.jsx)(`td`,{className:`mono num`,children:Math.max(0,Math.round(e.ttl_remaining_s))+`s`}),(0,y.jsx)(`td`,{children:(0,y.jsx)(`button`,{type:`button`,className:`btn sm ghost`,onClick:()=>void f(e.key),children:`Delete`})})]},e.key)})})]})}):(0,y.jsx)(`div`,{className:`hint`,children:`Nothing cached right now - call a query with a cache_ttl set, then check back.`})]})}var w=e=>({ok:!1,status:0,statusText:``,contentType:``,headers:[],page:1,size:0,hasMore:!1,elapsed:0,format:``,error:e});function T({entry:e}){let{showError:t}=i(),n=e.meta?.name||`cache-entry`,r=e.meta?.format||`json`,[a,o]=(0,v.useState)(null);return(0,v.useEffect)(()=>{let i=!0,a=performance.now();return d(`/api/v1/cache/entries/${encodeURIComponent(e.key)}`).then(e=>g(e,{page:1,size:0,format:r,elapsed:Math.round(performance.now()-a),filename:n})).then(e=>{i&&(e.error&&t(e.error,{status:e.status}),o(e))},e=>i&&o(w(`Network error: `+e.message))),()=>{i=!1}},[e.key,r,n,t]),(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(`div`,{className:`hint`,children:`The exact cached body, served with its real Content-Type - what a caller receives on a hit.`}),(0,y.jsx)(_,{result:a,running:!a,filename:n,onPage:()=>{},onPageSize:()=>{},layout:`drawer`})]})}export{x as CachingPage};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{Z as e}from"./index-
|
|
1
|
+
import{Z as e}from"./index-BNMV1syF.js";var t=new Set(`select from where and or not in is null as join left right inner outer full cross on using group by order having limit offset union all distinct insert into values update set delete create table view index drop alter with recursive case when then else end asc desc like ilike between exists returning primary key default top fetch next rows only over partition window count sum avg min max coalesce cast true false interval date timestamp extract filter lateral intersect except nulls first last`.split(` `)),n=/(--[^\n]*|\/\*[\s\S]*?(?:\*\/|$))|('(?:[^']|'')*'?)|("(?:[^"]|"")*"?|`[^`]*`?)|(::)|(:[A-Za-z_]\w*)|(\b\d+(?:\.\d+)?\b)|([A-Za-z_]\w*)/g;function r(e){let r=[],i=0;n.lastIndex=0;for(let a=n.exec(e);a;a=n.exec(e)){a.index>i&&r.push({cls:null,text:e.slice(i,a.index)});let o=null;a[1]?o=`c`:a[2]?o=`s`:a[5]?o=`p`:a[6]?o=`n`:a[7]&&t.has(a[7].toLowerCase())&&(o=`k`),r.push({cls:o,text:a[0]}),i=n.lastIndex}return i<e.length&&r.push({cls:null,text:e.slice(i)}),r}function i(e){let t=new Set;n.lastIndex=0;for(let r=n.exec(e);r;r=n.exec(e))r[5]&&t.add(r[5].slice(1));return[...t]}function a(e){return`'`+e.replace(/'/g,`'\\''`)+`'`}var o=new Set(`select from where having limit offset union with insert update set values join on group order delete left right inner outer full cross`.split(` `)),s=new Set([`left`,`right`,`inner`,`outer`,`full`,`cross`]);function c(e){let t=[],r=0,i=null;n.lastIndex=0;for(let a=n.exec(e);a;a=n.exec(e)){let c=e.slice(r,a.index).replace(/[ \t]{2,}/g,` `),l=a[7]?a[7].toLowerCase():null,u=l===`join`&&i!==null&&s.has(i)||l===`from`&&i===`delete`;l&&o.has(l)&&!u?t.push(c,`
|
|
2
2
|
`,a[0]):t.push(c,a[0]),i=l,r=n.lastIndex}return t.push(e.slice(r)),t.join(``).split(`
|
|
3
3
|
`).map(e=>e.trim()).filter(e=>e!==``).join(`
|
|
4
4
|
`)}var l=[`mysql`,`postgres`,`clickhouse`,`sqlite`,`duckdb`],u=e();function d({sql:e}){return(0,u.jsx)(`div`,{className:`codebox`,children:(0,u.jsx)(`div`,{className:`ln-rows`,children:e.split(`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{t as e}from"./useMutation-
|
|
1
|
+
import{t as e}from"./useMutation-DCzNFnov.js";import{$ as t,C as n,D as r,F as i,H as a,L as o,M as s,N as c,O as l,P as u,Q as d,S as f,X as p,Z as m,c as h,k as g,z as _}from"./index-BNMV1syF.js";import{t as v}from"./Time-CR17Ozn9.js";import{n as y,t as b}from"./icons-C-i_90Gs.js";import{t as x}from"./Managed-DjB6h9Kc.js";var S=t(d(),1),C=m(),w=[`mysql`,`postgres`,`clickhouse`,`sqlite`,`h2`,`duckdb`,`mongo`],T=`files`,E=[`h2`,`jdbc`,`mongo`],D=[`mysql`,`postgres`,`clickhouse`,`mongo`];function O(e){if(!e||!e.queries)return null;let t=[e.queries+(e.queries===1?` query`:` queries`)];return e.errors&&t.push(e.errors+` failed`),e.avg_duration_ms!==null&&e.avg_duration_ms!==void 0&&t.push(e.avg_duration_ms+`ms avg`),t.join(` · `)}function k(e){return e.host?e.host+(e.port?`:`+e.port:``):``}function A(){return a({queryKey:[`connections`],queryFn:async()=>r(await n.GET(`/api/v1/connections`)).items,retry:!1})}function ee(e){return a({queryKey:[`connections`,`deleted`],queryFn:async()=>r(await n.GET(`/api/v1/connections/deleted`)).items,enabled:e,retry:!1})}function j(){let e=h(),t=_(),{openDrawer:n}=o(),r=A(),[a,c]=(0,S.useState)(`all`),[l,u]=(0,S.useState)(``),d=ee(!0),f=r.data??[],p=f.filter(e=>e.active).length,m=d.data??[],g=l.trim().toLowerCase(),w=()=>n({title:`New connection`,kicker:`POST /api/v1/connections`,content:(0,C.jsx)(N,{})}),T,E=null;if(a===`deleted`){let e=m.filter(e=>!g||[e.name,e.deleted_by,e.reason,e.db,e.host].join(` `).toLowerCase().includes(g));m.length?(E=(0,C.jsx)(`span`,{children:`Showing ${e.length} of ${m.length}${m.length===1?` deleted connection`:` deleted connections`}`}),T=e.length?(0,C.jsx)(`div`,{style:{overflowX:`auto`},children:(0,C.jsxs)(`table`,{className:`grid`,children:[(0,C.jsx)(`thead`,{children:(0,C.jsx)(`tr`,{children:[`Name`,`Type`,`Host`,`Database`,`Deleted at`,`Deleted by`,`Reason`].map(e=>(0,C.jsx)(`th`,{children:e},e))})}),(0,C.jsx)(`tbody`,{children:e.map((e,t)=>(0,C.jsxs)(`tr`,{children:[(0,C.jsx)(`td`,{children:(0,C.jsx)(`span`,{className:`name`,children:e.name})}),(0,C.jsx)(`td`,{children:(0,C.jsx)(`span`,{className:`tag`,children:e.db||`?`})}),(0,C.jsx)(`td`,{className:`mono`,style:{whiteSpace:`nowrap`},children:k(e)||(0,C.jsx)(`span`,{className:`dim`,children:`—`})}),(0,C.jsx)(`td`,{className:`mono`,children:e.database||``}),(0,C.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,C.jsx)(v,{value:e.deleted_at})}),(0,C.jsx)(`td`,{className:`mono`,children:e.deleted_by||`—`}),(0,C.jsx)(`td`,{children:e.reason||`—`})]},`${e.name}-${t}`))})]})}):(0,C.jsx)(s,{children:`No deleted connections match “${g}”.`})):T=(0,C.jsx)(s,{title:`No deleted connections`,children:`Every connection deletion is recorded here, with who did it, when, and why.`})}else if(r.isPending)T=(0,C.jsx)(i,{text:`Loading connections…`});else if(r.isError)T=(0,C.jsx)(s,{title:`Couldn’t load connections`,children:(0,C.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>r.refetch(),children:`Retry`})});else if(!f.length)T=(0,C.jsxs)(`div`,{className:`empty`,children:[(0,C.jsx)(`strong`,{children:`No connections yet`}),(0,C.jsx)(`span`,{children:`Add a MySQL, PostgreSQL, ClickHouse, SQLite or H2 database to start running SQL.`}),(0,C.jsx)(`button`,{hidden:!e(`connections.write`),type:`button`,className:`btn primary`,onClick:w,children:`New connection`})]});else{let r=f.filter(e=>a===`active`&&!e.active||a===`inactive`&&e.active?!1:!g||[e.name,e.db,e.host,e.database,e.user].join(` `).toLowerCase().includes(g));E=(0,C.jsx)(`span`,{children:`Showing ${r.length} of ${f.length}${f.length===1?` connection`:` connections`}`}),T=r.length?(0,C.jsx)(`div`,{style:{overflowX:`auto`},children:(0,C.jsxs)(`table`,{className:`grid`,children:[(0,C.jsx)(`thead`,{children:(0,C.jsx)(`tr`,{children:[`Name`,`Type`,`Host`,`Database`,`User`,`Status`,`Usage`,`Created`,`Last modified`,``].map((e,t)=>(0,C.jsx)(`th`,{children:e},t))})}),(0,C.jsx)(`tbody`,{children:r.map(r=>{let i=O(r.usage);return(0,C.jsxs)(`tr`,{"data-name":r.name,children:[(0,C.jsx)(`td`,{children:(0,C.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,C.jsx)(`span`,{className:`name`,children:r.name}),(0,C.jsx)(x,{kind:`connections`,name:r.name}),r.example&&(0,C.jsx)(`span`,{className:`tag example`,title:`Installed by the example APIs — removing the examples removes it`,children:`example`})]})}),(0,C.jsx)(`td`,{children:(0,C.jsx)(`span`,{className:`tag`,children:r.db||`?`})}),(0,C.jsx)(`td`,{className:`mono`,style:{whiteSpace:`nowrap`},children:k(r)||(0,C.jsx)(`span`,{className:`dim`,children:`—`})}),(0,C.jsx)(`td`,{className:`mono`,children:r.database||``}),(0,C.jsx)(`td`,{className:`mono dim`,children:r.user||`—`}),(0,C.jsx)(`td`,{children:(0,C.jsxs)(`span`,{className:`pill ${r.active?`ok`:`off`}`,children:[(0,C.jsx)(`i`,{}),r.active?`Active`:`Inactive`]})}),i?(0,C.jsx)(`td`,{title:`${r.usage.rows}${r.usage.rows===1?` row`:` rows`} returned in total · since this process started`,style:{whiteSpace:`nowrap`},children:i}):(0,C.jsx)(`td`,{className:`dim`,style:{whiteSpace:`nowrap`},children:`No activity yet`}),(0,C.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,C.jsx)(v,{value:r.created_at})}),(0,C.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,C.jsx)(v,{value:r.updated_at})}),(0,C.jsx)(`td`,{children:(0,C.jsxs)(`div`,{className:`actions`,children:[(0,C.jsx)(`button`,{hidden:!e(`data`),type:`button`,className:`btn sm outlined`,disabled:!r.active,title:`Open in API Designer`,onClick:()=>t(`/designer`,{state:{select:r.name}}),children:`Query`}),(0,C.jsx)(`button`,{hidden:!e(`connections.write`),type:`button`,className:`btn ghost sm icon`,title:`Edit ${r.name}`,"aria-label":`Edit`,onClick:()=>n({title:`Edit connection`,kicker:r.name,content:(0,C.jsx)(N,{name:r.name})}),children:(0,C.jsx)(b,{})}),(0,C.jsx)(`button`,{hidden:!e(`connections.write`),type:`button`,className:`btn ghost sm icon danger`,title:`Delete ${r.name}`,"aria-label":`Delete`,onClick:()=>n({title:`Delete connection`,kicker:r.name,content:(0,C.jsx)(te,{name:r.name})}),children:(0,C.jsx)(y,{})})]})})]},r.name)})})]})}):(0,C.jsx)(s,{children:g?`No connections match “${g}”.`:`No ${a} connections.`})}let D=[[`all`,`All`,f.length],[`active`,`Active`,p],[`inactive`,`Inactive`,f.length-p],[`deleted`,`Deleted`,m.length]];return(0,C.jsxs)(C.Fragment,{children:[(0,C.jsxs)(`div`,{className:`page-head`,children:[(0,C.jsxs)(`div`,{className:`titles`,children:[(0,C.jsx)(`h1`,{children:`Connections`}),(0,C.jsx)(`span`,{className:`sub`,children:`Databases this gateway can run saved queries against.`})]}),(0,C.jsx)(`span`,{className:`spacer`}),(0,C.jsx)(`button`,{id:`new-connection`,type:`button`,className:`btn primary`,hidden:a===`deleted`||!e(`connections.write`),onClick:w,children:`New connection`})]}),(0,C.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,C.jsxs)(`div`,{className:`panel-bar`,children:[(0,C.jsx)(`div`,{className:`seg`,id:`conn-tabs`,role:`group`,"aria-label":`Connection status`,children:D.map(([e,t,n])=>(0,C.jsxs)(`button`,{type:`button`,className:a===e?`on`:``,onClick:()=>c(e),children:[t,(0,C.jsx)(`span`,{className:`n`,children:n})]},e))}),(0,C.jsx)(`span`,{className:`spacer`}),(0,C.jsx)(`input`,{id:`conn-filter`,className:`search`,type:`search`,placeholder:a===`deleted`?`Filter by name, actor, reason…`:`Filter by name, type, host…`,value:l,onChange:e=>u(e.target.value)})]}),(0,C.jsx)(`div`,{id:`connections-table`,children:T}),(0,C.jsx)(`div`,{className:`panel-foot`,id:`connections-foot`,children:E})]})]})}function M(e,t){t instanceof f?t.code===`precondition_failed`?e(`This connection changed since you opened the form - close it, check it and try again.`,{status:412}):e(t.message,{status:t.status}):e(t.message)}function N({name:e}){let t=!!e,r=p(),{closeDrawer:s,showError:c,toast:l}=o(),u=a({queryKey:[`connections`,`detail`,e],queryFn:async()=>g(await n.GET(`/api/v1/connections/{name}`,{params:{path:{name:e}}})),enabled:t,retry:!1});return t&&u.isPending?(0,C.jsx)(i,{}):t&&u.isError?(0,C.jsx)(`div`,{className:`hint`,children:u.error.message}):(0,C.jsx)(P,{name:e,existing:u.data?.data,etag:u.data?.etag??null,onSaved:e=>{c(``),s(),l((t?`Saved `:`Created `)+e),r.invalidateQueries({queryKey:[`connections`]})}})}function P({name:t,existing:i,etag:a,onSaved:s}){let l=!!t,{closeDrawer:d,showError:p}=o(),[m,h]=(0,S.useState)(t??``),[g,_]=(0,S.useState)(i?.db===`duckdb`&&i.database===`:memory:`?T:i?.db??`mysql`),v=g===T,y=v?`duckdb`:g,[b,x]=(0,S.useState)(i?.host??``),[O,k]=(0,S.useState)(i?.port==null?``:String(i.port)),[A,ee]=(0,S.useState)(i?.user??``),[j,N]=(0,S.useState)(l?i?.password??``:``),[P,te]=(0,S.useState)(!1),[F,I]=(0,S.useState)(i?.database??``),[L,ne]=(0,S.useState)(!i||i.active),[R,re]=(0,S.useState)(null),[z,B]=(0,S.useState)(null),[V,H]=(0,S.useState)(null),[ie,ae]=(0,S.useState)(!1),U=i?.options??{},[W,oe]=(0,S.useState)((U.allowed_paths??[]).join(`
|
|
2
2
|
`)),[G,se]=(0,S.useState)(U.storage??`s3`),[K,ce]=(0,S.useState)(U.region??``),[q,le]=(0,S.useState)(U.endpoint??``),[J,ue]=(0,S.useState)(U.url_style??``),[Y,de]=(0,S.useState)(U.use_ssl!==!1),[X,fe]=(0,S.useState)(U.views?JSON.stringify(U.views,null,2):``),[pe,me]=(0,S.useState)(i?U.auto_views===!0:null),he=pe??v,Z=y===`duckdb`,Q=e=>{if(!Z)return{};let t=W.split(`
|
|
3
3
|
`).map(e=>e.trim()).filter(Boolean),n=e;if(X.trim())try{n=JSON.parse(X)}catch{throw Error(`Views must be a JSON object of {"name": "SELECT ..."}.`)}return{allowed_paths:t.length?t:e,storage:G===`s3`?e:G,region:K.trim()||e,endpoint:q.trim()||e,url_style:J||e,use_ssl:Y?e:!1,auto_views:he?!0:e,views:n}},ge=()=>({db:y,host:b||void 0,port:O?Number(O):void 0,user:A||void 0,password:j,database:v?`:memory:`:F||void 0,...l?{name:t}:{},...Q(void 0)}),$=e({mutationFn:async e=>{let t=e===`test`?`/api/v1/connections/test`:`/api/v1/connections/databases`,i=await n.POST(t,{body:ge()});return{kind:e,data:r(i)}}}),_e=e=>`✗ `+(e instanceof f?e.message+(e.detail?`: `+e.detail:``):`Network error`),ve=()=>{B(null),$.mutate(`databases`,{onSuccess:({data:e})=>{let t=e.databases;re(t),B({ok:!0,text:`✓ ${t.length}${t.length===1?` database`:` databases`}`})},onError:e=>B({ok:!1,text:_e(e)})})},ye=()=>{H(null),$.mutate(`test`,{onSuccess:({data:e})=>{let t=e.elapsed_ms;H({ok:!0,text:`✓ Connected`+(t===void 0?``:` (${t} ms)`)})},onError:e=>H({ok:!1,text:_e(e)})})},be=D.includes(y);l&&be&&!ie&&(ae(!0),setTimeout(ve,0));let xe=e({mutationFn:async()=>{if(l){let e={db:y,active:L,host:b||null,port:O?Number(O):null,user:A||null,password:j,database:v?`:memory:`:F||null,...Q(null)};return r(await n.PATCH(`/api/v1/connections/{name}`,{params:{path:{name:t},header:a?{"If-Match":a}:void 0},body:e})),t}let e={...ge(),name:m.trim(),active:L};return r(await n.POST(`/api/v1/connections`,{body:e})).name},onSuccess:s,onError:e=>e instanceof f?M(p,e):p(e.message)});return(0,C.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{if(e.preventDefault(),!l&&!m.trim()){p(`A connection name is required.`,{errors:{name:`This field is required`}});return}xe.mutate()},children:[(0,C.jsx)(c,{id:`c-name`,label:`Name`,hint:l?`Renaming isn’t supported — create a new connection instead.`:`Referenced as connection_name by queries and the API.`,children:(0,C.jsx)(`input`,{id:`c-name`,placeholder:`reporting-db`,autoComplete:`off`,spellCheck:!1,disabled:l,autoFocus:!l,value:m,onChange:e=>h(e.target.value)})}),(0,C.jsx)(c,{id:`c-db`,label:`Database type`,children:(0,C.jsxs)(`select`,{id:`c-db`,value:g,onChange:e=>_(e.target.value),children:[w.map(e=>(0,C.jsx)(`option`,{value:e,children:E.includes(e)?`${e} (experimental)`:e},e)),(0,C.jsx)(`option`,{value:T,children:`Files (Parquet, CSV, JSON)`})]})}),(0,C.jsxs)(`div`,{className:`grid-host`,hidden:Z,children:[(0,C.jsx)(c,{id:`c-host`,label:`Host`,children:(0,C.jsx)(`input`,{id:`c-host`,placeholder:`localhost`,autoComplete:`off`,spellCheck:!1,autoFocus:l,value:b,onChange:e=>x(e.target.value)})}),(0,C.jsx)(c,{id:`c-port`,label:`Port`,children:(0,C.jsx)(`input`,{id:`c-port`,type:`number`,autoComplete:`off`,value:O,onChange:e=>k(e.target.value)})})]}),(0,C.jsxs)(`div`,{className:`grid2`,children:[(0,C.jsx)(c,{id:`c-user`,label:Z?`Access key ID`:`User`,hint:Z?`For object storage; leave empty for public files.`:void 0,children:(0,C.jsx)(`input`,{id:`c-user`,autoComplete:`off`,spellCheck:!1,value:A,onChange:e=>ee(e.target.value)})}),(0,C.jsx)(c,{id:`c-password`,label:Z?`Secret access key`:`Password`,hint:l?`Leave the mask to keep the stored password.`:"${ENV_VAR} references are resolved on the server.",children:(0,C.jsxs)(`div`,{className:`password-field`,children:[(0,C.jsx)(`input`,{id:`c-password`,type:P?`text`:`password`,autoComplete:`off`,spellCheck:!1,value:j,onChange:e=>N(e.target.value)}),(0,C.jsx)(`button`,{type:`button`,className:`btn ghost sm`,"aria-label":(P?`Hide`:`Show`)+` password`,onClick:()=>te(e=>!e),children:P?`Hide`:`Show`})]})})]}),(0,C.jsx)(`div`,{hidden:v,children:(0,C.jsx)(c,{id:`c-database`,label:`Default database`,hint:Z?`A .duckdb file that already exists, or :memory: for a source made only of files.`:`SQLite, DuckDB and H2 take a file path here instead.`,children:(0,C.jsx)(`div`,{children:R?(0,C.jsx)(`select`,{id:`c-database`,value:R.includes(F)?F:R[0]??``,onChange:e=>I(e.target.value),children:R.map(e=>(0,C.jsx)(`option`,{value:e,children:e},e))}):(0,C.jsx)(`input`,{id:`c-database`,placeholder:`SQLite, DuckDB and H2 take a file path`,autoComplete:`off`,spellCheck:!1,value:F,onChange:e=>I(e.target.value)})})})}),(0,C.jsxs)(`div`,{className:`test-row`,children:[(0,C.jsx)(`button`,{type:`button`,className:`btn sm ghost`,hidden:!be,disabled:$.isPending,onClick:ve,children:$.isPending&&$.variables===`databases`?`Loading…`:`Load databases…`}),(0,C.jsx)(`span`,{className:`test-result`,children:z&&(0,C.jsx)(`span`,{className:z.ok?`test-ok`:`test-fail`,children:z.text})})]}),Z&&(0,C.jsxs)(`fieldset`,{className:`duck-files`,children:[(0,C.jsx)(`legend`,{children:`Files it may read`}),(0,C.jsx)(c,{id:`c-paths`,label:`Allowed paths`,hint:`One per line. Folders and buckets end in / (s3://sales/2026/, /data/files/); a web address must name one file. Nothing else is readable - an empty list means no files at all.`,children:(0,C.jsx)(`textarea`,{id:`c-paths`,rows:3,spellCheck:!1,placeholder:`s3://sales/2026/
|
|
4
4
|
https://data.example.com/prices.parquet`,value:W,onChange:e=>oe(e.target.value)})}),(0,C.jsxs)(`div`,{className:`grid2`,children:[(0,C.jsx)(c,{id:`c-storage`,label:`Object storage`,children:(0,C.jsxs)(`select`,{id:`c-storage`,value:G,onChange:e=>se(e.target.value),children:[(0,C.jsx)(`option`,{value:`s3`,children:`S3 or S3-compatible`}),(0,C.jsx)(`option`,{value:`gcs`,children:`Google Cloud Storage`}),(0,C.jsx)(`option`,{value:`r2`,children:`Cloudflare R2`})]})}),(0,C.jsx)(c,{id:`c-region`,label:`Region`,children:(0,C.jsx)(`input`,{id:`c-region`,placeholder:`us-east-1`,spellCheck:!1,value:K,onChange:e=>ce(e.target.value)})})]}),(0,C.jsxs)(`div`,{className:`grid2`,children:[(0,C.jsx)(c,{id:`c-endpoint`,label:`Endpoint`,hint:`Only for an S3-compatible service, e.g. minio.internal:9000.`,children:(0,C.jsx)(`input`,{id:`c-endpoint`,spellCheck:!1,value:q,onChange:e=>le(e.target.value)})}),(0,C.jsx)(c,{id:`c-url-style`,label:`URL style`,children:(0,C.jsxs)(`select`,{id:`c-url-style`,value:J,onChange:e=>ue(e.target.value),children:[(0,C.jsx)(`option`,{value:``,children:`Default`}),(0,C.jsx)(`option`,{value:`vhost`,children:`Virtual-hosted (bucket.host)`}),(0,C.jsx)(`option`,{value:`path`,children:`Path (host/bucket)`})]})})]}),(0,C.jsxs)(`label`,{className:`switch`,children:[(0,C.jsx)(`input`,{id:`c-auto-views`,type:`checkbox`,checked:he,onChange:e=>me(e.target.checked)}),`A view for each file and folder`,(0,C.jsx)(`span`,{className:`hint`,children:`— orders.parquet becomes orders; a folder of files, one view over all of them`})]}),(0,C.jsxs)(`label`,{className:`switch`,children:[(0,C.jsx)(`input`,{id:`c-ssl`,type:`checkbox`,checked:Y,onChange:e=>de(e.target.checked)}),`Use HTTPS for object storage`]}),(0,C.jsx)(c,{id:`c-views`,label:`Views`,hint:`Optional JSON: {"orders": "SELECT * FROM read_parquet('s3://sales/2026/*.parquet')"}. They appear in the schema browser and can be granted with allowed_tables.`,children:(0,C.jsx)(`textarea`,{id:`c-views`,rows:3,spellCheck:!1,value:X,onChange:e=>fe(e.target.value)})})]}),(0,C.jsxs)(`label`,{className:`switch`,children:[(0,C.jsx)(`input`,{id:`c-active`,type:`checkbox`,checked:L,onChange:e=>ne(e.target.checked)}),`Active`,(0,C.jsx)(`span`,{className:`hint`,children:`— inactive connections refuse queries`})]}),(0,C.jsxs)(`div`,{className:`test-row`,children:[(0,C.jsx)(`button`,{type:`button`,className:`btn`,disabled:$.isPending,onClick:ye,children:$.isPending&&$.variables===`test`?`Testing…`:`Test connection`}),(0,C.jsx)(`span`,{className:`test-result`,children:V&&(0,C.jsx)(`span`,{className:V.ok?`test-ok`:`test-fail`,children:V.text})})]}),l&&i?.created_at&&(0,C.jsx)(`div`,{className:`hint`,children:`Created `+i.created_at+(i.updated_at&&i.updated_at!==i.created_at?` · last edited `+i.updated_at:``)}),(0,C.jsx)(u,{onCancel:d,children:(0,C.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:xe.isPending,children:l?`Save`:`Create`})})]})}function te({name:t}){let r=p(),{closeDrawer:i,showError:a,toast:s}=o(),[d,f]=(0,S.useState)(``),[m,h]=(0,S.useState)(``),g=e({mutationFn:async()=>l(await n.DELETE(`/api/v1/connections/{name}`,{params:{path:{name:t}},body:{reason:d.trim()}})),onSuccess:()=>{i(),s(`Deleted `+t),r.invalidateQueries({queryKey:[`connections`]})},onError:e=>M(a,e)}),_=!!d.trim()&&m===t;return(0,C.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{e.preventDefault(),_&&g.mutate()},children:[(0,C.jsxs)(`p`,{className:`d-desc`,children:[`This deletes `,(0,C.jsx)(`span`,{className:`name`,children:t}),` permanently. Every saved query that uses it will stop working. It moves to the Deleted tab, with the reason and who deleted it.`]}),(0,C.jsx)(c,{id:`del-reason`,label:`Reason`,hint:`Recorded in the audit log - required.`,children:(0,C.jsx)(`textarea`,{id:`del-reason`,placeholder:`Why is this connection being deleted?`,autoFocus:!0,value:d,onChange:e=>f(e.target.value)})}),(0,C.jsx)(c,{id:`del-confirm`,label:`Type "${t}" to confirm`,children:(0,C.jsx)(`input`,{id:`del-confirm`,type:`text`,autoComplete:`off`,spellCheck:!1,placeholder:t,value:m,onChange:e=>h(e.target.value)})}),(0,C.jsx)(u,{onCancel:i,children:(0,C.jsx)(`button`,{type:`submit`,className:`btn danger`,disabled:!_||g.isPending,children:`Delete`})})]})}export{N as ConnectionForm,j as ConnectionsPage};
|