queryapigate 0.30.0__tar.gz → 0.31.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.31.0}/PKG-INFO +1 -1
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/__init__.py +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/AccessMapPage-CU9b_-fo.js → queryapigate-0.31.0/queryapigate/console_dist/assets/AccessMapPage-CFAKvNHS.js +3 -3
- queryapigate-0.30.0/queryapigate/console_dist/assets/AdministratorsPage-Bwjtg36U.js → queryapigate-0.31.0/queryapigate/console_dist/assets/AdministratorsPage-O9o_8T8Q.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/AgentsPage-BmXNCpQ0.js → queryapigate-0.31.0/queryapigate/console_dist/assets/AgentsPage-CJL7R9ML.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/AlertsPage-C7tvw3Hr.js → queryapigate-0.31.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.31.0/queryapigate/console_dist/assets/ApiKeysPage-LSfT7OFf.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/ApprovalsPage-Bqsr5dHt.js → queryapigate-0.31.0/queryapigate/console_dist/assets/ApprovalsPage-CxyRU_om.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/AuditLogPage-hAKiebHz.js → queryapigate-0.31.0/queryapigate/console_dist/assets/AuditLogPage-CfozUklt.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/CachingPage-WFUpWs_W.js → queryapigate-0.31.0/queryapigate/console_dist/assets/CachingPage-8TY7yUvl.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/CodeBox-DuaKuoNl.js → queryapigate-0.31.0/queryapigate/console_dist/assets/CodeBox-B8iq2Oop.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/ConnectionsPage-DwdiiWsf.js → queryapigate-0.31.0/queryapigate/console_dist/assets/ConnectionsPage-CrgOBe8i.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/DesignerPage-BfhC69pJ.js → queryapigate-0.31.0/queryapigate/console_dist/assets/DesignerPage-C7m2WgjH.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/DestinationsPage-yaGxeOXB.js → queryapigate-0.31.0/queryapigate/console_dist/assets/DestinationsPage-hjd4ejdt.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/ExportsPage-DlkNfYMZ.js → queryapigate-0.31.0/queryapigate/console_dist/assets/ExportsPage-D70vuQNl.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/HelpPage-Bey5KI_-.js → queryapigate-0.31.0/queryapigate/console_dist/assets/HelpPage-C7vakDSS.js +2 -2
- queryapigate-0.30.0/queryapigate/console_dist/assets/HomePage-DeF8Zaq6.js → queryapigate-0.31.0/queryapigate/console_dist/assets/HomePage-BuExjOpj.js +2 -2
- queryapigate-0.30.0/queryapigate/console_dist/assets/Managed-CQVNTWrU.js → queryapigate-0.31.0/queryapigate/console_dist/assets/Managed-DjB6h9Kc.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/MetricsPage-DqeoN2tZ.js → queryapigate-0.31.0/queryapigate/console_dist/assets/MetricsPage-BAQxP5-4.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/RepositoryPage-QqouaqLm.js → queryapigate-0.31.0/queryapigate/console_dist/assets/RepositoryPage-8CBmviv-.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/Results-BhwS6Q_T.js → queryapigate-0.31.0/queryapigate/console_dist/assets/Results-DofTMPkj.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/RolesPage-DhkAkZ1v.js → queryapigate-0.31.0/queryapigate/console_dist/assets/RolesPage-CH_Dr1DP.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/SchemaBrowser-DQ0mi4Vm.js → queryapigate-0.31.0/queryapigate/console_dist/assets/SchemaBrowser-D57iE2Kg.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/SettingsPage-4T0vN6dU.js → queryapigate-0.31.0/queryapigate/console_dist/assets/SettingsPage-CHmKfYHu.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/StatTile-DHKvOsXg.js → queryapigate-0.31.0/queryapigate/console_dist/assets/StatTile-BBd2i5mC.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/Time-KeJDv2te.js → queryapigate-0.31.0/queryapigate/console_dist/assets/Time-CR17Ozn9.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/forms-StJt-xsd.js → queryapigate-0.31.0/queryapigate/console_dist/assets/forms-C3dw2wG6.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/forms-bSOovYin.js → queryapigate-0.31.0/queryapigate/console_dist/assets/forms-DR5CXITG.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/icons-CeIi0TUG.js → queryapigate-0.31.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.31.0/queryapigate/console_dist/assets/index-BNMV1syF.js +2 -2
- queryapigate-0.30.0/queryapigate/console_dist/assets/maskRules-9C_A2JIu.js → queryapigate-0.31.0/queryapigate/console_dist/assets/maskRules-DyD2pxp7.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/reach-DWK2PD5I.js → queryapigate-0.31.0/queryapigate/console_dist/assets/reach-0Yvn3NAt.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/shared-rt98BmTW.js → queryapigate-0.31.0/queryapigate/console_dist/assets/shared-BebZOZXs.js +1 -1
- queryapigate-0.30.0/queryapigate/console_dist/assets/useMutation-BcyO6Hjg.js → queryapigate-0.31.0/queryapigate/console_dist/assets/useMutation-DCzNFnov.js +1 -1
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/console_dist/index.html +1 -1
- {queryapigate-0.30.0 → queryapigate-0.31.0/queryapigate.egg-info}/PKG-INFO +1 -1
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate.egg-info/SOURCES.txt +33 -32
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_helm.py +11 -0
- queryapigate-0.31.0/tests/test_supply_chain.py +95 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/LICENSE +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/README.md +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/pyproject.toml +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/__main__.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/accesslog.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/adminroles.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/admins.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/agentwrites.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/alerts.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/apikeys.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/app.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/auditchain.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/auditstream.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/aws.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/broadcast.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/bundle.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/cache.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/cli.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/collection_admin.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/config.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/configcode.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/console.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/console_dist/assets/index-BLd0t6vZ.css +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/console_dist/assets/state-O13cl7cO.js +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/console_dist/favicon.svg +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/cors.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/databases.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/db.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/definitions.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/deprecations.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/descriptions.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/destinations.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/duckfiles.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/engine.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/errors.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/events.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/examples.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/experimental.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/exporting.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/exports.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/formats.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/governance.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/gunicorn_conf.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/history.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/instances.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/jwtauth.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/lib/h2-2.2.224.jar +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/limits.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/logging_setup.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/masking.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/mcp_server.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/metrics.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/mongotools.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/oidc.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/openapi.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/params.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/parquet.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/pool.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/postman.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/quotas.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/ratelimit.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/rediscache.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/rekey.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/runners.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/schema.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/secheaders.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/secretrefs.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/services/__init__.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/services/access.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/services/administrators.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/services/audit.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/services/collections.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/services/connections.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/services/descriptions.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/services/destinations.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/services/exports.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/services/mcp.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/services/queries.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/sqlflow.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/sqltools.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/sso.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/store.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/tableguard.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/tracing.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/usage.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/v1.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/v1_spec.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate/vault.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate.egg-info/dependency_links.txt +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate.egg-info/entry_points.txt +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate.egg-info/requires.txt +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/queryapigate.egg-info/top_level.txt +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/setup.cfg +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_adhoc_history.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_adminroles.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_admins.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_agent_writes.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_alerts.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_api_administrators.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_api_keys.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_api_v1.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_audit_chain.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_audit_log.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_audit_stream.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_aws.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_backup_restore.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_broadcast.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_cli.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_collection_bundle.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_collection_postman.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_collections.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_configcode.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_connection_encryption.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_console.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_cors_and_rate_limit.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_data_protection.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_databases.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_db.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_dependency_floors.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_deprecations.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_descriptions.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_destinations.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_docs_site.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_duckdb.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_duckdb_files.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_errors.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_events.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_events_server.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_examples.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_experimental.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_export_security.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_exporting.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_exports.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_fips.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_history.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_howto.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_instances.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_integration.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_jdbc.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_jwt.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_key_rotation.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_limits.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_masking.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_masking_requests.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_mcp.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_mcp_keycloak.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_mcp_oauth.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_mongotools.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_multinode.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_observability.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_oidc.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_params.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_parquet.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_pool.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_publishing.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_query_window.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_queryapigate.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_quotas.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_rekey.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_release_check.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_response_cache.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_roles.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_rolling_upgrade.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_secretrefs.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_security_headers.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_settings.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_shared_rate_limit.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_sql_guard_fuzz.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_sql_guard_writes.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_sqlflow.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_sso.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_sso_keycloak.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_tableguard.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_tracing.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_upgrades.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.0}/tests/test_usage.py +0 -0
- {queryapigate-0.30.0 → queryapigate-0.31.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.31.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
|
|
@@ -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};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{$ as e,C as t,D as n,E as r,H as i,L as a,N as o,P as s,Q as c,R as l,Z as u,j as d,l as f,n as p,o as m,r as h,t as g,u as _,z as v}from"./index-
|
|
1
|
+
import{$ as e,C as t,D as n,E as r,H as i,L as a,N as o,P as s,Q as c,R as l,Z as u,j as d,l as f,n as p,o as m,r as h,t as g,u as _,z as v}from"./index-BNMV1syF.js";import{c as y,n as b,t as ee}from"./SchemaBrowser-D57iE2Kg.js";import{a as te,i as x}from"./CodeBox-B8iq2Oop.js";import{n as ne,r as S,t as re}from"./Results-DofTMPkj.js";import{Impact as C,t as w}from"./forms-DR5CXITG.js";var T=e(c(),1),E=()=>h().format,D=null;function ie(){return D??={type:``,host:``,connection:``,database:``,format:E(),sql:``,params:``,page:1,pageSize:p(),timeout:``},D}function ae(e){D=e}var O=`queryapigate-run-history`,k=20;function A(){try{return JSON.parse(sessionStorage.getItem(O)||`[]`)}catch{return[]}}function j(e){try{sessionStorage.setItem(O,JSON.stringify(e))}catch{}}function oe(e,t,n){let r=e.trim();if(!r)return A();let i=A().filter(e=>e.sql!==r||e.connection!==t);i.unshift({sql:r,connection:t,format:n,status:null,rows:null,duration_ms:null});let a=i.slice(0,k);return j(a),a}function se(e,t,n){let r=A();return r.length?(r[0]={...r[0],status:e,rows:t,duration_ms:n},j(r),r):r}var M=/([A-Za-z_]\w*)\s*(=|!=|<>|<=|>=|<|>)\s*('(?:[^']|'')*'|(?:-?\d+(?:\.\d+)?|NULL|TRUE|FALSE)\b)/dgi;function ce(e,t,n){let r=e.lastIndexOf(`
|
|
2
2
|
`,t-1)+1,i=e.indexOf(`
|
|
3
3
|
`,n);i===-1&&(i=e.length);let a=e.slice(r,i),o=t-r,s=n-r;M.lastIndex=0;for(let e=M.exec(a);e;e=M.exec(a)){let t=e.indices[1],n=e.indices[3];if(o<t[1]&&s>t[0]||o<n[1]&&s>n[0])return{name:e[1],valueText:e[3],absStart:r+n[0],absEnd:r+n[1]}}return null}function le(e){return/^null$/i.test(e)?null:/^true$/i.test(e)?!0:/^false$/i.test(e)?!1:/^-?\d+(\.\d+)?$/.test(e)?Number(e):e.startsWith(`'`)&&e.endsWith(`'`)?e.slice(1,-1).replace(/''/g,`'`):e}function ue(e){let t;try{t=JSON.parse(e)}catch{return[]}let n=[],r=e=>{if(typeof e==`string`){let t=/^:([A-Za-z_]\w*)$/.exec(e);t&&!n.includes(t[1])&&n.push(t[1])}else Array.isArray(e)?e.forEach(r):e&&typeof e==`object`&&Object.values(e).forEach(r)};return r(t),n}var N=u(),de=[`mysql`,`postgres`,`clickhouse`,`mongo`],P=`queryapigate-ui-run-side-tab`,fe=`queryapigate-ui-runner-side-w`,F=220,I=560,L=320;function R(e){return e.host?e.host+(e.port?`:`+e.port:``):`(local file)`}function pe(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!==void 0&&e.avg_duration_ms!==null&&t.push(e.avg_duration_ms+`ms avg`),t.join(` · `)}function z(e){try{return localStorage.getItem(e)}catch{return null}}function B(e,t){try{localStorage.setItem(e,t)}catch{}}function V(){let e=v(),o=l(),{showError:s}=a(),c=_(),u=(0,T.useMemo)(()=>c.data??[],[c.data]),f=(0,T.useMemo)(()=>Object.fromEntries(u.map(e=>[e.name,e])),[u]),[p,m]=(0,T.useState)(ie);(0,T.useEffect)(()=>ae(p),[p]);let h=e=>m(t=>({...t,...e})),y=(0,T.useRef)(null),C=[...new Set(u.map(e=>e.db))].sort(),E=C.includes(p.type)?p.type:``,D=[...new Set(u.filter(e=>!E||e.db===E).map(R))].sort(),O=D.includes(p.host)?p.host:``,k=u.filter(e=>(!E||e.db===E)&&(!O||R(e)===O)).map(e=>e.name).sort(),j=k.includes(p.connection)?p.connection:k.find(e=>f[e]?.active)??``,M=f[j],P=M?.db===`mongo`,L=!!(M&&de.includes(M.db)),B=i({queryKey:[`connections`,j,`databases`],queryFn:async()=>n(await t.POST(`/api/v1/connections/databases`,{body:{name:j}})),enabled:L,retry:!1,staleTime:3e5}),V=B.data?.databases??[],H=V.includes(p.database)?p.database:M?.database??``,U=M&&H&&H!==M.database?H:null,ye=b(j||void 0,U),be=(0,T.useMemo)(()=>Object.fromEntries((ye.data?.tables??[]).map(e=>[e.name,e.columns.map(e=>e.name)])),[ye.data]),[xe,Se]=(0,T.useState)(null),[W,Ce]=(0,T.useState)(!1),[G,we]=(0,T.useState)(null),[Te,K]=(0,T.useState)(A),[Ee,De]=(0,T.useState)(),[q,Oe]=(0,T.useState)(!1);async function J(e={}){let t=e.connection??j,n=f[t];if(!t||!n){s(`Pick a connection first — add one on the Connections tab.`);return}let i=n.db===`mongo`,a=e.sql??p.sql;if(e.explain&&i){s(`EXPLAIN is not supported for Mongo connections yet.`);return}if(!a.trim()){s(i?`Write a query first.`:`Write some SQL to run.`,{errors:{sql:`This field is required`}});return}let o={};if(p.params.trim())try{o=JSON.parse(p.params)}catch(e){s(`Bound parameters must be valid JSON: `+e.message,{errors:{params:e.message}});return}let c=e.page??1,l=e.pageSize??(p.pageSize||10),u=`format=${encodeURIComponent(p.format)}&page=${c}&page_size=${l}${p.timeout?`&timeout=`+encodeURIComponent(p.timeout):``}`,m,g;if(i){let e;try{e=JSON.parse(a)}catch(e){s(`The query must be valid JSON: `+e.message,{errors:{sql:e.message}});return}if(!e||typeof e!=`object`||Array.isArray(e)||!e.collection){s(`The query must be a JSON object with a "collection" field, e.g. {"collection": "users", "filter": {}}.`,{errors:{sql:`collection is required`}});return}m=`/execute_mongo?${u}`,g={collection:e.collection,filter:e.filter??{},connection_name:t,params:o},e.projection&&(g.projection=e.projection),e.sort&&(g.sort=e.sort)}else m=`/execute_sql?${u}`,g={sql:e.explain?`EXPLAIN `+a:a,connection_name:t,params:o},t===j&&U&&(g.database=U);Oe(!!e.explain),h({page:c}),s(``),!e.explain&&c===1&&e.page===void 0&&K(oe(a,t,p.format)),Ce(e.explain?`explain`:`run`),De([`curl -X POST ${x(new URL(m,globalThis.location?.href??`http://localhost`).href)}`,` -H 'Content-Type: application/json'`,...d()?[` -H 'X-API-Key: YOUR_KEY_HERE' # replace with your own key`]:[],` -d ${x(JSON.stringify(g))}`].join(` \\
|
|
4
4
|
`));try{let{response:t,elapsed:n}=await r(m,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(g)}),i=await ne(t,{page:c,size:l,format:p.format,elapsed:n,filename:e.explain?`explain`:`query`});i.error&&s(i.error,{status:i.status}),Se(i);let a=i.rows?i.rows.length:null;we({status:i.status,rows:a,elapsed:n,format:p.format,widest:i.rows?S(i.rows):null}),e.explain||K(se(i.status,a,n))}catch(e){s(`Network error: `+e.message)}finally{Ce(!1)}}let ke=o.state,Y=ke?.preview,X=ke?.select,Ae=(0,T.useRef)(null);(0,T.useEffect)(()=>{if(!X||Ae.current===o.key||!f[X])return;Ae.current=o.key;let t=f[X];m(e=>({...e,type:t.db,host:R(t),connection:t.name,database:``})),e(o.pathname,{replace:!0,state:null})},[X,o.key,o.pathname,f,e]);let je=(0,T.useRef)(null);(0,T.useEffect)(()=>{if(!Y||je.current===o.key||!f[Y.connection])return;je.current=o.key;let t=f[Y.connection],n=t.db===`mongo`?JSON.stringify({collection:Y.table,filter:{}},null,2):`SELECT * FROM `+Y.table;m(e=>({...e,type:t.db,host:R(t),connection:t.name,database:``,sql:n,page:1})),J({sql:n,connection:t.name}),e(o.pathname,{replace:!0,state:null})},[Y,o.key,o.pathname,f,J,e]);function Me(e){let t=f[e.connection];h({sql:e.sql,format:e.format||p.format,...t?{type:t.db,host:R(t),connection:t.name,database:``}:{}})}let[Z,Q]=(0,T.useState)(null);function Ne(e){let t=y.current?.selection();if(!t||t.from===t.to)return;let n=ce(t.doc,t.from,t.to);n&&Q({x:e.clientX/g(),y:e.clientY/g(),candidate:n})}function Pe(e){Q(null),y.current?.replaceRange(e.absStart,e.absEnd,`:`+e.name);let t;try{t=p.params.trim()?JSON.parse(p.params):{}}catch{t={}}t[e.name]=le(e.valueText),h({params:JSON.stringify(t,null,2)})}let[Fe,$]=(0,T.useState)(!1);function Ie(){if(!j){s(`Pick a connection first — add one on the Connections tab.`);return}if(!p.sql.trim()){s(`Write a query first.`,{errors:{sql:`This field is required`}});return}$(!0)}let Le=P?ue(p.sql):te(p.sql),Re=pe(M?.usage);return(0,N.jsxs)(N.Fragment,{children:[(0,N.jsx)(`div`,{className:`page-head`,children:(0,N.jsxs)(`div`,{className:`titles`,children:[(0,N.jsx)(`h1`,{children:`API Designer`}),(0,N.jsx)(`span`,{className:`sub`,children:`Ad-hoc statements against any active connection. Nothing here is saved.`})]})}),(0,N.jsxs)(`form`,{id:`run-form`,className:`panel runner`,noValidate:!0,onSubmit:e=>{e.preventDefault(),J()},style:{"--runner-side-w":void 0},ref:e=>{let t=Number(z(fe));e&&t&&!e.style.getPropertyValue(`--runner-side-w`)&&e.style.setProperty(`--runner-side-w`,Math.max(F,Math.min(I,t))+`px`)},children:[(0,N.jsxs)(`div`,{className:`runner-main`,children:[(0,N.jsxs)(`div`,{className:`runner-bar`,children:[(0,N.jsx)(`span`,{className:`lbl`,children:`Type`}),(0,N.jsxs)(`select`,{id:`run-conn-type`,"aria-label":`Database type`,value:E,onChange:e=>h({type:e.target.value}),children:[(0,N.jsx)(`option`,{value:``,children:`All types`}),C.map(e=>(0,N.jsx)(`option`,{value:e,children:e},e))]}),(0,N.jsx)(`span`,{className:`lbl`,children:`Host`}),(0,N.jsxs)(`select`,{id:`run-conn-host`,"aria-label":`Host`,value:O,onChange:e=>h({host:e.target.value}),children:[(0,N.jsx)(`option`,{value:``,children:`All hosts`}),D.map(e=>(0,N.jsx)(`option`,{value:e,children:e},e))]}),(0,N.jsx)(`span`,{className:`lbl`,children:`Connection`}),(0,N.jsx)(`select`,{id:`run-connection`,required:!0,"aria-label":`Connection`,value:j,onChange:e=>h({connection:e.target.value,database:``}),children:k.map(e=>(0,N.jsx)(`option`,{value:e,children:`${e} · ${f[e]?.database||`?`}${f[e]?.active?``:` (inactive)`}`},e))}),(0,N.jsx)(`span`,{className:`lbl`,children:`Database`}),(0,N.jsx)(`select`,{id:`run-database`,"aria-label":`Database`,disabled:!L||!B.isSuccess,value:L&&B.isSuccess?H:``,onChange:e=>h({database:e.target.value}),children:M?L?B.isPending?(0,N.jsx)(`option`,{value:``,children:`Loading…`}):B.isError?(0,N.jsx)(`option`,{value:``,children:`Unavailable`}):V.map(e=>(0,N.jsx)(`option`,{value:e,children:e},e)):(0,N.jsx)(`option`,{value:``,children:M.database||`(default)`}):(0,N.jsx)(`option`,{value:``,children:`—`})}),(0,N.jsx)(`span`,{className:`lbl`,children:`Format`}),(0,N.jsx)(`select`,{id:`run-format`,"aria-label":`Format`,value:p.format,onChange:e=>h({format:e.target.value}),children:[`json`,`ndjson`,`csv`,`tsv`,`xml`,`yaml`,`xlsx`,`parquet`].map(e=>(0,N.jsx)(`option`,{value:e,children:e},e))}),(0,N.jsx)(`span`,{className:`spacer`}),(0,N.jsxs)(`span`,{className:`hint hide-sm`,children:[(0,N.jsx)(`kbd`,{children:`Ctrl`}),` `,(0,N.jsx)(`kbd`,{children:`Enter`})]}),(0,N.jsx)(`button`,{type:`button`,className:`btn ghost md`,id:`run-save-as-api-button`,title:`Save this query as a new API`,onClick:Ie,children:`Save as New API`}),!P&&(0,N.jsx)(`button`,{type:`button`,className:`btn ghost md`,id:`run-explain-button`,title:`Run EXPLAIN on this query`,disabled:!!W,onClick:()=>void J({explain:!0}),children:`Explain`}),(0,N.jsx)(`button`,{type:`submit`,className:`btn primary md`,id:`run-button`,style:{padding:`0 16px`},disabled:!!W,children:`Run`})]}),(0,N.jsx)(`div`,{className:`runner-conn-context`,id:`run-conn-context`,children:M&&(0,N.jsxs)(N.Fragment,{children:[(0,N.jsx)(`span`,{className:`dot ${M.active?`ok`:`bad`}`,title:M.active?`Active`:`Inactive`}),(0,N.jsx)(`span`,{children:M.name}),(0,N.jsx)(`span`,{className:`dim`,children:M.host?`${R(M)} · ${M.database||`?`}`:M.database||`(local file)`}),(0,N.jsx)(`span`,{className:`dim`,children:Re||`No activity yet`})]})}),(0,N.jsx)(w,{handle:y,id:`run-sql`,boxed:!1,value:p.sql,onChange:e=>h({sql:e}),dialect:M?.db,schema:be,label:P?`Mongo find query (JSON)`:`SQL`,placeholder:P?`{"collection": "orders", "filter": {"status": ":status"}}`:`SELECT * FROM t WHERE id = :id`,onRun:()=>void J(),onDoubleClick:Ne})]}),(0,N.jsx)(he,{}),(0,N.jsx)(`div`,{className:`runner-side`,children:(0,N.jsx)(me,{recent:(0,N.jsx)(ge,{entries:Te,onPick:Me}),settings:(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`div`,{className:`field`,children:[(0,N.jsxs)(`label`,{htmlFor:`run-params`,children:[`Bound parameters `,(0,N.jsx)(`span`,{className:`type`,children:`JSON`})]}),(0,N.jsx)(`textarea`,{id:`run-params`,spellCheck:!1,placeholder:`{"id": 1}`,value:p.params,onChange:e=>h({params:e.target.value}),onKeyDown:e=>{e.key===`Enter`&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),J())}}),(0,N.jsxs)(`div`,{className:`refs`,id:`run-refs`,children:[!P&&Le.length>0&&(0,N.jsx)(`span`,{className:`hint`,children:`In SQL:`}),!P&&Le.map(e=>(0,N.jsxs)(`span`,{className:`tag`,children:[`:`,e]},e))]})]}),(0,N.jsxs)(`div`,{className:`grid2`,children:[(0,N.jsxs)(`div`,{className:`field`,children:[(0,N.jsx)(`label`,{htmlFor:`run-page`,children:`Page`}),(0,N.jsx)(`input`,{id:`run-page`,type:`number`,min:`1`,value:p.page,onChange:e=>h({page:Number(e.target.value)||1})})]}),(0,N.jsxs)(`div`,{className:`field`,children:[(0,N.jsx)(`label`,{htmlFor:`run-page-size`,children:`Page size`}),(0,N.jsx)(`input`,{id:`run-page-size`,type:`number`,min:`1`,value:p.pageSize,onChange:e=>h({pageSize:Number(e.target.value)||10})})]})]}),(0,N.jsxs)(`div`,{className:`field`,children:[(0,N.jsxs)(`label`,{htmlFor:`run-timeout`,children:[`Timeout `,(0,N.jsx)(`span`,{className:`type`,children:`seconds, optional`})]}),(0,N.jsx)(`input`,{id:`run-timeout`,type:`number`,min:`0`,step:`any`,placeholder:`server default`,value:p.timeout,onChange:e=>h({timeout:e.target.value})})]})]}),schema:(0,N.jsx)(`div`,{id:`run-schema-slot`,children:(0,N.jsx)(ee,{connection:j,dialect:M?.db,database:U,editor:y,isMongo:P,onPreview:e=>{let t=P?JSON.stringify({collection:e,filter:{}},null,2):`SELECT * FROM `+e;h({sql:t,page:1}),J({sql:t})},showDdl:!0,showUsage:!0})})})})]}),(0,N.jsx)(`div`,{id:`run-quick-stats`,children:G&&(0,N.jsxs)(N.Fragment,{children:[(0,N.jsx)(`span`,{className:`dot ${G.status<400?`ok`:`bad`}`}),(0,N.jsxs)(`span`,{children:[(0,N.jsx)(`b`,{children:G.status}),G.status<400?` OK`:` error`]}),G.rows!==null&&(0,N.jsxs)(`span`,{children:[(0,N.jsx)(`b`,{children:G.rows}),G.rows===1?` row`:` rows`]}),(0,N.jsxs)(`span`,{children:[(0,N.jsx)(`b`,{children:G.elapsed}),` ms`]}),(0,N.jsx)(`span`,{className:`dim`,children:G.format}),G.widest&&(0,N.jsx)(`span`,{className:`dim`,children:`Widest column: ${G.widest.column} (~${G.widest.maxLen} chars)`})]})}),(0,N.jsx)(`div`,{className:`panel`,id:`run-save-panel`,hidden:!Fe,style:{marginTop:12},children:Fe&&(0,N.jsx)(ve,{connection:j,sql:p.sql,isMongo:P,onClose:()=>$(!1),onSaved:t=>{$(!1),e(`/queries/${encodeURIComponent(t)}`)}})}),(0,N.jsx)(re,{result:xe,running:!!W,filename:q?`explain`:`query`,curl:Ee,panelProps:{id:`run-results-panel`,className:`panel results`},onPage:e=>void J({explain:q,page:e}),onPageSize:e=>{h({pageSize:e}),J({explain:q,page:1,pageSize:e})}}),Z&&(0,N.jsx)(_e,{...Z,onApply:Pe,onClose:()=>Q(null)})]})}function me({recent:e,settings:t,schema:n}){let[r,i]=(0,T.useState)(()=>{let e=z(P);return e===`recent`||e===`settings`?e:`schema`}),a=e=>{i(e),B(P,e)},o=(e,t)=>(0,N.jsx)(`button`,{type:`button`,className:r===e?`side-tab active`:`side-tab`,"data-side-tab":e,role:`tab`,"aria-selected":r===e,onClick:()=>a(e),children:t});return(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`div`,{className:`side-tabs`,role:`tablist`,children:[o(`recent`,`Recent Queries`),o(`settings`,`Settings`),o(`schema`,`Schema`)]}),(0,N.jsxs)(`div`,{className:`side-panels`,children:[(0,N.jsx)(`div`,{className:`side-tab-panel`,"data-side-panel":`recent`,hidden:r!==`recent`,children:(0,N.jsx)(`div`,{id:`run-history-slot`,children:e})}),(0,N.jsx)(`div`,{className:`side-tab-panel`,"data-side-panel":`settings`,hidden:r!==`settings`,children:t}),(0,N.jsx)(`div`,{className:`side-tab-panel`,"data-side-panel":`schema`,hidden:r!==`schema`,children:n})]})]})}function he(){let e=(0,T.useRef)(null),t=(0,T.useRef)(null),n=()=>e.current?.closest(`.runner`)??null,r=()=>(n()?.querySelector(`.runner-side`)?.getBoundingClientRect().width??280*g())/g(),i=e=>n()?.style.setProperty(`--runner-side-w`,e+`px`),a=()=>{let e=parseFloat(getComputedStyle(n()).getPropertyValue(`--runner-side-w`));e&&B(fe,String(e))};return(0,N.jsx)(`div`,{ref:e,className:`runner-splitter`,id:`run-splitter`,role:`separator`,"aria-orientation":`vertical`,"aria-label":`Resize sidebar`,tabIndex:0,onPointerDown:n=>{t.current={startX:n.clientX,startW:r()},e.current?.classList.add(`dragging`),e.current?.setPointerCapture(n.pointerId),document.body.style.userSelect=`none`},onPointerMove:e=>{if(!t.current)return;let r=t.current.startW-(e.clientX-t.current.startX)/g(),a=Math.min(I,(n()?.getBoundingClientRect().width??0)/g()-L-7);i(Math.max(F,Math.min(a,r)))},onPointerUp:()=>{t.current&&(t.current=null,e.current?.classList.remove(`dragging`),document.body.style.userSelect=``,a())},onPointerCancel:()=>{t.current=null,e.current?.classList.remove(`dragging`),document.body.style.userSelect=``},onKeyDown:e=>{let t=e.shiftKey?40:12;if(e.key===`ArrowLeft`)i(Math.min(I,r()+t));else if(e.key===`ArrowRight`)i(Math.max(F,r()-t));else return;e.preventDefault(),a()}})}function ge({entries:e,onPick:t}){return e.length?(0,N.jsx)(`div`,{className:`history-list`,children:e.map(e=>{let n=[];return e.status!==null&&e.status>=400&&n.push(`failed (${e.status})`),e.rows!==null&&n.push(e.rows+(e.rows===1?` row`:` rows`)),e.duration_ms!==null&&n.push(e.duration_ms+` ms`),(0,N.jsxs)(`button`,{type:`button`,className:`history-item`,title:e.sql,onClick:()=>t(e),children:[(0,N.jsx)(`span`,{className:`history-sql`,children:e.sql.length>64?e.sql.slice(0,64)+`…`:e.sql}),n.length>0&&(0,N.jsx)(`span`,{className:`history-meta`,children:n.join(` · `)})]},`${e.connection}:${e.sql}`)})}):(0,N.jsx)(`div`,{className:`hint`,children:`Queries you run will show up here.`})}function _e({x:e,y:t,candidate:n,onApply:r,onClose:i}){let a=(0,T.useRef)(null),[o,s]=(0,T.useState)(e);return(0,T.useEffect)(()=>{let t=a.current?.offsetWidth??260;s(Math.max(8,Math.min(e,window.innerWidth/g()-t-12)));let n=e=>{a.current&&!a.current.contains(e.target)&&i()},r=e=>{e.key===`Escape`&&i()},o=setTimeout(()=>{document.addEventListener(`mousedown`,n,!0),document.addEventListener(`keydown`,r,!0)},0);return()=>{clearTimeout(o),document.removeEventListener(`mousedown`,n,!0),document.removeEventListener(`keydown`,r,!0)}},[e,i]),(0,N.jsxs)(`div`,{ref:a,id:`paramize-popover`,className:`paramize-popover`,style:{left:o,top:t+12},children:[(0,N.jsxs)(`div`,{className:`preview`,children:[`${n.name} = ${n.valueText} → ${n.name} = `,(0,N.jsxs)(`b`,{children:[`:`,n.name]})]}),(0,N.jsx)(`button`,{type:`button`,onClick:()=>r(n),children:`Parameterize`})]})}function ve({connection:e,sql:t,isMongo:n,onClose:r,onSaved:i}){let{showError:c,toast:l}=a(),u=m(),d=f(),p=y(),[h,g]=(0,T.useState)(``),[_,v]=(0,T.useState)(``),[b,ee]=(0,T.useState)(``),[x,ne]=(0,T.useState)(``),[S,re]=(0,T.useState)(``),[w,E]=(0,T.useState)(()=>{let e=n?ue(t):te(t);return e.length?JSON.stringify(Object.fromEntries(e.map(e=>[e,{}])),null,2):``});function D(r){let a={};if(w.trim())try{a=JSON.parse(w)}catch(e){c(`query_parameters is not valid JSON: `+e.message,{errors:{query_parameters:e.message}});return}let o=h.trim(),s={author:_,description:b,tags:S.split(`,`).map(e=>e.trim()).filter(Boolean),connection_name:e||void 0,parameters:a,publish:r};if(n){let e;try{e=t.trim()?JSON.parse(t):{}}catch(e){c(`The query must be valid JSON: `+e.message,{errors:{sql_query:e.message}});return}if(!e||typeof e!=`object`||Array.isArray(e)||!e.collection){c(`The query must be a JSON object with a "collection" field, e.g. {"collection": "users", "filter": {}}.`,{errors:{sql_query:`collection is required`}});return}Object.assign(s,{query_type:`mongo`,mongo_collection:e.collection,mongo_filter:e.filter??{}}),e.projection&&(s.mongo_projection=e.projection),e.sort&&(s.mongo_sort=e.sort)}else s.sql=t;let d=u.data?.find(e=>e.name===o),f=x.trim();if(f&&d&&(d.collection??``)!==f){c(`“${o}” already exists${d.collection?` in collection `+d.collection:` with no collection`}. Save it without a collection here, then use “Move…” — that shows which keys gain or lose access first.`);return}let m={...s,name:o,...f?{collection:f}:{}};p.mutate(d?{kind:`version`,name:o,etag:null,body:s}:{kind:`create`,body:m},{onSuccess:e=>{c(``),l(`Saved ${e.name} v${e.latest_version}${r?``:` as a draft`}`),i(e.name)},onError:e=>c(e.message)})}return(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`div`,{className:`panel-head`,children:[(0,N.jsx)(`h2`,{children:`Save as New API`}),(0,N.jsx)(`span`,{className:`spacer`}),(0,N.jsx)(`button`,{type:`button`,className:`btn sm ghost`,title:`Close`,onClick:r,children:`✕`})]}),(0,N.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{e.preventDefault(),D(!0)},children:[(0,N.jsx)(o,{id:`rs-filename`,label:`Filename`,hint:`Letters, digits, spaces, “.”, “_” and “-”. Saving an existing name adds a version.`,children:(0,N.jsx)(`input`,{id:`rs-filename`,placeholder:`films_by_rating`,required:!0,autoComplete:`off`,spellCheck:!1,autoFocus:!0,value:h,onChange:e=>g(e.target.value)})}),(0,N.jsxs)(`div`,{className:`grid2`,children:[(0,N.jsx)(o,{id:`rs-author`,label:`Author`,children:(0,N.jsx)(`input`,{id:`rs-author`,required:!0,autoComplete:`off`,spellCheck:!1,value:_,onChange:e=>v(e.target.value)})}),(0,N.jsx)(o,{id:`rs-description`,label:`Description`,children:(0,N.jsx)(`input`,{id:`rs-description`,required:!0,autoComplete:`off`,spellCheck:!1,value:b,onChange:e=>ee(e.target.value)})})]}),(0,N.jsxs)(`div`,{className:`field`,children:[(0,N.jsx)(`label`,{htmlFor:`rs-collection`,children:`Collection`}),(0,N.jsx)(`input`,{id:`rs-collection`,list:`rs-collection-list`,placeholder:`optional — e.g. reporting`,autoComplete:`off`,spellCheck:!1,value:x,onChange:e=>ne(e.target.value)}),(0,N.jsx)(`datalist`,{id:`rs-collection-list`,children:Object.keys(d.data?.collections??{}).sort().map(e=>(0,N.jsx)(`option`,{value:e},e))}),(0,N.jsx)(`div`,{className:`hint`,children:`Optional. Lowercase letters, digits, “.”, “_” and “-”. Later, use “Move…” to change it.`}),x.trim()&&(0,N.jsx)(C,{moves:[[null,x.trim()]],info:d.data})]}),(0,N.jsx)(o,{id:`rs-tags`,label:`Tags`,children:(0,N.jsx)(`input`,{id:`rs-tags`,placeholder:`comma-separated`,autoComplete:`off`,spellCheck:!1,value:S,onChange:e=>re(e.target.value)})}),(0,N.jsx)(o,{id:`rs-params`,label:`query_parameters`,hint:`JSON object: name → type, or {type, min, max, default, description}. Read from the editor above at save time.`,children:(0,N.jsx)(`textarea`,{id:`rs-params`,spellCheck:!1,placeholder:`{"id": {"type": "int", "min": 1}}`,value:w,onChange:e=>E(e.target.value)})}),(0,N.jsxs)(s,{onCancel:r,children:[(0,N.jsx)(`button`,{type:`button`,className:`btn`,disabled:p.isPending,title:`Save without publishing`,onClick:()=>D(!1),children:`Save as draft`}),(0,N.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:p.isPending,children:`Save`})]})]})]})}export{V as DesignerPage};
|
|
@@ -1 +1 @@
|
|
|
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,N as s,O as c,P as l,Q as u,S as d,X as f,Z as p,c as m,k as h}from"./index-BNMV1syF.js";import{n as g,t as _}from"./icons-C-i_90Gs.js";import{t as v}from"./Managed-DjB6h9Kc.js";import{r as y,t as b}from"./shared-BebZOZXs.js";var x=t(u(),1),S=p();function C(){let e=b(),t=m(),{openDrawer:a,toast:s}=o(),l=y(),u=f(),d=t(`destinations.write`),p=()=>a({title:`New destination`,content:(0,S.jsx)(w,{})}),h=async e=>{try{let t=r(await n.POST(`/api/v1/destinations/{name}/test`,{params:{path:{name:e}}}));s(`✓ Wrote ${t.object} (${t.elapsed_ms} ms)`)}catch(e){l(e)}},x=async e=>{if(window.confirm(`Delete destination '${e}'? Its credentials are removed; files already written stay.`))try{c(await n.DELETE(`/api/v1/destinations/{name}`,{params:{path:{name:e}}})),s(`Deleted `+e),u.invalidateQueries({queryKey:[`destinations`]})}catch(e){l(e)}},C;return C=e.isPending?(0,S.jsx)(i,{text:`Loading destinations…`}):e.isError?(0,S.jsx)(`div`,{className:`hint`,children:e.error.message}):e.data.length?(0,S.jsx)(`div`,{style:{overflowX:`auto`},children:(0,S.jsxs)(`table`,{className:`grid`,children:[(0,S.jsx)(`thead`,{children:(0,S.jsx)(`tr`,{children:[`Name`,`Writes under`,`Storage`,`Credentials`,``].map((e,t)=>(0,S.jsx)(`th`,{children:e},t))})}),(0,S.jsx)(`tbody`,{children:e.data.map(e=>(0,S.jsxs)(`tr`,{"data-name":e.name,children:[(0,S.jsxs)(`td`,{children:[(0,S.jsx)(`span`,{className:`name`,children:e.name}),` `,(0,S.jsx)(v,{kind:`destinations`,name:e.name})]}),(0,S.jsx)(`td`,{className:`mono`,children:e.url}),(0,S.jsx)(`td`,{className:`dim`,children:[e.storage??`local folder`,e.region,e.endpoint].filter(Boolean).join(` · `)}),(0,S.jsx)(`td`,{className:`dim`,children:e.user?`${e.user} · ${e.password??`no secret`}`:`none`}),(0,S.jsx)(`td`,{children:(0,S.jsxs)(`div`,{className:`actions`,hidden:!d,children:[(0,S.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>void h(e.name),children:`Test`}),(0,S.jsx)(`button`,{type:`button`,className:`btn ghost sm icon`,title:`Edit ${e.name}`,"aria-label":`Edit`,onClick:()=>a({title:`Edit destination`,kicker:e.name,content:(0,S.jsx)(w,{name:e.name})}),children:(0,S.jsx)(_,{})}),(0,S.jsx)(`button`,{type:`button`,className:`btn ghost sm icon danger`,title:`Delete ${e.name}`,"aria-label":`Delete`,onClick:()=>void x(e.name),children:(0,S.jsx)(g,{})})]})})]},e.name))})]})}):(0,S.jsxs)(`div`,{className:`empty`,children:[(0,S.jsx)(`strong`,{children:`No destinations yet`}),(0,S.jsx)(`span`,{children:`A destination is where exports may write - an S3, Google Cloud Storage or R2 bucket prefix, or a folder - and the credentials to write there. Nothing is ever written outside it.`}),d&&(0,S.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:p,children:`New destination`})]}),(0,S.jsxs)(S.Fragment,{children:[(0,S.jsxs)(`div`,{className:`page-head`,children:[(0,S.jsxs)(`div`,{className:`titles`,children:[(0,S.jsx)(`h1`,{children:`Destinations`}),(0,S.jsx)(`span`,{className:`sub`,children:`Buckets and folders exports may write to, and the credentials to write there.`})]}),(0,S.jsx)(`span`,{className:`spacer`}),d&&(0,S.jsx)(`button`,{id:`new-destination`,type:`button`,className:`btn primary`,onClick:p,children:`New destination`})]}),(0,S.jsx)(`div`,{id:`destinations-table`,className:`panel`,children:C})]})}function w({name:e}){let t=a({queryKey:[`destinations`,`detail`,e],queryFn:async()=>h(await n.GET(`/api/v1/destinations/{name}`,{params:{path:{name:e}}})),enabled:!!e,retry:!1,gcTime:0});return e&&t.isPending?(0,S.jsx)(i,{}):e&&t.isError?(0,S.jsx)(`div`,{className:`hint`,children:t.error.message}):(0,S.jsx)(T,{name:e,existing:t.data?.data,etag:t.data?.etag??null},e??``)}function T({name:t,existing:i,etag:a}){let c=!!t,{closeDrawer:u,showError:p,toast:m}=o(),h=y(),g=f(),[_,v]=(0,x.useState)(t??``),[b,C]=(0,x.useState)(i?.url??``),[w,T]=(0,x.useState)(i?.region??``),[E,D]=(0,x.useState)(i?.endpoint??``),[O,k]=(0,x.useState)(i?.url_style??``),[A,j]=(0,x.useState)(i?.use_ssl!==!1),[M,N]=(0,x.useState)(i?.account_id??``),[P,F]=(0,x.useState)(i?.user??``),[I,L]=(0,x.useState)(i?.password??``),[R,z]=(0,x.useState)(null),B=/^(s3|gs|gcs|r2):\/\//.test(b),V=e=>({url:b.trim(),region:B&&w.trim()||e,endpoint:B&&E.trim()||e,url_style:B&&O||e,use_ssl:B&&!A?!1:e,account_id:B&&M.trim()||e,user:B&&P.trim()||e,password:B&&I||e}),H=e({mutationFn:async()=>r(c?await n.PATCH(`/api/v1/destinations/{name}`,{params:{path:{name:t},header:a?{"If-Match":a}:{}},body:V(null)}):await n.POST(`/api/v1/destinations`,{body:{name:_.trim(),...V(void 0)}})),onSuccess:e=>{p(``),g.invalidateQueries({queryKey:[`destinations`]}),u(),m((c?`Updated `:`Created `)+e.name)},onError:h}),U=e({mutationFn:async()=>c&&I===i?.password?r(await n.POST(`/api/v1/destinations/{name}/test`,{params:{path:{name:t}}})):r(await n.POST(`/api/v1/destinations/test`,{body:V(void 0)})),onSuccess:e=>z({ok:!0,text:`✓ Wrote ${e.object} (${e.elapsed_ms} ms)`}),onError:e=>z({ok:!1,text:`✗ `+(e instanceof d,e.message)})});return(0,S.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{if(e.preventDefault(),!c&&!_.trim()){p(`A name is required.`,{errors:{name:`This field is required`}});return}H.mutate()},children:[(0,S.jsx)(s,{id:`d-name`,label:`Name`,hint:c?void 0:`How exports refer to it, e.g. partner-acme.`,children:(0,S.jsx)(`input`,{id:`d-name`,value:_,placeholder:`partner-acme`,autoComplete:`off`,spellCheck:!1,disabled:c,autoFocus:!c,onChange:e=>v(e.target.value)})}),(0,S.jsx)(s,{id:`d-url`,label:`Writes under`,hint:`An s3://, gs:// or r2:// prefix, or a folder (/exports/ or file:///exports/), ending in /. Exports write here and nowhere else.`,children:(0,S.jsx)(`input`,{id:`d-url`,value:b,placeholder:`s3://acme-exchange/from-us/`,autoComplete:`off`,spellCheck:!1,onChange:e=>C(e.target.value)})}),B&&(0,S.jsxs)(`fieldset`,{className:`duck-files`,children:[(0,S.jsx)(`legend`,{children:`Object storage`}),(0,S.jsxs)(`div`,{className:`grid2`,children:[(0,S.jsx)(s,{id:`d-user`,label:`Access key ID`,children:(0,S.jsx)(`input`,{id:`d-user`,value:P,autoComplete:`off`,spellCheck:!1,onChange:e=>F(e.target.value)})}),(0,S.jsx)(s,{id:`d-password`,label:`Secret access key`,hint:c?`Leave the mask to keep the stored secret.`:"${ENV_VAR} references are resolved on the server.",children:(0,S.jsx)(`input`,{id:`d-password`,type:`password`,value:I,autoComplete:`off`,onChange:e=>L(e.target.value)})})]}),(0,S.jsxs)(`div`,{className:`grid2`,children:[(0,S.jsx)(s,{id:`d-region`,label:`Region`,children:(0,S.jsx)(`input`,{id:`d-region`,value:w,placeholder:`eu-west-1`,onChange:e=>T(e.target.value)})}),(0,S.jsx)(s,{id:`d-endpoint`,label:`Endpoint`,hint:`Only for an S3-compatible service, e.g. minio.internal:9000.`,children:(0,S.jsx)(`input`,{id:`d-endpoint`,value:E,onChange:e=>D(e.target.value)})})]}),(0,S.jsxs)(`div`,{className:`grid2`,children:[(0,S.jsx)(s,{id:`d-url-style`,label:`URL style`,children:(0,S.jsxs)(`select`,{id:`d-url-style`,value:O,onChange:e=>k(e.target.value),children:[(0,S.jsx)(`option`,{value:``,children:`Default`}),(0,S.jsx)(`option`,{value:`vhost`,children:`Virtual-hosted (bucket.host)`}),(0,S.jsx)(`option`,{value:`path`,children:`Path (host/bucket)`})]})}),(0,S.jsx)(s,{id:`d-account`,label:`Account ID`,hint:`Cloudflare R2 only.`,children:(0,S.jsx)(`input`,{id:`d-account`,value:M,onChange:e=>N(e.target.value)})})]}),(0,S.jsxs)(`label`,{className:`switch`,children:[(0,S.jsx)(`input`,{id:`d-ssl`,type:`checkbox`,checked:A,onChange:e=>j(e.target.checked)}),`Use HTTPS`]})]}),(0,S.jsxs)(`div`,{className:`test-row`,children:[(0,S.jsx)(`button`,{type:`button`,className:`btn`,disabled:U.isPending||!b.trim(),onClick:()=>U.mutate(),children:U.isPending?`Testing…`:`Test`}),(0,S.jsx)(`span`,{className:`test-result`,children:R&&(0,S.jsx)(`span`,{className:R.ok?`test-ok`:`test-fail`,children:R.text})})]}),(0,S.jsxs)(`div`,{className:`hint`,children:[`Test writes one small file, `,`_queryapigate_probe.csv`,`, under the prefix - overwritten each time.`]}),(0,S.jsx)(l,{onCancel:u,children:(0,S.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:H.isPending,children:c?`Save`:`Create`})})]})}export{C as DestinationsPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./useMutation-BcyO6Hjg.js";import{$ as t,C as n,D as r,F as i,H as a,L as o,N as s,O as c,P as l,Q as u,X as d,Z as f,c as p,k as m,o as h}from"./index-DvCopLMA.js";import{t as g}from"./Time-KeJDv2te.js";import{n as _,t as v}from"./maskRules-9C_A2JIu.js";import{n as y,t as b}from"./icons-CeIi0TUG.js";import{t as x}from"./Managed-CQVNTWrU.js";import{n as S,r as C,t as w}from"./shared-rt98BmTW.js";var T=t(u(),1),E=f(),D=[`parquet`,`csv`,`ndjson`];function O(){let t=S(),a=p(),{openDrawer:s,toast:l}=o(),u=C(),f=d(),m=a(`exports.write`),h=()=>s({title:`New export`,wide:!0,content:(0,E.jsx)(A,{})}),g=e({mutationFn:async e=>r(await n.POST(`/api/v1/exports/{name}/runs`,{params:{path:{name:e}}})),onSuccess:e=>{l(e.object?`✓ ${e.rows} ${e.rows===1?`row`:`rows`} written to ${e.object}`:`✓ No new rows - nothing written`),f.invalidateQueries({queryKey:[`exports`]})},onError:e=>{u(e),f.invalidateQueries({queryKey:[`exports`]})}}),_=async e=>{if(window.confirm(`Delete export '${e}'? Files it already wrote stay where they are.`))try{c(await n.DELETE(`/api/v1/exports/{name}`,{params:{path:{name:e}}})),l(`Deleted `+e),f.invalidateQueries({queryKey:[`exports`]})}catch(e){u(e)}},v;return v=t.isPending?(0,E.jsx)(i,{text:`Loading exports…`}):t.isError?(0,E.jsx)(`div`,{className:`hint`,children:t.error.message}):t.data.length?(0,E.jsx)(`div`,{style:{overflowX:`auto`},children:(0,E.jsxs)(`table`,{className:`grid`,children:[(0,E.jsx)(`thead`,{children:(0,E.jsx)(`tr`,{children:[`Name`,`Query`,`Writes to`,`Format`,`Watermark`,``].map((e,t)=>(0,E.jsx)(`th`,{children:e},t))})}),(0,E.jsx)(`tbody`,{children:t.data.map(e=>(0,E.jsxs)(`tr`,{"data-name":e.name,children:[(0,E.jsx)(`td`,{children:(0,E.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,E.jsx)(`span`,{className:`name`,children:e.name}),(0,E.jsx)(x,{kind:`exports`,name:e.name}),e.running&&(0,E.jsx)(`span`,{className:`tag`,children:`running`})]})}),(0,E.jsx)(`td`,{className:`mono`,children:e.query}),(0,E.jsxs)(`td`,{className:`mono`,children:[e.destination,`:`,e.path]}),(0,E.jsx)(`td`,{children:e.format}),(0,E.jsx)(`td`,{className:e.incremental?`mono`:`dim`,children:e.incremental?`${e.incremental.column} > ${e.watermark??e.incremental.start??`(start)`}`:`everything, each run`}),(0,E.jsx)(`td`,{children:(0,E.jsxs)(`div`,{className:`actions`,children:[a(`exports.run`)&&(0,E.jsx)(`button`,{type:`button`,className:`btn sm outlined`,disabled:g.isPending||e.running,onClick:()=>g.mutate(e.name),children:g.isPending&&g.variables===e.name?`Running…`:`Run now`}),(0,E.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>s({title:`Runs`,kicker:e.name,wide:!0,content:(0,E.jsx)(k,{name:e.name})}),children:`Runs`}),m&&(0,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(`button`,{type:`button`,className:`btn ghost sm icon`,title:`Edit ${e.name}`,"aria-label":`Edit`,onClick:()=>s({title:`Edit export`,kicker:e.name,wide:!0,content:(0,E.jsx)(A,{name:e.name})}),children:(0,E.jsx)(b,{})}),(0,E.jsx)(`button`,{type:`button`,className:`btn ghost sm icon danger`,title:`Delete ${e.name}`,"aria-label":`Delete`,onClick:()=>void _(e.name),children:(0,E.jsx)(y,{})})]})]})})]},e.name))})]})}):(0,E.jsxs)(`div`,{className:`empty`,children:[(0,E.jsx)(`strong`,{children:`No exports yet`}),(0,E.jsx)(`span`,{children:`An export delivers a saved query's result as a file to a destination - Parquet, CSV or NDJSON - and, if incremental, only what changed since the last run. Run it here, or from cron, a Kubernetes CronJob or Airflow.`}),m&&(0,E.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:h,children:`New export`})]}),(0,E.jsxs)(E.Fragment,{children:[(0,E.jsxs)(`div`,{className:`page-head`,children:[(0,E.jsxs)(`div`,{className:`titles`,children:[(0,E.jsx)(`h1`,{children:`Exports`}),(0,E.jsx)(`span`,{className:`sub`,children:`Saved queries delivered as files to a bucket or a folder.`})]}),(0,E.jsx)(`span`,{className:`spacer`}),m&&(0,E.jsx)(`button`,{id:`new-export`,type:`button`,className:`btn primary`,onClick:h,children:`New export`})]}),(0,E.jsx)(`div`,{id:`exports-table`,className:`panel`,children:v})]})}function k({name:e}){let t=a({queryKey:[`exports`,e,`runs`],queryFn:async()=>r(await n.GET(`/api/v1/exports/{name}/runs`,{params:{path:{name:e}}})).items,retry:!1});return t.isPending?(0,E.jsx)(i,{text:`Loading runs…`}):t.isError?(0,E.jsx)(`div`,{className:`hint`,children:t.error.message}):t.data.length?(0,E.jsxs)(`table`,{className:`grid`,id:`export-runs`,children:[(0,E.jsx)(`thead`,{children:(0,E.jsx)(`tr`,{children:[`When`,`Status`,`Rows`,`Written`,`By`].map(e=>(0,E.jsx)(`th`,{children:e},e))})}),(0,E.jsx)(`tbody`,{children:t.data.map(e=>(0,E.jsxs)(`tr`,{children:[(0,E.jsx)(`td`,{className:`mono`,style:{whiteSpace:`nowrap`},children:(0,E.jsx)(g,{value:e.executed_at})}),(0,E.jsxs)(`td`,{children:[(0,E.jsx)(`span`,{className:`dot ${e.status===`success`?`ok`:`bad`}`}),` `,e.status]}),(0,E.jsx)(`td`,{className:`mono`,children:e.rows??`—`}),(0,E.jsx)(`td`,{className:e.object?`mono`:`dim`,children:e.status===`error`?e.error:e.object??`nothing - no new rows`}),(0,E.jsx)(`td`,{className:`dim`,children:e.key_name??`—`})]},String(e.run_id??e.executed_at)))})]}):(0,E.jsx)(`div`,{className:`hint`,children:`No runs yet.`})}function A({name:e}){let t=a({queryKey:[`exports`,`detail`,e],queryFn:async()=>m(await n.GET(`/api/v1/exports/{name}`,{params:{path:{name:e}}})),enabled:!!e,retry:!1,gcTime:0});return e&&t.isPending?(0,E.jsx)(i,{}):e&&t.isError?(0,E.jsx)(`div`,{className:`hint`,children:t.error.message}):(0,E.jsx)(j,{name:e,existing:t.data?.data,etag:t.data?.etag??null},e??``)}function j({name:t,existing:i,etag:a}){let c=!!t,{closeDrawer:u,showError:f,toast:p}=o(),m=C(),g=d(),y=h(),b=w(),[x,S]=(0,T.useState)(t??``),[O,k]=(0,T.useState)(i?.query??``),[A,j]=(0,T.useState)(i?.destination??``),[M,N]=(0,T.useState)(i?.format??`parquet`),[P,F]=(0,T.useState)(i?.path??`{name}/{date}/{name}_{run}.parquet`),[I,L]=(0,T.useState)(i?.params&&Object.keys(i.params).length?JSON.stringify(i.params):``),[R,z]=(0,T.useState)(!!i?.incremental),[B,V]=(0,T.useState)(i?.incremental?.column??``),[H,U]=(0,T.useState)(i?.incremental?.parameter??``),[W,G]=(0,T.useState)(String(i?.incremental?.start??``)),[K,q]=(0,T.useState)(!i||i.skip_empty),[J,Y]=(0,T.useState)((i?.mask??[]).map(e=>({column:e.column,action:e.action}))),[X,Z]=(0,T.useState)(i?.description??``),Q=e=>{N(e),F(t=>t.replace(/\.(parquet|csv|ndjson)$/,`.`+e))},$=e({mutationFn:async()=>{let e={};if(I.trim())try{e=JSON.parse(I)}catch{throw Error(`Parameters must be a JSON object, e.g. {"region": "EU"}.`)}let i={query:O,destination:A,format:M,path:P.trim(),params:e,skip_empty:K,mask:v(J,c?null:void 0),description:X.trim()||(c?null:void 0),incremental:R?{column:B.trim(),parameter:H.trim(),...W.trim()?{start:W.trim()}:{}}:c?null:void 0};return r(c?await n.PATCH(`/api/v1/exports/{name}`,{params:{path:{name:t},header:a?{"If-Match":a}:{}},body:i}):await n.POST(`/api/v1/exports`,{body:{name:x.trim(),...i}}))},onSuccess:e=>{f(``),g.invalidateQueries({queryKey:[`exports`]}),u(),p((c?`Updated `:`Created `)+e.name)},onError:m}),ee=e=>{if(e.preventDefault(),!c&&!x.trim()){f(`A name is required.`,{errors:{name:`This field is required`}});return}$.mutate()},te=(y.data??[]).map(e=>e.name).sort(),ne=(b.data??[]).map(e=>e.name);return(0,E.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:ee,children:[(0,E.jsxs)(`div`,{className:`grid2`,children:[(0,E.jsx)(s,{id:`x-name`,label:`Name`,children:(0,E.jsx)(`input`,{id:`x-name`,value:x,placeholder:`acme-daily-orders`,autoComplete:`off`,spellCheck:!1,disabled:c,autoFocus:!c,onChange:e=>S(e.target.value)})}),(0,E.jsx)(s,{id:`x-description`,label:`Description`,hint:`Optional.`,children:(0,E.jsx)(`input`,{id:`x-description`,value:X,onChange:e=>Z(e.target.value)})})]}),(0,E.jsxs)(`div`,{className:`grid2`,children:[(0,E.jsx)(s,{id:`x-query`,label:`Saved query`,hint:`Its published version runs.`,children:(0,E.jsxs)(`select`,{id:`x-query`,value:O,onChange:e=>k(e.target.value),children:[(0,E.jsx)(`option`,{value:``,children:`Choose a query…`}),te.map(e=>(0,E.jsx)(`option`,{value:e,children:e},e))]})}),(0,E.jsx)(s,{id:`x-destination`,label:`Destination`,children:(0,E.jsxs)(`select`,{id:`x-destination`,value:A,onChange:e=>j(e.target.value),children:[(0,E.jsx)(`option`,{value:``,children:`Choose a destination…`}),ne.map(e=>(0,E.jsx)(`option`,{value:e,children:e},e))]})})]}),(0,E.jsxs)(`div`,{className:`grid2`,children:[(0,E.jsx)(s,{id:`x-path`,label:`File path`,hint:`Under the destination. {name}, {date}, {time}, {run} and the query's parameters ({region}) are filled in.`,children:(0,E.jsx)(`input`,{id:`x-path`,value:P,spellCheck:!1,onChange:e=>F(e.target.value)})}),(0,E.jsx)(s,{id:`x-format`,label:`Format`,children:(0,E.jsx)(`select`,{id:`x-format`,value:M,onChange:e=>Q(e.target.value),children:D.map(e=>(0,E.jsx)(`option`,{value:e,children:e},e))})})]}),(0,E.jsx)(s,{id:`x-params`,label:`Parameters`,hint:`JSON, e.g. {"region": "EU"}. Optional.`,children:(0,E.jsx)(`input`,{id:`x-params`,className:`mono`,value:I,spellCheck:!1,onChange:e=>L(e.target.value)})}),(0,E.jsxs)(`label`,{className:`switch`,children:[(0,E.jsx)(`input`,{id:`x-incremental`,type:`checkbox`,checked:R,onChange:e=>z(e.target.checked)}),`Incremental`,(0,E.jsx)(`span`,{className:`hint`,children:`— each run delivers only what changed since the last one`})]}),R&&(0,E.jsxs)(`div`,{className:`grid2`,style:{gridTemplateColumns:`1fr 1fr 1fr`},children:[(0,E.jsx)(s,{id:`x-column`,label:`Column`,hint:`Its largest value delivered is remembered, e.g. updated_at.`,children:(0,E.jsx)(`input`,{id:`x-column`,value:B,spellCheck:!1,onChange:e=>V(e.target.value)})}),(0,E.jsx)(s,{id:`x-parameter`,label:`Parameter`,hint:`Bound to it next run: WHERE updated_at > :since.`,children:(0,E.jsx)(`input`,{id:`x-parameter`,value:H,spellCheck:!1,onChange:e=>U(e.target.value)})}),(0,E.jsx)(s,{id:`x-start`,label:`Start`,hint:`Bound on the first run.`,children:(0,E.jsx)(`input`,{id:`x-start`,value:W,spellCheck:!1,onChange:e=>G(e.target.value)})})]}),(0,E.jsx)(_,{id:`x-mask`,rules:J,onChange:Y,who:`this export writes`}),(0,E.jsxs)(`label`,{className:`switch`,children:[(0,E.jsx)(`input`,{id:`x-skip-empty`,type:`checkbox`,checked:K,onChange:e=>q(e.target.checked)}),`Skip empty runs`,(0,E.jsx)(`span`,{className:`hint`,children:`— a run with no rows writes no file`})]}),(0,E.jsx)(l,{onCancel:u,children:(0,E.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:$.isPending,children:c?`Save`:`Create`})})]})}export{O as ExportsPage};
|
|
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,N as s,O as c,P as l,Q as u,X as d,Z as f,c as p,k as m,o as h}from"./index-BNMV1syF.js";import{t as g}from"./Time-CR17Ozn9.js";import{n as _,t as v}from"./maskRules-DyD2pxp7.js";import{n as y,t as b}from"./icons-C-i_90Gs.js";import{t as x}from"./Managed-DjB6h9Kc.js";import{n as S,r as C,t as w}from"./shared-BebZOZXs.js";var T=t(u(),1),E=f(),D=[`parquet`,`csv`,`ndjson`];function O(){let t=S(),a=p(),{openDrawer:s,toast:l}=o(),u=C(),f=d(),m=a(`exports.write`),h=()=>s({title:`New export`,wide:!0,content:(0,E.jsx)(A,{})}),g=e({mutationFn:async e=>r(await n.POST(`/api/v1/exports/{name}/runs`,{params:{path:{name:e}}})),onSuccess:e=>{l(e.object?`✓ ${e.rows} ${e.rows===1?`row`:`rows`} written to ${e.object}`:`✓ No new rows - nothing written`),f.invalidateQueries({queryKey:[`exports`]})},onError:e=>{u(e),f.invalidateQueries({queryKey:[`exports`]})}}),_=async e=>{if(window.confirm(`Delete export '${e}'? Files it already wrote stay where they are.`))try{c(await n.DELETE(`/api/v1/exports/{name}`,{params:{path:{name:e}}})),l(`Deleted `+e),f.invalidateQueries({queryKey:[`exports`]})}catch(e){u(e)}},v;return v=t.isPending?(0,E.jsx)(i,{text:`Loading exports…`}):t.isError?(0,E.jsx)(`div`,{className:`hint`,children:t.error.message}):t.data.length?(0,E.jsx)(`div`,{style:{overflowX:`auto`},children:(0,E.jsxs)(`table`,{className:`grid`,children:[(0,E.jsx)(`thead`,{children:(0,E.jsx)(`tr`,{children:[`Name`,`Query`,`Writes to`,`Format`,`Watermark`,``].map((e,t)=>(0,E.jsx)(`th`,{children:e},t))})}),(0,E.jsx)(`tbody`,{children:t.data.map(e=>(0,E.jsxs)(`tr`,{"data-name":e.name,children:[(0,E.jsx)(`td`,{children:(0,E.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,E.jsx)(`span`,{className:`name`,children:e.name}),(0,E.jsx)(x,{kind:`exports`,name:e.name}),e.running&&(0,E.jsx)(`span`,{className:`tag`,children:`running`})]})}),(0,E.jsx)(`td`,{className:`mono`,children:e.query}),(0,E.jsxs)(`td`,{className:`mono`,children:[e.destination,`:`,e.path]}),(0,E.jsx)(`td`,{children:e.format}),(0,E.jsx)(`td`,{className:e.incremental?`mono`:`dim`,children:e.incremental?`${e.incremental.column} > ${e.watermark??e.incremental.start??`(start)`}`:`everything, each run`}),(0,E.jsx)(`td`,{children:(0,E.jsxs)(`div`,{className:`actions`,children:[a(`exports.run`)&&(0,E.jsx)(`button`,{type:`button`,className:`btn sm outlined`,disabled:g.isPending||e.running,onClick:()=>g.mutate(e.name),children:g.isPending&&g.variables===e.name?`Running…`:`Run now`}),(0,E.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>s({title:`Runs`,kicker:e.name,wide:!0,content:(0,E.jsx)(k,{name:e.name})}),children:`Runs`}),m&&(0,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(`button`,{type:`button`,className:`btn ghost sm icon`,title:`Edit ${e.name}`,"aria-label":`Edit`,onClick:()=>s({title:`Edit export`,kicker:e.name,wide:!0,content:(0,E.jsx)(A,{name:e.name})}),children:(0,E.jsx)(b,{})}),(0,E.jsx)(`button`,{type:`button`,className:`btn ghost sm icon danger`,title:`Delete ${e.name}`,"aria-label":`Delete`,onClick:()=>void _(e.name),children:(0,E.jsx)(y,{})})]})]})})]},e.name))})]})}):(0,E.jsxs)(`div`,{className:`empty`,children:[(0,E.jsx)(`strong`,{children:`No exports yet`}),(0,E.jsx)(`span`,{children:`An export delivers a saved query's result as a file to a destination - Parquet, CSV or NDJSON - and, if incremental, only what changed since the last run. Run it here, or from cron, a Kubernetes CronJob or Airflow.`}),m&&(0,E.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:h,children:`New export`})]}),(0,E.jsxs)(E.Fragment,{children:[(0,E.jsxs)(`div`,{className:`page-head`,children:[(0,E.jsxs)(`div`,{className:`titles`,children:[(0,E.jsx)(`h1`,{children:`Exports`}),(0,E.jsx)(`span`,{className:`sub`,children:`Saved queries delivered as files to a bucket or a folder.`})]}),(0,E.jsx)(`span`,{className:`spacer`}),m&&(0,E.jsx)(`button`,{id:`new-export`,type:`button`,className:`btn primary`,onClick:h,children:`New export`})]}),(0,E.jsx)(`div`,{id:`exports-table`,className:`panel`,children:v})]})}function k({name:e}){let t=a({queryKey:[`exports`,e,`runs`],queryFn:async()=>r(await n.GET(`/api/v1/exports/{name}/runs`,{params:{path:{name:e}}})).items,retry:!1});return t.isPending?(0,E.jsx)(i,{text:`Loading runs…`}):t.isError?(0,E.jsx)(`div`,{className:`hint`,children:t.error.message}):t.data.length?(0,E.jsxs)(`table`,{className:`grid`,id:`export-runs`,children:[(0,E.jsx)(`thead`,{children:(0,E.jsx)(`tr`,{children:[`When`,`Status`,`Rows`,`Written`,`By`].map(e=>(0,E.jsx)(`th`,{children:e},e))})}),(0,E.jsx)(`tbody`,{children:t.data.map(e=>(0,E.jsxs)(`tr`,{children:[(0,E.jsx)(`td`,{className:`mono`,style:{whiteSpace:`nowrap`},children:(0,E.jsx)(g,{value:e.executed_at})}),(0,E.jsxs)(`td`,{children:[(0,E.jsx)(`span`,{className:`dot ${e.status===`success`?`ok`:`bad`}`}),` `,e.status]}),(0,E.jsx)(`td`,{className:`mono`,children:e.rows??`—`}),(0,E.jsx)(`td`,{className:e.object?`mono`:`dim`,children:e.status===`error`?e.error:e.object??`nothing - no new rows`}),(0,E.jsx)(`td`,{className:`dim`,children:e.key_name??`—`})]},String(e.run_id??e.executed_at)))})]}):(0,E.jsx)(`div`,{className:`hint`,children:`No runs yet.`})}function A({name:e}){let t=a({queryKey:[`exports`,`detail`,e],queryFn:async()=>m(await n.GET(`/api/v1/exports/{name}`,{params:{path:{name:e}}})),enabled:!!e,retry:!1,gcTime:0});return e&&t.isPending?(0,E.jsx)(i,{}):e&&t.isError?(0,E.jsx)(`div`,{className:`hint`,children:t.error.message}):(0,E.jsx)(j,{name:e,existing:t.data?.data,etag:t.data?.etag??null},e??``)}function j({name:t,existing:i,etag:a}){let c=!!t,{closeDrawer:u,showError:f,toast:p}=o(),m=C(),g=d(),y=h(),b=w(),[x,S]=(0,T.useState)(t??``),[O,k]=(0,T.useState)(i?.query??``),[A,j]=(0,T.useState)(i?.destination??``),[M,N]=(0,T.useState)(i?.format??`parquet`),[P,F]=(0,T.useState)(i?.path??`{name}/{date}/{name}_{run}.parquet`),[I,L]=(0,T.useState)(i?.params&&Object.keys(i.params).length?JSON.stringify(i.params):``),[R,z]=(0,T.useState)(!!i?.incremental),[B,V]=(0,T.useState)(i?.incremental?.column??``),[H,U]=(0,T.useState)(i?.incremental?.parameter??``),[W,G]=(0,T.useState)(String(i?.incremental?.start??``)),[K,q]=(0,T.useState)(!i||i.skip_empty),[J,Y]=(0,T.useState)((i?.mask??[]).map(e=>({column:e.column,action:e.action}))),[X,Z]=(0,T.useState)(i?.description??``),Q=e=>{N(e),F(t=>t.replace(/\.(parquet|csv|ndjson)$/,`.`+e))},$=e({mutationFn:async()=>{let e={};if(I.trim())try{e=JSON.parse(I)}catch{throw Error(`Parameters must be a JSON object, e.g. {"region": "EU"}.`)}let i={query:O,destination:A,format:M,path:P.trim(),params:e,skip_empty:K,mask:v(J,c?null:void 0),description:X.trim()||(c?null:void 0),incremental:R?{column:B.trim(),parameter:H.trim(),...W.trim()?{start:W.trim()}:{}}:c?null:void 0};return r(c?await n.PATCH(`/api/v1/exports/{name}`,{params:{path:{name:t},header:a?{"If-Match":a}:{}},body:i}):await n.POST(`/api/v1/exports`,{body:{name:x.trim(),...i}}))},onSuccess:e=>{f(``),g.invalidateQueries({queryKey:[`exports`]}),u(),p((c?`Updated `:`Created `)+e.name)},onError:m}),ee=e=>{if(e.preventDefault(),!c&&!x.trim()){f(`A name is required.`,{errors:{name:`This field is required`}});return}$.mutate()},te=(y.data??[]).map(e=>e.name).sort(),ne=(b.data??[]).map(e=>e.name);return(0,E.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:ee,children:[(0,E.jsxs)(`div`,{className:`grid2`,children:[(0,E.jsx)(s,{id:`x-name`,label:`Name`,children:(0,E.jsx)(`input`,{id:`x-name`,value:x,placeholder:`acme-daily-orders`,autoComplete:`off`,spellCheck:!1,disabled:c,autoFocus:!c,onChange:e=>S(e.target.value)})}),(0,E.jsx)(s,{id:`x-description`,label:`Description`,hint:`Optional.`,children:(0,E.jsx)(`input`,{id:`x-description`,value:X,onChange:e=>Z(e.target.value)})})]}),(0,E.jsxs)(`div`,{className:`grid2`,children:[(0,E.jsx)(s,{id:`x-query`,label:`Saved query`,hint:`Its published version runs.`,children:(0,E.jsxs)(`select`,{id:`x-query`,value:O,onChange:e=>k(e.target.value),children:[(0,E.jsx)(`option`,{value:``,children:`Choose a query…`}),te.map(e=>(0,E.jsx)(`option`,{value:e,children:e},e))]})}),(0,E.jsx)(s,{id:`x-destination`,label:`Destination`,children:(0,E.jsxs)(`select`,{id:`x-destination`,value:A,onChange:e=>j(e.target.value),children:[(0,E.jsx)(`option`,{value:``,children:`Choose a destination…`}),ne.map(e=>(0,E.jsx)(`option`,{value:e,children:e},e))]})})]}),(0,E.jsxs)(`div`,{className:`grid2`,children:[(0,E.jsx)(s,{id:`x-path`,label:`File path`,hint:`Under the destination. {name}, {date}, {time}, {run} and the query's parameters ({region}) are filled in.`,children:(0,E.jsx)(`input`,{id:`x-path`,value:P,spellCheck:!1,onChange:e=>F(e.target.value)})}),(0,E.jsx)(s,{id:`x-format`,label:`Format`,children:(0,E.jsx)(`select`,{id:`x-format`,value:M,onChange:e=>Q(e.target.value),children:D.map(e=>(0,E.jsx)(`option`,{value:e,children:e},e))})})]}),(0,E.jsx)(s,{id:`x-params`,label:`Parameters`,hint:`JSON, e.g. {"region": "EU"}. Optional.`,children:(0,E.jsx)(`input`,{id:`x-params`,className:`mono`,value:I,spellCheck:!1,onChange:e=>L(e.target.value)})}),(0,E.jsxs)(`label`,{className:`switch`,children:[(0,E.jsx)(`input`,{id:`x-incremental`,type:`checkbox`,checked:R,onChange:e=>z(e.target.checked)}),`Incremental`,(0,E.jsx)(`span`,{className:`hint`,children:`— each run delivers only what changed since the last one`})]}),R&&(0,E.jsxs)(`div`,{className:`grid2`,style:{gridTemplateColumns:`1fr 1fr 1fr`},children:[(0,E.jsx)(s,{id:`x-column`,label:`Column`,hint:`Its largest value delivered is remembered, e.g. updated_at.`,children:(0,E.jsx)(`input`,{id:`x-column`,value:B,spellCheck:!1,onChange:e=>V(e.target.value)})}),(0,E.jsx)(s,{id:`x-parameter`,label:`Parameter`,hint:`Bound to it next run: WHERE updated_at > :since.`,children:(0,E.jsx)(`input`,{id:`x-parameter`,value:H,spellCheck:!1,onChange:e=>U(e.target.value)})}),(0,E.jsx)(s,{id:`x-start`,label:`Start`,hint:`Bound on the first run.`,children:(0,E.jsx)(`input`,{id:`x-start`,value:W,spellCheck:!1,onChange:e=>G(e.target.value)})})]}),(0,E.jsx)(_,{id:`x-mask`,rules:J,onChange:Y,who:`this export writes`}),(0,E.jsxs)(`label`,{className:`switch`,children:[(0,E.jsx)(`input`,{id:`x-skip-empty`,type:`checkbox`,checked:K,onChange:e=>q(e.target.checked)}),`Skip empty runs`,(0,E.jsx)(`span`,{className:`hint`,children:`— a run with no rows writes no file`})]}),(0,E.jsx)(l,{onCancel:u,children:(0,E.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:$.isPending,children:c?`Save`:`Create`})})]})}export{O as ExportsPage};
|