queryapigate 0.25.0__tar.gz → 0.26.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.25.0/queryapigate.egg-info → queryapigate-0.26.0}/PKG-INFO +1 -1
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/__init__.py +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/AccessMapPage-B05IM1yA.js → queryapigate-0.26.0/queryapigate/console_dist/assets/AccessMapPage-rRSiv2mm.js +3 -3
- queryapigate-0.25.0/queryapigate/console_dist/assets/AdministratorsPage-CFkNHBlC.js → queryapigate-0.26.0/queryapigate/console_dist/assets/AdministratorsPage-DoeYjW1i.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/AgentsPage-DcMW4p3t.js → queryapigate-0.26.0/queryapigate/console_dist/assets/AgentsPage-DerF6ZBb.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/AlertsPage-ByabJHGV.js → queryapigate-0.26.0/queryapigate/console_dist/assets/AlertsPage-BWROcwNn.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/ApiKeysPage-CjQHbGhY.js → queryapigate-0.26.0/queryapigate/console_dist/assets/ApiKeysPage-DxEySu_Z.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/ApprovalsPage-B-bvb0ZN.js → queryapigate-0.26.0/queryapigate/console_dist/assets/ApprovalsPage-DSKisl9D.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/AuditLogPage-CnJ_fEAi.js → queryapigate-0.26.0/queryapigate/console_dist/assets/AuditLogPage-Etti--Y7.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/CachingPage-CI3B44tv.js → queryapigate-0.26.0/queryapigate/console_dist/assets/CachingPage-BZ61rPfT.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/CodeBox-B8o2ndfg.js → queryapigate-0.26.0/queryapigate/console_dist/assets/CodeBox-GGv4fKoR.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/ConnectionsPage-Dos9vQ7j.js → queryapigate-0.26.0/queryapigate/console_dist/assets/ConnectionsPage-CFCKdJ9-.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/DesignerPage-CzxotVnW.js → queryapigate-0.26.0/queryapigate/console_dist/assets/DesignerPage-DL1ysNb9.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/DestinationsPage-BuZX_J9v.js → queryapigate-0.26.0/queryapigate/console_dist/assets/DestinationsPage-Du2l56ln.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/Experimental-CpLUXYZw.js → queryapigate-0.26.0/queryapigate/console_dist/assets/Experimental-BB0g6F3s.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/ExportsPage-CIkC1su0.js → queryapigate-0.26.0/queryapigate/console_dist/assets/ExportsPage-Cs2BpNim.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/HelpPage-MnGrT-gK.js → queryapigate-0.26.0/queryapigate/console_dist/assets/HelpPage-CUTTBoH6.js +2 -2
- queryapigate-0.25.0/queryapigate/console_dist/assets/HomePage-_Nny67B8.js → queryapigate-0.26.0/queryapigate/console_dist/assets/HomePage-DtOzlurj.js +2 -2
- queryapigate-0.25.0/queryapigate/console_dist/assets/MetricsPage-CEreAU6d.js → queryapigate-0.26.0/queryapigate/console_dist/assets/MetricsPage-o3kSXmOq.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/RepositoryPage-BK0LWmBR.js → queryapigate-0.26.0/queryapigate/console_dist/assets/RepositoryPage-Cooj3jby.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/Results-D4jR6dpv.js → queryapigate-0.26.0/queryapigate/console_dist/assets/Results-xBStmWdp.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/RolesPage-BFY70eIi.js → queryapigate-0.26.0/queryapigate/console_dist/assets/RolesPage-CcR_ttpB.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/SchemaBrowser-svcNt7n5.js → queryapigate-0.26.0/queryapigate/console_dist/assets/SchemaBrowser-DykbAObL.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/SettingsPage-QtjuqElj.js → queryapigate-0.26.0/queryapigate/console_dist/assets/SettingsPage-mw196Ecu.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/StatTile-6dYBOfNi.js → queryapigate-0.26.0/queryapigate/console_dist/assets/StatTile-5XA5PKWp.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/Time-BHophAO8.js → queryapigate-0.26.0/queryapigate/console_dist/assets/Time-zMnS1kSm.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/forms-C7JK7oo_.js → queryapigate-0.26.0/queryapigate/console_dist/assets/forms-C5L_fKDm.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/forms-C8CfTxrI.js → queryapigate-0.26.0/queryapigate/console_dist/assets/forms-Cl7JC3vt.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/icons-BVIbiOCh.js → queryapigate-0.26.0/queryapigate/console_dist/assets/icons-B_wbfO4Z.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/index-DRaDyzll.js → queryapigate-0.26.0/queryapigate/console_dist/assets/index-D0zbyWWM.js +2 -2
- queryapigate-0.25.0/queryapigate/console_dist/assets/maskRules-BcPINA1z.js → queryapigate-0.26.0/queryapigate/console_dist/assets/maskRules-Cw-l_UG8.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/reach-BxceRBAh.js → queryapigate-0.26.0/queryapigate/console_dist/assets/reach-DFqBXYli.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/shared-CtL1a_C1.js → queryapigate-0.26.0/queryapigate/console_dist/assets/shared-DygTbsGs.js +1 -1
- queryapigate-0.25.0/queryapigate/console_dist/assets/useMutation-yxhEJUOh.js → queryapigate-0.26.0/queryapigate/console_dist/assets/useMutation-BCy3Br67.js +1 -1
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/console_dist/index.html +1 -1
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/examples.py +9 -6
- {queryapigate-0.25.0 → queryapigate-0.26.0/queryapigate.egg-info}/PKG-INFO +1 -1
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate.egg-info/SOURCES.txt +33 -32
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_examples.py +10 -0
- queryapigate-0.26.0/tests/test_helm.py +192 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/LICENSE +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/README.md +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/pyproject.toml +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/__main__.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/adminroles.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/admins.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/agentwrites.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/alerts.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/apikeys.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/app.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/auditchain.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/auditstream.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/broadcast.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/bundle.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/cache.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/cli.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/collection_admin.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/config.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/console.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/console_dist/assets/index-D9oV4vVP.css +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/console_dist/assets/state-O13cl7cO.js +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/console_dist/favicon.svg +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/cors.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/databases.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/db.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/definitions.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/deprecations.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/descriptions.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/destinations.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/duckfiles.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/engine.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/errors.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/events.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/experimental.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/exporting.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/exports.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/formats.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/governance.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/history.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/instances.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/jwtauth.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/lib/h2-2.2.224.jar +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/limits.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/logging_setup.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/masking.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/mcp_server.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/metrics.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/mongotools.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/oidc.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/openapi.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/params.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/parquet.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/pool.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/postman.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/quotas.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/ratelimit.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/rediscache.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/rekey.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/runners.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/schema.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/secheaders.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/services/__init__.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/services/access.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/services/administrators.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/services/audit.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/services/collections.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/services/connections.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/services/descriptions.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/services/destinations.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/services/exports.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/services/mcp.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/services/queries.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/sqlflow.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/sqltools.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/sso.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/store.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/tableguard.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/tracing.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/usage.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/v1.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate/v1_spec.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate.egg-info/dependency_links.txt +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate.egg-info/entry_points.txt +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate.egg-info/requires.txt +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/queryapigate.egg-info/top_level.txt +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/setup.cfg +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_adhoc_history.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_adminroles.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_admins.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_agent_writes.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_alerts.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_api_administrators.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_api_keys.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_api_v1.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_audit_chain.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_audit_log.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_audit_stream.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_backup_restore.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_broadcast.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_cli.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_collection_bundle.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_collection_postman.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_collections.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_connection_encryption.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_console.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_cors_and_rate_limit.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_databases.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_db.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_dependency_floors.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_deprecations.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_descriptions.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_destinations.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_duckdb.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_duckdb_files.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_errors.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_events.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_events_server.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_experimental.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_export_security.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_exporting.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_exports.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_history.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_howto.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_instances.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_integration.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_jdbc.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_jwt.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_key_rotation.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_limits.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_masking.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_masking_requests.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_mcp.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_mcp_keycloak.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_mcp_oauth.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_mongotools.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_multinode.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_observability.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_oidc.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_params.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_parquet.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_pool.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_publishing.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_query_window.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_queryapigate.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_quotas.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_rekey.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_release_check.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_response_cache.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_roles.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_rolling_upgrade.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_security_headers.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_settings.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_shared_rate_limit.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_sql_guard_fuzz.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_sql_guard_writes.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_sqlflow.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_sso.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_sso_keycloak.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_tableguard.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_tracing.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_upgrades.py +0 -0
- {queryapigate-0.25.0 → queryapigate-0.26.0}/tests/test_usage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: queryapigate
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.26.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{B as e,E as t,F as n,I as r,P as i,Q as a,R as o,S as s,V as c,X as l,Z as u,m as d,o as f,s as p,u as m}from"./index-
|
|
3
|
-
`)),staleTime:1/0});if(f.isPending)return(0,w.jsx)(i,{});if(!p||!m)return(0,w.jsx)(`div`,{className:`hint`,children:`Could not load the SQL.`});let y=_.data?.formatted||h,b=p.created_at,x=p.last_used_at;return(0,w.jsxs)(`div`,{className:`form`,children:[m.description?(0,w.jsx)(`p`,{className:`d-desc`,children:m.description}):null,(0,w.jsxs)(`dl`,{className:`meta`,children:[(0,w.jsx)(T,{term:`connection`,children:m.connection_name||`—`}),(0,w.jsx)(T,{term:`database`,children:a?(0,w.jsx)(`span`,{className:`tag`,children:a}):(0,w.jsx)(`span`,{className:`dim`,children:`—`})}),(0,w.jsx)(T,{term:`collection`,children:p.collection||`—`}),(0,w.jsx)(T,{term:`status`,children:m.status||`—`}),(0,w.jsx)(T,{term:`author`,children:m.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:m.last_modified_at||m.created_at})}),(0,w.jsx)(T,{term:`last used`,children:(0,w.jsx)(g,{value:x,fallback:`never`})}),m.tags.length?(0,w.jsx)(T,{term:`tags`,children:m.tags.join(`, `)}):null]}),(0,w.jsx)(`p`,{className:`sub-h`,style:{marginTop:6},children:`SQL · v${m.version}${p.versions.length>1?` (latest)`:``}`}),(0,w.jsx)(`div`,{children:_.isFetching?(0,w.jsx)(i,{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:()=>n(y,d),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:[...p.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:u,children:`Close`}),(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:()=>{u(),l(`/queries/${encodeURIComponent(e)}`)},children:`Open in API Repository`})]})]})}var D=(0,C.lazy)(()=>e(()=>import(`./forms-C8CfTxrI.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=o(),{openDrawer:t}=r(),n=f(),a=p(),s=d(),c=m(),[l,u]=(0,C.useState)(x),g=e=>{let t={...l,...e};_(t),u(t)},v=c.data??[],S=e=>v.find(t=>t.name===e)?.db??null,T=y(l.conn||void 0),O=T.data?.tables??[],k=b(l.conn||void 0,S(l.conn),l.table&&!l.tableMatches?[l.table]:[]),A=l.table?l.tableMatches??(k.data?k.data[l.table]??[]:null):null,j=n.data??[],M=[...new Set(j.map(e=>S(e.connection_name)).filter(e=>!!e))].sort(),P=a.data??{},F=s.data??{},I=Object.keys(P).sort(),L=Object.keys(F).sort(),R,z=!0;l.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)(i,{});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=l.q.trim().toLowerCase(),r=[...j].sort((e,t)=>(e.collection||``)+e.name<(t.collection||``)+t.name?-1:1),a=e=>e.toLowerCase().includes(n),o=n?r.filter(e=>a(e.name)||e.collection&&a(e.collection)):r,s=n?I.filter(a):I,c=n?L.filter(a):L,u=o.length||s.length||c.length,d=o.length?o:u?r:[],f=s.length?s:u?I:[],p=c.length?c:u?L:[];if(l.conn&&(d=d.filter(e=>e.connection_name===l.conn)),l.table&&A===null)V=(0,w.jsx)(i,{text:`Checking which queries reference ${l.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}));l.db&&(n=n.filter(e=>e.dbType===l.db)),l.reach===`reachable`?n=n.filter(e=>e.reach>0):l.reach===`unreachable`&&(n=n.filter(e=>e.reach===0));let r=l.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:l,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 r=n?`Nothing matches “${n}”.`:l.table?`“${l.table}” on ${l.conn} is not exposed by any saved query yet.`:l.conn?`No saved query uses “${l.conn}” yet.`:`Nothing matches these filters.`;V=(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`span`,{children:r}),!n&&l.conn?(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:()=>{let n=S(l.conn)===`mongo`,r=l.table?n?JSON.stringify({collection:l.table,filter:{}},null,2):`SELECT * FROM `+l.table:void 0;e(`/queries`),t({title:`New API`,kicker:`POST /api/v1/queries`,content:(0,w.jsx)(C.Suspense,{fallback:(0,w.jsx)(i,{}),children:(0,w.jsx)(D,{starter:{connection:l.conn,sql:r}})})})},children:`New API`+(l.table?` on `+l.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===l.conn)?l.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===l.table)?l.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(l.db)?l.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:l.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:l.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-Cl7JC3vt.js","assets/index-D0zbyWWM.js","assets/index-D9oV4vVP.css","assets/SchemaBrowser-DykbAObL.js","assets/useMutation-BCy3Br67.js","assets/reach-DFqBXYli.js","assets/CodeBox-GGv4fKoR.js"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{B as e,E as t,F as n,I as r,P as i,Q as a,R as o,S as s,V as c,X as l,Z as u,m as d,o as f,s as p,u as m}from"./index-D0zbyWWM.js";import{a as h}from"./reach-DFqBXYli.js";import{t as g}from"./Time-zMnS1kSm.js";import{f as _,i as v,n as y,r as b,u as x}from"./SchemaBrowser-DykbAObL.js";import{t as S}from"./CodeBox-GGv4fKoR.js";var C=a(u(),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:a}){let l=o(),{closeDrawer:u,toast:d}=r(),f=c({queryKey:[`queries`,`detail`,e,`info`],queryFn:async()=>t(await s.GET(`/api/v1/queries/{name}`,{params:{path:{name:e}}})),retry:!1}),p=f.data,m=p?.versions[p.versions.length-1],h=m?.query_type===`mongo`?JSON.stringify(m.mongo??{},null,2):m?.sql??``,_=c({queryKey:[`queries`,`flow`,e,m?.version],queryFn:()=>v(e,m.version),enabled:!(!m||m.query_type!==`sql`||h.includes(`
|
|
3
|
+
`)),staleTime:1/0});if(f.isPending)return(0,w.jsx)(i,{});if(!p||!m)return(0,w.jsx)(`div`,{className:`hint`,children:`Could not load the SQL.`});let y=_.data?.formatted||h,b=p.created_at,x=p.last_used_at;return(0,w.jsxs)(`div`,{className:`form`,children:[m.description?(0,w.jsx)(`p`,{className:`d-desc`,children:m.description}):null,(0,w.jsxs)(`dl`,{className:`meta`,children:[(0,w.jsx)(T,{term:`connection`,children:m.connection_name||`—`}),(0,w.jsx)(T,{term:`database`,children:a?(0,w.jsx)(`span`,{className:`tag`,children:a}):(0,w.jsx)(`span`,{className:`dim`,children:`—`})}),(0,w.jsx)(T,{term:`collection`,children:p.collection||`—`}),(0,w.jsx)(T,{term:`status`,children:m.status||`—`}),(0,w.jsx)(T,{term:`author`,children:m.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:m.last_modified_at||m.created_at})}),(0,w.jsx)(T,{term:`last used`,children:(0,w.jsx)(g,{value:x,fallback:`never`})}),m.tags.length?(0,w.jsx)(T,{term:`tags`,children:m.tags.join(`, `)}):null]}),(0,w.jsx)(`p`,{className:`sub-h`,style:{marginTop:6},children:`SQL · v${m.version}${p.versions.length>1?` (latest)`:``}`}),(0,w.jsx)(`div`,{children:_.isFetching?(0,w.jsx)(i,{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:()=>n(y,d),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:[...p.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:u,children:`Close`}),(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:()=>{u(),l(`/queries/${encodeURIComponent(e)}`)},children:`Open in API Repository`})]})]})}var D=(0,C.lazy)(()=>e(()=>import(`./forms-Cl7JC3vt.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=o(),{openDrawer:t}=r(),n=f(),a=p(),s=d(),c=m(),[l,u]=(0,C.useState)(x),g=e=>{let t={...l,...e};_(t),u(t)},v=c.data??[],S=e=>v.find(t=>t.name===e)?.db??null,T=y(l.conn||void 0),O=T.data?.tables??[],k=b(l.conn||void 0,S(l.conn),l.table&&!l.tableMatches?[l.table]:[]),A=l.table?l.tableMatches??(k.data?k.data[l.table]??[]:null):null,j=n.data??[],M=[...new Set(j.map(e=>S(e.connection_name)).filter(e=>!!e))].sort(),P=a.data??{},F=s.data??{},I=Object.keys(P).sort(),L=Object.keys(F).sort(),R,z=!0;l.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)(i,{});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=l.q.trim().toLowerCase(),r=[...j].sort((e,t)=>(e.collection||``)+e.name<(t.collection||``)+t.name?-1:1),a=e=>e.toLowerCase().includes(n),o=n?r.filter(e=>a(e.name)||e.collection&&a(e.collection)):r,s=n?I.filter(a):I,c=n?L.filter(a):L,u=o.length||s.length||c.length,d=o.length?o:u?r:[],f=s.length?s:u?I:[],p=c.length?c:u?L:[];if(l.conn&&(d=d.filter(e=>e.connection_name===l.conn)),l.table&&A===null)V=(0,w.jsx)(i,{text:`Checking which queries reference ${l.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}));l.db&&(n=n.filter(e=>e.dbType===l.db)),l.reach===`reachable`?n=n.filter(e=>e.reach>0):l.reach===`unreachable`&&(n=n.filter(e=>e.reach===0));let r=l.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:l,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 r=n?`Nothing matches “${n}”.`:l.table?`“${l.table}” on ${l.conn} is not exposed by any saved query yet.`:l.conn?`No saved query uses “${l.conn}” yet.`:`Nothing matches these filters.`;V=(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`span`,{children:r}),!n&&l.conn?(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:()=>{let n=S(l.conn)===`mongo`,r=l.table?n?JSON.stringify({collection:l.table,filter:{}},null,2):`SELECT * FROM `+l.table:void 0;e(`/queries`),t({title:`New API`,kicker:`POST /api/v1/queries`,content:(0,w.jsx)(C.Suspense,{fallback:(0,w.jsx)(i,{}),children:(0,w.jsx)(D,{starter:{connection:l.conn,sql:r}})})})},children:`New API`+(l.table?` on `+l.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===l.conn)?l.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===l.table)?l.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(l.db)?l.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:l.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:l.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-yxhEJUOh.js";import{D as t,E as n,I as r,M as i,N as a,O as o,P as s,Q as c,S as l,V as u,X as d,Y as f,Z as p,c as m,d as h,k as g,p as _,x as v}from"./index-DRaDyzll.js";import{t as y}from"./Time-BHophAO8.js";import{t as b}from"./forms-C7JK7oo_.js";import{n as x,t as S}from"./icons-BVIbiOCh.js";var C=c(p(),1),w=d(),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}=r();return t=>t instanceof v?e(t.message,{status:t.status}):e(t.message)}function j(e){let t=h(`admins.read`,`other administrators`);return u({queryKey:[`administrators`],queryFn:async()=>(await t.check(),n(await l.GET(`/api/v1/administrators`)).items),enabled:e,retry:!1})}function M(e){return u({queryKey:[`administrators`,e,`tokens`],queryFn:async()=>n(await l.GET(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:e}}})).items,retry:!1})}function N(){let e=_(),t=m(),n=!!e.data&&t(`admins.read`),i=j(n),{openDrawer:a}=r(),o=()=>a({title:`New administrator`,content:(0,w.jsx)(I,{})}),c;return c=e.isPending?(0,w.jsx)(s,{}):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?i.isPending?(0,w.jsx)(s,{text:`Loading administrators…`}):i.isError?(0,w.jsx)(`div`,{className:`hint`,children:i.error.message}):i.data.length?(0,w.jsx)(P,{items:i.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:n}){let{openDrawer:i,toast:a}=r(),o=A(),s=f(),c=async e=>{if(window.confirm(`Remove administrator '${e}'? Every token of theirs stops working immediately.`))try{t(await l.DELETE(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),a(`Removed `+e),s.invalidateQueries({queryKey:[`administrators`]})}catch(e){o(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===n&&(0,w.jsx)(`span`,{className:`tag`,children:`you`}),e.sso?.status===`linked`&&(0,w.jsx)(`span`,{className:`tag`,title:`Signs in through ${e.sso.issuer??`SSO`}; role from their groups`,children:`SSO`}),e.sso?.status===`pending`&&(0,w.jsx)(`span`,{className:`tag`,title:`Linked at their next SSO sign-in, by their verified email`,children:`SSO pending`})]})}),(0,w.jsx)(`td`,{children:E(e.role)}),(0,w.jsx)(`td`,{className:e.email?void 0:`dim`,children:e.email??`—`}),(0,w.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,w.jsx)(y,{value:e.last_seen_at,fallback:`never`})}),(0,w.jsx)(`td`,{className:`mono`,children:e.tokens}),(0,w.jsx)(`td`,{children:(0,w.jsxs)(`div`,{className:`actions`,children:[(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>i({title:`Admin tokens`,kicker:e.name,wide:!0,content:(0,w.jsx)(R,{name:e.name})}),children:`Tokens`}),(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm icon`,title:`Edit ${e.name}`,"aria-label":`Edit`,onClick:()=>i({title:`Edit administrator`,kicker:e.name,content:(0,w.jsx)(I,{name:e.name})}),children:(0,w.jsx)(S,{})}),(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm icon danger`,title:`Remove ${e.name}`,"aria-label":`Remove`,onClick:()=>void 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=u({queryKey:[`administrators`,`detail`,e],queryFn:async()=>o(await l.GET(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),enabled:!!e,retry:!1,gcTime:0});return e&&t.isPending?(0,w.jsx)(s,{}):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:o,etag:s}){let c=!!t,{closeDrawer:u,openDrawer:d,showError:p,toast:m}=r(),h=A(),_=f(),[v,y]=(0,C.useState)(t??``),[x,S]=(0,C.useState)(o?.role??`developer`),[E,j]=(0,C.useState)(o?.email??``),[M,N]=(0,C.useState)(!o||o.active),P=g(),F=o?.sso?.status===`linked`,[I,L]=(0,C.useState)(o?.sso?.status??`none`),R=e({mutationFn:async()=>{let e={role:x,email:E.trim()||null};if(c)return n(await l.PATCH(`/api/v1/administrators/{name}`,{params:{path:{name:t},header:s?{"If-Match":s}:{}},body:{...F?{email:e.email}:e,active:M,...I===(o?.sso?.status??`none`)?{}:{sso:I===`pending`?`pending`:null}}})),null;let r=n(await l.POST(`/api/v1/administrators`,{body:{name:v.trim(),...e}}));return n(await l.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:r.name}},body:{label:`first token`,expires_at:O(D)}}))},onSuccess:e=>{if(p(``),_.invalidateQueries({queryKey:[`administrators`]}),!e){u(),m(`Updated `+t);return}d({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:h});return(0,w.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{if(e.preventDefault(),!c&&!v.trim()){p(`A name is required.`,{errors:{name:`This field is required`}});return}R.mutate()},children:[(0,w.jsx)(i,{id:`a-name`,label:`Name`,hint:c?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:c,autoFocus:!c,onChange:e=>y(e.target.value)})}),(0,w.jsx)(i,{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)(i,{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)})}),c&&(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`})]}),c&&P.data?.sso&&(0,w.jsx)(i,{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`})]})}),!c&&(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)(a,{onCancel:u,children:(0,w.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:R.isPending,children:c?`Save`:`Create`})})]})}function R({name:a}){let o=M(a),c=f(),{toast:u}=r(),d=A(),[p,m]=(0,C.useState)(``),[h,g]=(0,C.useState)(O(D)),[_,v]=(0,C.useState)(null),x=()=>void c.invalidateQueries({queryKey:[`administrators`]}),S=e({mutationFn:async()=>n(await l.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:a}},body:{label:p.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{t(await l.DELETE(`/api/v1/administrators/{name}/tokens/{token_id}`,{params:{path:{name:a,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:[o.isPending?(0,w.jsx)(s,{text:`Loading tokens…`}):o.isError?(0,w.jsx)(`div`,{className:`hint`,children:o.error.message}):o.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:o.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)(i,{id:`t-label`,label:`New token: label`,hint:`What it's for - laptop, ci.`,children:(0,w.jsx)(`input`,{id:`t-label`,value:p,autoComplete:`off`,onChange:e=>m(e.target.value)})}),(0,w.jsx)(i,{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-BCy3Br67.js";import{D as t,E as n,I as r,M as i,N as a,O as o,P as s,Q as c,S as l,V as u,X as d,Y as f,Z as p,c as m,d as h,k as g,p as _,x as v}from"./index-D0zbyWWM.js";import{t as y}from"./Time-zMnS1kSm.js";import{t as b}from"./forms-C5L_fKDm.js";import{n as x,t as S}from"./icons-B_wbfO4Z.js";var C=c(p(),1),w=d(),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}=r();return t=>t instanceof v?e(t.message,{status:t.status}):e(t.message)}function j(e){let t=h(`admins.read`,`other administrators`);return u({queryKey:[`administrators`],queryFn:async()=>(await t.check(),n(await l.GET(`/api/v1/administrators`)).items),enabled:e,retry:!1})}function M(e){return u({queryKey:[`administrators`,e,`tokens`],queryFn:async()=>n(await l.GET(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:e}}})).items,retry:!1})}function N(){let e=_(),t=m(),n=!!e.data&&t(`admins.read`),i=j(n),{openDrawer:a}=r(),o=()=>a({title:`New administrator`,content:(0,w.jsx)(I,{})}),c;return c=e.isPending?(0,w.jsx)(s,{}):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?i.isPending?(0,w.jsx)(s,{text:`Loading administrators…`}):i.isError?(0,w.jsx)(`div`,{className:`hint`,children:i.error.message}):i.data.length?(0,w.jsx)(P,{items:i.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:n}){let{openDrawer:i,toast:a}=r(),o=A(),s=f(),c=async e=>{if(window.confirm(`Remove administrator '${e}'? Every token of theirs stops working immediately.`))try{t(await l.DELETE(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),a(`Removed `+e),s.invalidateQueries({queryKey:[`administrators`]})}catch(e){o(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===n&&(0,w.jsx)(`span`,{className:`tag`,children:`you`}),e.sso?.status===`linked`&&(0,w.jsx)(`span`,{className:`tag`,title:`Signs in through ${e.sso.issuer??`SSO`}; role from their groups`,children:`SSO`}),e.sso?.status===`pending`&&(0,w.jsx)(`span`,{className:`tag`,title:`Linked at their next SSO sign-in, by their verified email`,children:`SSO pending`})]})}),(0,w.jsx)(`td`,{children:E(e.role)}),(0,w.jsx)(`td`,{className:e.email?void 0:`dim`,children:e.email??`—`}),(0,w.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,w.jsx)(y,{value:e.last_seen_at,fallback:`never`})}),(0,w.jsx)(`td`,{className:`mono`,children:e.tokens}),(0,w.jsx)(`td`,{children:(0,w.jsxs)(`div`,{className:`actions`,children:[(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>i({title:`Admin tokens`,kicker:e.name,wide:!0,content:(0,w.jsx)(R,{name:e.name})}),children:`Tokens`}),(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm icon`,title:`Edit ${e.name}`,"aria-label":`Edit`,onClick:()=>i({title:`Edit administrator`,kicker:e.name,content:(0,w.jsx)(I,{name:e.name})}),children:(0,w.jsx)(S,{})}),(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm icon danger`,title:`Remove ${e.name}`,"aria-label":`Remove`,onClick:()=>void 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=u({queryKey:[`administrators`,`detail`,e],queryFn:async()=>o(await l.GET(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),enabled:!!e,retry:!1,gcTime:0});return e&&t.isPending?(0,w.jsx)(s,{}):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:o,etag:s}){let c=!!t,{closeDrawer:u,openDrawer:d,showError:p,toast:m}=r(),h=A(),_=f(),[v,y]=(0,C.useState)(t??``),[x,S]=(0,C.useState)(o?.role??`developer`),[E,j]=(0,C.useState)(o?.email??``),[M,N]=(0,C.useState)(!o||o.active),P=g(),F=o?.sso?.status===`linked`,[I,L]=(0,C.useState)(o?.sso?.status??`none`),R=e({mutationFn:async()=>{let e={role:x,email:E.trim()||null};if(c)return n(await l.PATCH(`/api/v1/administrators/{name}`,{params:{path:{name:t},header:s?{"If-Match":s}:{}},body:{...F?{email:e.email}:e,active:M,...I===(o?.sso?.status??`none`)?{}:{sso:I===`pending`?`pending`:null}}})),null;let r=n(await l.POST(`/api/v1/administrators`,{body:{name:v.trim(),...e}}));return n(await l.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:r.name}},body:{label:`first token`,expires_at:O(D)}}))},onSuccess:e=>{if(p(``),_.invalidateQueries({queryKey:[`administrators`]}),!e){u(),m(`Updated `+t);return}d({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:h});return(0,w.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{if(e.preventDefault(),!c&&!v.trim()){p(`A name is required.`,{errors:{name:`This field is required`}});return}R.mutate()},children:[(0,w.jsx)(i,{id:`a-name`,label:`Name`,hint:c?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:c,autoFocus:!c,onChange:e=>y(e.target.value)})}),(0,w.jsx)(i,{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)(i,{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)})}),c&&(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`})]}),c&&P.data?.sso&&(0,w.jsx)(i,{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`})]})}),!c&&(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)(a,{onCancel:u,children:(0,w.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:R.isPending,children:c?`Save`:`Create`})})]})}function R({name:a}){let o=M(a),c=f(),{toast:u}=r(),d=A(),[p,m]=(0,C.useState)(``),[h,g]=(0,C.useState)(O(D)),[_,v]=(0,C.useState)(null),x=()=>void c.invalidateQueries({queryKey:[`administrators`]}),S=e({mutationFn:async()=>n(await l.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:a}},body:{label:p.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{t(await l.DELETE(`/api/v1/administrators/{name}/tokens/{token_id}`,{params:{path:{name:a,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:[o.isPending?(0,w.jsx)(s,{text:`Loading tokens…`}):o.isError?(0,w.jsx)(`div`,{className:`hint`,children:o.error.message}):o.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:o.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)(i,{id:`t-label`,label:`New token: label`,hint:`What it's for - laptop, ci.`,children:(0,w.jsx)(`input`,{id:`t-label`,value:p,autoComplete:`off`,onChange:e=>m(e.target.value)})}),(0,w.jsx)(i,{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{E as e,P as t,Q as n,S as r,V as i,X as a,Z as o}from"./index-
|
|
1
|
+
import{E as e,P as t,Q as n,S as r,V as i,X as a,Z as o}from"./index-D0zbyWWM.js";import{t as s}from"./Time-zMnS1kSm.js";import{t as c}from"./CodeBox-GGv4fKoR.js";import{t as l}from"./Experimental-BB0g6F3s.js";var u=n(o(),1),d=a(),f=[{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}],p=e=>e.toLocaleString();function m({refusals:e}){let t=Object.entries(e);return t.length?(0,d.jsx)(`span`,{style:{display:`inline-flex`,flexWrap:`wrap`,gap:4},children:t.map(([e,t])=>(0,d.jsx)(`span`,{className:`tag`,title:`${t} call${t===1?``:`s`} failed with ${e}`,children:`${e} × ${t}`},e))}):(0,d.jsx)(`span`,{className:`dim`,children:`none`})}function h({writes:e}){let t=Object.entries(e??{}).filter(([,e])=>e).map(([e,t])=>`${t} ${e}`);return(0,d.jsx)(`div`,{className:`hint`,children:t.length?t.join(` · `):`None.`})}function g({agent:e}){let t=Object.entries(e.queries);return(0,d.jsxs)(`div`,{className:`agent-detail`,children:[(0,d.jsx)(`p`,{className:`sub-h`,children:`Writes asked for`}),(0,d.jsx)(h,{writes:e.writes}),(0,d.jsx)(`p`,{className:`sub-h`,style:{marginTop:12},children:`Saved queries`}),t.length?(0,d.jsx)(`div`,{className:`hint`,children:t.map(([e,t])=>`${e} × ${t}`).join(` · `)}):(0,d.jsx)(`div`,{className:`hint`,children:`None - only ad-hoc SQL.`}),(0,d.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,d.jsxs)(`div`,{className:`agent-statement`,children:[(0,d.jsx)(`div`,{className:`hint`,children:`${e.count} call${e.count===1?``:`s`} · ${p(e.rows)} rows`+(e.errors?` · ${e.errors} failed`:``)}),e.sql?(0,d.jsx)(c,{sql:e.sql}):(0,d.jsx)(`span`,{className:`mono dim`,children:`sha256 ${e.sql_sha256}`})]},t)):(0,d.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 _(){let[n,a]=(0,u.useState)(`24h`),[o,c]=(0,u.useState)(null),h=f.find(e=>e.id===n).hours,_=i({queryKey:[`usage`,`agents`,n],queryFn:async()=>e(await r.GET(`/api/v1/usage/agents`,{params:{query:{hours:h}}})),retry:!1}),v=_.data?.items??[],y;y=_.isPending?(0,d.jsx)(t,{text:`Loading agents…`}):_.isError?(0,d.jsxs)(`div`,{className:`empty`,children:[(0,d.jsx)(`strong`,{children:`Couldn’t load agent usage`}),(0,d.jsx)(`span`,{children:_.error.message})]}):v.length?(0,d.jsx)(`div`,{style:{overflowX:`auto`},children:(0,d.jsxs)(`table`,{className:`grid`,children:[(0,d.jsx)(`thead`,{children:(0,d.jsx)(`tr`,{children:[`Agent`,`Calls`,`Rows`,`Failed`,`Masked`,`Refused or failed with`,`Last seen`].map(e=>(0,d.jsx)(`th`,{children:e},e))})}),(0,d.jsx)(`tbody`,{children:v.map(e=>(0,d.jsxs)(u.Fragment,{children:[(0,d.jsxs)(`tr`,{children:[(0,d.jsxs)(`td`,{children:[(0,d.jsx)(`button`,{type:`button`,className:`agent-name`,"aria-expanded":o===e.agent,onClick:()=>c(o===e.agent?null:e.agent),children:e.agent}),(0,d.jsx)(`div`,{className:`dim`,style:{fontSize:12},children:e.connections.join(`, `)})]}),(0,d.jsx)(`td`,{className:`num`,children:p(e.calls)}),(0,d.jsx)(`td`,{className:`num`,children:p(e.rows)}),(0,d.jsx)(`td`,{className:`num`,children:p(e.errors)}),(0,d.jsx)(`td`,{className:`num`,children:p(e.masked_calls)}),(0,d.jsx)(`td`,{children:(0,d.jsx)(m,{refusals:e.refusals})}),(0,d.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,d.jsx)(s,{value:e.last_seen,fallback:``})})]}),o===e.agent&&(0,d.jsx)(`tr`,{children:(0,d.jsx)(`td`,{colSpan:7,children:(0,d.jsx)(g,{agent:e})})})]},e.agent))})]})}):(0,d.jsxs)(`div`,{className:`empty`,children:[(0,d.jsx)(`strong`,{children:`No agent called the MCP server in this window`}),(0,d.jsx)(`span`,{children:`Runs over MCP - saved-query tools and execute_sql - appear here, per API key or signed-in user.`})]});let b=[];return _.data&&_.data.sample_rate<1&&b.push(`Run history keeps ${Math.round(_.data.sample_rate*100)}% of successful runs, so successful calls and rows are a sample; failures are all here.`),_.data?.truncated&&b.push(`More runs than one summary reads (100,000) - narrow the window.`),(0,d.jsxs)(d.Fragment,{children:[(0,d.jsxs)(`div`,{className:`page-head`,children:[(0,d.jsxs)(`div`,{className:`titles`,children:[(0,d.jsxs)(`h1`,{children:[`Agents `,(0,d.jsx)(l,{})]}),(0,d.jsx)(`span`,{className:`sub`,children:`What each AI agent did over MCP - per API key or signed-in user, from run history.`})]}),(0,d.jsx)(`span`,{className:`spacer`}),(0,d.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>void _.refetch(),children:`Refresh`})]}),(0,d.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,d.jsxs)(`div`,{className:`panel-bar`,children:[(0,d.jsx)(`select`,{"aria-label":`Window`,value:n,onChange:e=>a(e.target.value),children:f.map(e=>(0,d.jsx)(`option`,{value:e.id,children:e.label},e.id))}),(0,d.jsx)(`span`,{className:`spacer`}),(0,d.jsx)(`span`,{className:`panel-count`,children:_.data?`${v.length} agent${v.length===1?``:`s`}`:``})]}),b.map(e=>(0,d.jsx)(`div`,{className:`hint`,style:{padding:`6px 12px`},children:e},e)),y]})]})}export{_ as AgentsPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{P as e,Q as t,R as n,X as r,Z as i,b as a,g as o,v as s,y as c,z as l}from"./index-
|
|
1
|
+
import{P as e,Q as t,R as n,X as r,Z as i,b as a,g as o,v as s,y as c,z as l}from"./index-D0zbyWWM.js";import{t as u}from"./Time-zMnS1kSm.js";import{n as d}from"./state-O13cl7cO.js";var f=t(i(),1),p=r(),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=n();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:()=>c(e.id),children:`Restore`}):(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,title:`Hide it in this browser until it clears`,onClick:()=>s(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 t=n(),r=l(),i=o.find(e=>e.id===r.tab),{query:s,active:c,dismissed:d}=a(),[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=s.isPending?(0,p.jsx)(e,{text:`Checking…`}):s.isError?(0,p.jsx)(`div`,{className:`panel`,children:(0,p.jsxs)(`div`,{className:`empty`,children:[(0,p.jsx)(`strong`,{children:`Couldn’t check for alerts`}),(0,p.jsx)(`span`,{children:s.error.message}),(0,p.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>void s.refetch(),children:`Retry`})]})}):(0,p.jsxs)(p.Fragment,{children:[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=(e,n,r)=>(0,p.jsxs)(`button`,{type:`button`,role:`tab`,"aria-selected":(i?.id??`all`)===e,className:`minitab`+((i?.id??`all`)===e?` active`:``),onClick:()=>t(e===`all`?`/alerts`:`/alerts/`+e),children:[n,s.data?(0,p.jsx)(`span`,{className:`n`+(r.length?` on`:``)+(r.some(e=>e.severity===`critical`)?` critical`:``),children:r.length}):null]},e);return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)(`div`,{className:`page-head`,children:[(0,p.jsxs)(`div`,{className:`titles`,children:[(0,p.jsx)(`h1`,{children:`Alerts`}),(0,p.jsxs)(`span`,{className:`sub`,children:[`What needs attention now, checked every minute. Each clears by itself once its cause does.`,s.data?(0,p.jsxs)(p.Fragment,{children:[` Last checked `,(0,p.jsx)(u,{value:s.data.checked_at}),`.`]}):null]})]}),(0,p.jsx)(`span`,{className:`spacer`}),(0,p.jsx)(`button`,{id:`refresh-alerts`,type:`button`,className:`btn`,onClick:()=>void s.refetch(),children:`Check now`})]}),(0,p.jsxs)(`div`,{className:`minitabs alerts-tabs`,role:`tablist`,"aria-label":`Checks`,children:[C(`all`,`All`,x()),o.map(e=>C(e.id,e.label,x(e.kinds)))]}),S]})}export{y as AlertsPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{P as e,X as t,c as n,l as r,s as i}from"./index-
|
|
1
|
+
import{P as e,X as t,c as n,l as r,s as i}from"./index-D0zbyWWM.js";import{t as a}from"./reach-DFqBXYli.js";import{t as o}from"./Time-zMnS1kSm.js";import{i as s,n as c,o as l}from"./forms-C5L_fKDm.js";import{t as u}from"./icons-B_wbfO4Z.js";var d=t();function f(e,t){let n=new Date(Date.now()+6048e5).toISOString().slice(0,10);return!!(e&&e>=t&&e<=n)}function p({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,d.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,d.jsxs)(`span`,{className:`dim`,children:[n.used.toLocaleString(),` / `,n.limit.toLocaleString(),` `,n.what,` `,i]}),(0,d.jsx)(`div`,{className:`quota-bar${r>=1?` full`:r>=.8?` near`:``}`,children:(0,d.jsx)(`span`,{style:{width:`${Math.round(r*100)}%`}})})]})}function m(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 h(){let t=n(),h=i(),g=r(),_=c(),v=s(),y=Object.keys(h.data??{}).sort(),b=new Date().toISOString().slice(0,10),x;return x=h.isPending?(0,d.jsx)(e,{text:`Loading API keys…`}):h.isError?(0,d.jsxs)(`div`,{className:`empty`,children:[(0,d.jsx)(`strong`,{children:`Couldn’t load API keys`}),(0,d.jsx)(`span`,{children:h.error.message}),(0,d.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>h.refetch(),children:`Retry`})]}):y.length?(0,d.jsx)(`div`,{style:{overflowX:`auto`},children:(0,d.jsxs)(`table`,{className:`grid`,children:[(0,d.jsx)(`thead`,{children:(0,d.jsx)(`tr`,{children:[`Name`,`Scope`,`Access`,`Rate limit`,`IPs`,`Expires`,`Last used`,`Usage`,``].map((e,t)=>(0,d.jsx)(`th`,{children:e},t))})}),(0,d.jsx)(`tbody`,{children:y.map(e=>{let n=h.data[e],r=!!(n.expires_at&&n.expires_at<b),i=!r&&f(n.expires_at,b),s=n.allowed_ips??[],c=m(n.usage);return(0,d.jsxs)(`tr`,{"data-name":e,children:[(0,d.jsx)(`td`,{children:(0,d.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,d.jsx)(`span`,{className:`dot ${n.active&&!r?`ok`:`off`}`,title:n.active?r?`Expired`:`Active`:`Revoked`}),(0,d.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}),n.previous_expires_at?(0,d.jsx)(`span`,{className:`tag`,title:`Rotated: the old secret works until then too`,children:`rotating`}):null,n.mask?.length?(0,d.jsx)(`span`,{className:`tag`,title:n.mask.map(e=>`${e.column}: ${e.action}`).join(`, `),children:`masked`}):null]})}),(0,d.jsx)(`td`,{children:(0,d.jsx)(l,{entry:n,collections:g.data?.collections})}),(0,d.jsx)(a,{entry:n}),(0,d.jsx)(`td`,{className:n.rate_limit?`mono`:`mono dim`,style:{whiteSpace:`nowrap`},children:n.rate_limit||`server default`}),(0,d.jsx)(`td`,{className:`dim`,children:s.length?(0,d.jsx)(`span`,{title:s.join(`, `),children:s.length+(s.length===1?` IP`:` IPs`)}):(0,d.jsx)(`span`,{className:`dim`,children:`any`})}),(0,d.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:n.expires_at?(0,d.jsx)(`span`,{style:r?{color:`var(--danger)`}:i?{color:`var(--warn)`}:void 0,children:n.expires_at+(r?` (expired)`:i?` (expires soon)`:``)}):(0,d.jsx)(`span`,{className:`dim`,children:`never`})}),(0,d.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,d.jsx)(o,{value:n.last_used_at,fallback:`never`})}),n.usage_this_period?(0,d.jsxs)(`td`,{style:{whiteSpace:`nowrap`},children:[c,(0,d.jsx)(p,{usage:n.usage_this_period})]}):c?(0,d.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,d.jsx)(`td`,{className:`dim`,style:{whiteSpace:`nowrap`},children:`No activity yet`}),(0,d.jsx)(`td`,{children:(0,d.jsxs)(`div`,{className:`actions`,children:[(0,d.jsx)(`button`,{hidden:!t(`access.write`),type:`button`,className:`btn ghost sm icon`,title:`Edit ${e}`,"aria-label":`Edit`,onClick:()=>_.editKey(e),children:(0,d.jsx)(u,{})}),(0,d.jsx)(`button`,{hidden:!t(`access.write`),type:`button`,className:`btn ghost sm danger`,onClick:()=>void v(e),children:`Revoke`})]})})]},e)})})]})}):(0,d.jsxs)(`div`,{className:`empty`,children:[(0,d.jsx)(`strong`,{children:`No scoped API keys yet`}),(0,d.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,d.jsx)(`button`,{hidden:!t(`access.write`),type:`button`,className:`btn primary`,onClick:()=>_.newKey(),children:`New API key`})]}),(0,d.jsxs)(d.Fragment,{children:[(0,d.jsxs)(`div`,{className:`page-head`,children:[(0,d.jsxs)(`div`,{className:`titles`,children:[(0,d.jsx)(`h1`,{children:`API keys`}),(0,d.jsx)(`span`,{className:`sub`,id:`apikeys-sub`,children:(y.length?y.length+(y.length===1?` key. `:` keys. `):``)+`Scope each one to connections, collections or roles.`})]}),(0,d.jsx)(`span`,{className:`spacer`}),(0,d.jsx)(`button`,{hidden:!t(`access.write`),id:`new-apikey`,type:`button`,className:`btn primary`,onClick:()=>_.newKey(),children:`New API key`})]}),(0,d.jsx)(`div`,{id:`apikeys-table`,className:`panel`,children:x})]})}export{h as ApiKeysPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./useMutation-
|
|
1
|
+
import{t as e}from"./useMutation-BCy3Br67.js";import{E as t,I as n,M as r,P as i,Q as a,R as o,S as s,V as c,X as l,Y as u,Z as d,d as f,x as p,z as m}from"./index-D0zbyWWM.js";import{t as h}from"./Time-zMnS1kSm.js";import{t as g}from"./CodeBox-GGv4fKoR.js";import{t as _}from"./Experimental-BB0g6F3s.js";var v=a(d(),1),y=l(),b={pending:`Waiting`,approved:`Approved - not run yet`,running:`Running`,done:`Done`,failed:`Failed`,rejected:`Rejected`,expired:`Expired`};function x(e){let n=f(`writes.approve`,`write requests`);return c({queryKey:[`write-requests`,e||`all`],queryFn:async()=>(await n.check(),t(await s.GET(`/api/v1/write-requests`,{params:{query:e?{status:e}:{}}})).items),enabled:n.ready,retry:!1})}var S=e=>JSON.stringify(e);function C({args:e}){let t=Object.entries(e);return t.length?(0,y.jsx)(`table`,{className:`grid approval-args`,children:(0,y.jsx)(`tbody`,{children:t.map(([e,t])=>(0,y.jsxs)(`tr`,{children:[(0,y.jsx)(`td`,{className:`mono`,children:e}),(0,y.jsx)(`td`,{className:`mono`,children:S(t)})]},e))})}):(0,y.jsx)(`span`,{className:`dim`,children:`none`})}function w({id:a}){let{closeDrawer:o,showError:l,toast:d}=n(),f=u(),[m,_]=(0,v.useState)(``),x=c({queryKey:[`write-requests`,`one`,a],queryFn:async()=>t(await s.GET(`/api/v1/write-requests/{id}`,{params:{path:{id:a}}})),retry:!1}),w=e({mutationFn:async e=>t(e?await s.POST(`/api/v1/write-requests/{id}/approve`,{params:{path:{id:a}}}):await s.POST(`/api/v1/write-requests/{id}/reject`,{params:{path:{id:a}},body:m.trim()?{reason:m.trim()}:{}})),onSuccess:e=>{l(``),f.invalidateQueries({queryKey:[`write-requests`]}),f.invalidateQueries({queryKey:[`alerts`]}),o(),d(e.status===`approved`?`Approved - ${e.caller} may run it now`:`Rejected`)},onError:e=>l(e.message,e instanceof p?{status:e.status}:{})});if(x.isPending)return(0,y.jsx)(i,{});if(x.isError)return(0,y.jsx)(`div`,{className:`hint`,children:x.error.message});let T=x.data,E=T.status===`pending`;return(0,y.jsxs)(`div`,{className:`form`,children:[(0,y.jsxs)(`p`,{className:`hint`,children:[(0,y.jsx)(`strong`,{children:T.caller}),` asked to run `,(0,y.jsx)(`strong`,{children:T.query}),` (version `,T.version,`) on`,` `,(0,y.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,y.jsx)(r,{label:`SQL`,children:(0,y.jsx)(g,{sql:T.sql})}),(0,y.jsx)(r,{label:`With exactly these arguments`,hint:`An approval is for these alone - no other write.`,children:(0,y.jsx)(C,{args:T.arguments})}),(0,y.jsxs)(`div`,{className:`hint`,children:[`Asked `,(0,y.jsx)(h,{value:T.created_at}),` · `,b[T.status]??T.status,E||T.status===`approved`?(0,y.jsxs)(y.Fragment,{children:[` `,`· expires `,(0,y.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,y.jsx)(r,{label:`Result`,children:(0,y.jsx)(`span`,{className:`mono`,children:S(T.result)})}):null,E?(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(r,{id:`approval-reason`,label:`Reason`,hint:`Optional for a rejection - the agent is told.`,children:(0,y.jsx)(`textarea`,{id:`approval-reason`,rows:2,value:m,onChange:e=>_(e.target.value)})}),(0,y.jsxs)(`div`,{className:`form-actions`,children:[(0,y.jsx)(`button`,{type:`button`,className:`btn primary`,disabled:w.isPending,onClick:()=>w.mutate(!0),children:`Approve`}),(0,y.jsx)(`button`,{type:`button`,className:`btn danger`,disabled:w.isPending,onClick:()=>w.mutate(!1),children:`Reject`}),(0,y.jsx)(`button`,{type:`button`,className:`btn`,onClick:o,children:`Close`})]})]}):(0,y.jsx)(`div`,{className:`form-actions`,children:(0,y.jsx)(`button`,{type:`button`,className:`btn`,onClick:o,children:`Close`})})]})}function T(){let{id:e}=m(),t=o(),{openDrawer:r}=n(),[a,s]=(0,v.useState)(`pending`),c=x(a),l=c.data??[],u=e=>r({title:`Approve a write`,kicker:e,wide:!0,content:(0,y.jsx)(w,{id:e})});(0,v.useEffect)(()=>{e&&(u(e),t(`/approvals`,{replace:!0}))},[e]);let d;return d=c.isPending?(0,y.jsx)(i,{text:`Loading write requests…`}):c.isError?(0,y.jsxs)(`div`,{className:`empty`,children:[(0,y.jsx)(`strong`,{children:`Couldn’t load write requests`}),(0,y.jsx)(`span`,{children:c.error.message})]}):l.length?(0,y.jsx)(`div`,{style:{overflowX:`auto`},children:(0,y.jsxs)(`table`,{className:`grid`,children:[(0,y.jsx)(`thead`,{children:(0,y.jsx)(`tr`,{children:[`Asked`,`Agent`,`Tool`,`Arguments`,a===`pending`?`Expires`:`Status`].map(e=>(0,y.jsx)(`th`,{children:e},e))})}),(0,y.jsx)(`tbody`,{children:l.map(e=>(0,y.jsxs)(`tr`,{"data-request":e.id,children:[(0,y.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,y.jsx)(h,{value:e.created_at,fallback:``})}),(0,y.jsx)(`td`,{className:`mono`,children:e.caller}),(0,y.jsx)(`td`,{children:(0,y.jsx)(`button`,{type:`button`,className:`agent-name`,onClick:()=>u(e.id),children:e.query})}),(0,y.jsx)(`td`,{className:`mono dim approval-args-cell`,children:S(e.arguments)}),(0,y.jsx)(`td`,{className:`dim`,style:{whiteSpace:`nowrap`},children:a===`pending`?(0,y.jsx)(h,{value:e.expires_at}):b[e.status]??e.status})]},e.id))})]})}):(0,y.jsxs)(`div`,{className:`empty`,children:[(0,y.jsx)(`strong`,{children:a===`pending`?`No writes are waiting for approval`:`No write requests yet`}),(0,y.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,y.jsxs)(y.Fragment,{children:[(0,y.jsxs)(`div`,{className:`page-head`,children:[(0,y.jsxs)(`div`,{className:`titles`,children:[(0,y.jsxs)(`h1`,{children:[`Approvals `,(0,y.jsx)(_,{})]}),(0,y.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,y.jsx)(`span`,{className:`spacer`}),(0,y.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>void c.refetch(),children:`Refresh`})]}),(0,y.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,y.jsxs)(`div`,{className:`panel-bar`,children:[(0,y.jsxs)(`select`,{"aria-label":`Show`,value:a,onChange:e=>s(e.target.value),children:[(0,y.jsx)(`option`,{value:`pending`,children:`Waiting`}),(0,y.jsx)(`option`,{value:``,children:`All`}),(0,y.jsx)(`option`,{value:`done`,children:`Done`}),(0,y.jsx)(`option`,{value:`rejected`,children:`Rejected`}),(0,y.jsx)(`option`,{value:`expired`,children:`Expired`}),(0,y.jsx)(`option`,{value:`failed`,children:`Failed`})]}),(0,y.jsx)(`span`,{className:`spacer`}),(0,y.jsx)(`span`,{className:`panel-count`,children:c.data?`${l.length} request${l.length===1?``:`s`}`:``})]}),d]})]})}export{T as ApprovalsPage};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{E as e,I as t,P as n,Q as r,S as i,V as a,X as o,Z as s}from"./index-
|
|
1
|
+
import{E as e,I as t,P as n,Q as r,S as i,V as a,X as o,Z as s}from"./index-D0zbyWWM.js";import{t as c}from"./Time-zMnS1kSm.js";var l=r(s(),1),u=o();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:r}=t(),o=a({queryKey:[`audit`],queryFn:async()=>e(await i.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)(n,{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),r(`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{C as e,D as t,E as n,I as r,Q as i,R as a,S as o,V as s,X as c,Y as l,Z as u,h as d,o as f}from"./index-
|
|
1
|
+
import{C as e,D as t,E as n,I as r,Q as i,R as a,S as o,V as s,X as c,Y as l,Z as u,h as d,o as f}from"./index-D0zbyWWM.js";import{i as p,n as m,t as h}from"./StatTile-5XA5PKWp.js";import{n as g,t as _}from"./Results-xBStmWdp.js";var v=i(u(),1),y=c();function b(){return s({queryKey:[`cache`,`entries`],queryFn:async()=>n(await o.GET(`/api/v1/cache/entries`)).items,retry:!1})}function x(){let e=a(),t=l(),n=d(),r=m(),i=f(),o=r.data??[],s=n.data?.find(e=>e.id===`cache`)?.rows[0]?.value??`…`,c=p(o,`queryapigate_cache_hits_total`),u=p(o,`queryapigate_cache_misses_total`),g=c+u,_=(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:s}),(0,y.jsx)(h,{label:`Entries`,value:p(o,`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:u})]}),(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=l(),{openDrawer:n,showError:i,toast:a}=r(),s=b().data??[],c=()=>void e.invalidateQueries({queryKey:[`cache`,`entries`]}),u=e=>i(e.message),d=async()=>{if(window.confirm(`Evict all ${s.length} cache entries? The next call to each query will re-run for real.`))try{t(await o.DELETE(`/api/v1/cache/entries`)),a(`Cache cleared`),c()}catch(e){u(e)}},f=async e=>{try{t(await o.DELETE(`/api/v1/cache/entries/{key}`,{params:{path:{key:e}}})),a(`Entry deleted`),c()}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:`${s.length}${s.length===1?` live entry`:` live entries`} right now.`})]}),(0,y.jsx)(`span`,{className:`spacer`}),(0,y.jsx)(`button`,{type:`button`,className:`btn sm`,disabled:!s.length,onClick:()=>void d(),children:`Clear cache`})]}),s.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:s.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:t}){let{showError:n}=r(),i=t.meta?.name||`cache-entry`,a=t.meta?.format||`json`,[o,s]=(0,v.useState)(null);return(0,v.useEffect)(()=>{let r=!0,o=performance.now();return e(`/api/v1/cache/entries/${encodeURIComponent(t.key)}`).then(e=>g(e,{page:1,size:0,format:a,elapsed:Math.round(performance.now()-o),filename:i})).then(e=>{r&&(e.error&&n(e.error,{status:e.status}),s(e))},e=>r&&s(w(`Network error: `+e.message))),()=>{r=!1}},[t.key,a,i,n]),(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:o,running:!o,filename:i,onPage:()=>{},onPageSize:()=>{},layout:`drawer`})]})}export{x as CachingPage};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{X as e}from"./index-
|
|
1
|
+
import{X as e}from"./index-D0zbyWWM.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-BCy3Br67.js";import{D as t,E as n,I as r,M as i,N as a,O as o,P as s,Q as c,R as l,S as u,V as d,X as f,Y as p,Z as m,c as h,j as g,x as _}from"./index-D0zbyWWM.js";import{t as v}from"./Time-zMnS1kSm.js";import{n as y,t as b}from"./icons-B_wbfO4Z.js";var x=c(m(),1),S=f(),C=[`mysql`,`postgres`,`clickhouse`,`sqlite`,`h2`,`duckdb`,`mongo`],w=`files`,T=[`h2`,`jdbc`,`mongo`],ee=[`mysql`,`postgres`,`clickhouse`,`mongo`];function E(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 D(e){return e.host?e.host+(e.port?`:`+e.port:``):``}function O(){return d({queryKey:[`connections`],queryFn:async()=>n(await u.GET(`/api/v1/connections`)).items,retry:!1})}function k(e){return d({queryKey:[`connections`,`deleted`],queryFn:async()=>n(await u.GET(`/api/v1/connections/deleted`)).items,enabled:e,retry:!1})}function A(){let e=h(),t=l(),{openDrawer:n}=r(),i=O(),[a,o]=(0,x.useState)(`all`),[c,u]=(0,x.useState)(``),d=k(!0),f=i.data??[],p=f.filter(e=>e.active).length,m=d.data??[],_=c.trim().toLowerCase(),C=()=>n({title:`New connection`,kicker:`POST /api/v1/connections`,content:(0,S.jsx)(j,{})}),w,T=null;if(a===`deleted`){let e=m.filter(e=>!_||[e.name,e.deleted_by,e.reason,e.db,e.host].join(` `).toLowerCase().includes(_));m.length?(T=(0,S.jsx)(`span`,{children:`Showing ${e.length} of ${m.length}${m.length===1?` deleted connection`:` deleted connections`}`}),w=e.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`,`Type`,`Host`,`Database`,`Deleted at`,`Deleted by`,`Reason`].map(e=>(0,S.jsx)(`th`,{children:e},e))})}),(0,S.jsx)(`tbody`,{children:e.map((e,t)=>(0,S.jsxs)(`tr`,{children:[(0,S.jsx)(`td`,{children:(0,S.jsx)(`span`,{className:`name`,children:e.name})}),(0,S.jsx)(`td`,{children:(0,S.jsx)(`span`,{className:`tag`,children:e.db||`?`})}),(0,S.jsx)(`td`,{className:`mono`,style:{whiteSpace:`nowrap`},children:D(e)||(0,S.jsx)(`span`,{className:`dim`,children:`—`})}),(0,S.jsx)(`td`,{className:`mono`,children:e.database||``}),(0,S.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,S.jsx)(v,{value:e.deleted_at})}),(0,S.jsx)(`td`,{className:`mono`,children:e.deleted_by||`—`}),(0,S.jsx)(`td`,{children:e.reason||`—`})]},`${e.name}-${t}`))})]})}):(0,S.jsx)(g,{children:`No deleted connections match “${_}”.`})):w=(0,S.jsx)(g,{title:`No deleted connections`,children:`Every connection deletion is recorded here, with who did it, when, and why.`})}else if(i.isPending)w=(0,S.jsx)(s,{text:`Loading connections…`});else if(i.isError)w=(0,S.jsx)(g,{title:`Couldn’t load connections`,children:(0,S.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>i.refetch(),children:`Retry`})});else if(!f.length)w=(0,S.jsxs)(`div`,{className:`empty`,children:[(0,S.jsx)(`strong`,{children:`No connections yet`}),(0,S.jsx)(`span`,{children:`Add a MySQL, PostgreSQL, ClickHouse, SQLite or H2 database to start running SQL.`}),(0,S.jsx)(`button`,{hidden:!e(`connections.write`),type:`button`,className:`btn primary`,onClick:C,children:`New connection`})]});else{let r=f.filter(e=>a===`active`&&!e.active||a===`inactive`&&e.active?!1:!_||[e.name,e.db,e.host,e.database,e.user].join(` `).toLowerCase().includes(_));T=(0,S.jsx)(`span`,{children:`Showing ${r.length} of ${f.length}${f.length===1?` connection`:` connections`}`}),w=r.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`,`Type`,`Host`,`Database`,`User`,`Status`,`Usage`,`Created`,`Last modified`,``].map((e,t)=>(0,S.jsx)(`th`,{children:e},t))})}),(0,S.jsx)(`tbody`,{children:r.map(r=>{let i=E(r.usage);return(0,S.jsxs)(`tr`,{"data-name":r.name,children:[(0,S.jsx)(`td`,{children:(0,S.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,S.jsx)(`span`,{className:`name`,children:r.name}),r.example&&(0,S.jsx)(`span`,{className:`tag example`,title:`Installed by the example APIs — removing the examples removes it`,children:`example`})]})}),(0,S.jsx)(`td`,{children:(0,S.jsx)(`span`,{className:`tag`,children:r.db||`?`})}),(0,S.jsx)(`td`,{className:`mono`,style:{whiteSpace:`nowrap`},children:D(r)||(0,S.jsx)(`span`,{className:`dim`,children:`—`})}),(0,S.jsx)(`td`,{className:`mono`,children:r.database||``}),(0,S.jsx)(`td`,{className:`mono dim`,children:r.user||`—`}),(0,S.jsx)(`td`,{children:(0,S.jsxs)(`span`,{className:`pill ${r.active?`ok`:`off`}`,children:[(0,S.jsx)(`i`,{}),r.active?`Active`:`Inactive`]})}),i?(0,S.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,S.jsx)(`td`,{className:`dim`,style:{whiteSpace:`nowrap`},children:`No activity yet`}),(0,S.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,S.jsx)(v,{value:r.created_at})}),(0,S.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,S.jsx)(v,{value:r.updated_at})}),(0,S.jsx)(`td`,{children:(0,S.jsxs)(`div`,{className:`actions`,children:[(0,S.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,S.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,S.jsx)(j,{name:r.name})}),children:(0,S.jsx)(b,{})}),(0,S.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,S.jsx)(N,{name:r.name})}),children:(0,S.jsx)(y,{})})]})})]},r.name)})})]})}):(0,S.jsx)(g,{children:_?`No connections match “${_}”.`:`No ${a} connections.`})}let ee=[[`all`,`All`,f.length],[`active`,`Active`,p],[`inactive`,`Inactive`,f.length-p],[`deleted`,`Deleted`,m.length]];return(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:`Connections`}),(0,S.jsx)(`span`,{className:`sub`,children:`Databases this gateway can run saved queries against.`})]}),(0,S.jsx)(`span`,{className:`spacer`}),(0,S.jsx)(`button`,{id:`new-connection`,type:`button`,className:`btn primary`,hidden:a===`deleted`||!e(`connections.write`),onClick:C,children:`New connection`})]}),(0,S.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,S.jsxs)(`div`,{className:`panel-bar`,children:[(0,S.jsx)(`div`,{className:`seg`,id:`conn-tabs`,role:`group`,"aria-label":`Connection status`,children:ee.map(([e,t,n])=>(0,S.jsxs)(`button`,{type:`button`,className:a===e?`on`:``,onClick:()=>o(e),children:[t,(0,S.jsx)(`span`,{className:`n`,children:n})]},e))}),(0,S.jsx)(`span`,{className:`spacer`}),(0,S.jsx)(`input`,{id:`conn-filter`,className:`search`,type:`search`,placeholder:a===`deleted`?`Filter by name, actor, reason…`:`Filter by name, type, host…`,value:c,onChange:e=>u(e.target.value)})]}),(0,S.jsx)(`div`,{id:`connections-table`,children:w}),(0,S.jsx)(`div`,{className:`panel-foot`,id:`connections-foot`,children:T})]})]})}function te(e,t){t instanceof _?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 j({name:e}){let t=!!e,n=p(),{closeDrawer:i,showError:a,toast:c}=r(),l=d({queryKey:[`connections`,`detail`,e],queryFn:async()=>o(await u.GET(`/api/v1/connections/{name}`,{params:{path:{name:e}}})),enabled:t,retry:!1});return t&&l.isPending?(0,S.jsx)(s,{}):t&&l.isError?(0,S.jsx)(`div`,{className:`hint`,children:l.error.message}):(0,S.jsx)(M,{name:e,existing:l.data?.data,etag:l.data?.etag??null,onSaved:e=>{a(``),i(),c((t?`Saved `:`Created `)+e),n.invalidateQueries({queryKey:[`connections`]})}})}function M({name:t,existing:o,etag:s,onSaved:c}){let l=!!t,{closeDrawer:d,showError:f}=r(),[p,m]=(0,x.useState)(t??``),[h,g]=(0,x.useState)(o?.db===`duckdb`&&o.database===`:memory:`?w:o?.db??`mysql`),v=h===w,y=v?`duckdb`:h,[b,E]=(0,x.useState)(o?.host??``),[D,O]=(0,x.useState)(o?.port==null?``:String(o.port)),[k,A]=(0,x.useState)(o?.user??``),[j,M]=(0,x.useState)(l?o?.password??``:``),[N,ne]=(0,x.useState)(!1),[P,F]=(0,x.useState)(o?.database??``),[I,re]=(0,x.useState)(!o||o.active),[L,ie]=(0,x.useState)(null),[R,z]=(0,x.useState)(null),[B,V]=(0,x.useState)(null),[ae,oe]=(0,x.useState)(!1),H=o?.options??{},[U,se]=(0,x.useState)((H.allowed_paths??[]).join(`
|
|
2
2
|
`)),[W,ce]=(0,x.useState)(H.storage??`s3`),[G,le]=(0,x.useState)(H.region??``),[K,ue]=(0,x.useState)(H.endpoint??``),[q,de]=(0,x.useState)(H.url_style??``),[J,fe]=(0,x.useState)(H.use_ssl!==!1),[Y,pe]=(0,x.useState)(H.views?JSON.stringify(H.views,null,2):``),[me,he]=(0,x.useState)(o?H.auto_views===!0:null),X=me??v,Z=y===`duckdb`,ge=e=>{if(!Z)return{};let t=U.split(`
|
|
3
3
|
`).map(e=>e.trim()).filter(Boolean),n=e;if(Y.trim())try{n=JSON.parse(Y)}catch{throw Error(`Views must be a JSON object of {"name": "SELECT ..."}.`)}return{allowed_paths:t.length?t:e,storage:W===`s3`?e:W,region:G.trim()||e,endpoint:K.trim()||e,url_style:q||e,use_ssl:J?e:!1,auto_views:X?!0:e,views:n}},Q=()=>({db:y,host:b||void 0,port:D?Number(D):void 0,user:k||void 0,password:j,database:v?`:memory:`:P||void 0,...l?{name:t}:{},...ge(void 0)}),$=e({mutationFn:async e=>{let t=e===`test`?`/api/v1/connections/test`:`/api/v1/connections/databases`,r=await u.POST(t,{body:Q()});return{kind:e,data:n(r)}}}),_e=e=>`✗ `+(e instanceof _?e.message+(e.detail?`: `+e.detail:``):`Network error`),ve=()=>{z(null),$.mutate(`databases`,{onSuccess:({data:e})=>{let t=e.databases;ie(t),z({ok:!0,text:`✓ ${t.length}${t.length===1?` database`:` databases`}`})},onError:e=>z({ok:!1,text:_e(e)})})},ye=()=>{V(null),$.mutate(`test`,{onSuccess:({data:e})=>{let t=e.elapsed_ms;V({ok:!0,text:`✓ Connected`+(t===void 0?``:` (${t} ms)`)})},onError:e=>V({ok:!1,text:_e(e)})})},be=ee.includes(y);l&&be&&!ae&&(oe(!0),setTimeout(ve,0));let xe=e({mutationFn:async()=>{if(l){let e={db:y,active:I,host:b||null,port:D?Number(D):null,user:k||null,password:j,database:v?`:memory:`:P||null,...ge(null)};return n(await u.PATCH(`/api/v1/connections/{name}`,{params:{path:{name:t},header:s?{"If-Match":s}:void 0},body:e})),t}let e={...Q(),name:p.trim(),active:I};return n(await u.POST(`/api/v1/connections`,{body:e})).name},onSuccess:c,onError:e=>e instanceof _?te(f,e):f(e.message)});return(0,S.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{if(e.preventDefault(),!l&&!p.trim()){f(`A connection name is required.`,{errors:{name:`This field is required`}});return}xe.mutate()},children:[(0,S.jsx)(i,{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,S.jsx)(`input`,{id:`c-name`,placeholder:`reporting-db`,autoComplete:`off`,spellCheck:!1,disabled:l,autoFocus:!l,value:p,onChange:e=>m(e.target.value)})}),(0,S.jsx)(i,{id:`c-db`,label:`Database type`,children:(0,S.jsxs)(`select`,{id:`c-db`,value:h,onChange:e=>g(e.target.value),children:[C.map(e=>(0,S.jsx)(`option`,{value:e,children:T.includes(e)?`${e} (experimental)`:e},e)),(0,S.jsx)(`option`,{value:w,children:`Files (Parquet, CSV, JSON)`})]})}),(0,S.jsxs)(`div`,{className:`grid-host`,hidden:Z,children:[(0,S.jsx)(i,{id:`c-host`,label:`Host`,children:(0,S.jsx)(`input`,{id:`c-host`,placeholder:`localhost`,autoComplete:`off`,spellCheck:!1,autoFocus:l,value:b,onChange:e=>E(e.target.value)})}),(0,S.jsx)(i,{id:`c-port`,label:`Port`,children:(0,S.jsx)(`input`,{id:`c-port`,type:`number`,autoComplete:`off`,value:D,onChange:e=>O(e.target.value)})})]}),(0,S.jsxs)(`div`,{className:`grid2`,children:[(0,S.jsx)(i,{id:`c-user`,label:Z?`Access key ID`:`User`,hint:Z?`For object storage; leave empty for public files.`:void 0,children:(0,S.jsx)(`input`,{id:`c-user`,autoComplete:`off`,spellCheck:!1,value:k,onChange:e=>A(e.target.value)})}),(0,S.jsx)(i,{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,S.jsxs)(`div`,{className:`password-field`,children:[(0,S.jsx)(`input`,{id:`c-password`,type:N?`text`:`password`,autoComplete:`off`,spellCheck:!1,value:j,onChange:e=>M(e.target.value)}),(0,S.jsx)(`button`,{type:`button`,className:`btn ghost sm`,"aria-label":(N?`Hide`:`Show`)+` password`,onClick:()=>ne(e=>!e),children:N?`Hide`:`Show`})]})})]}),(0,S.jsx)(`div`,{hidden:v,children:(0,S.jsx)(i,{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,S.jsx)(`div`,{children:L?(0,S.jsx)(`select`,{id:`c-database`,value:L.includes(P)?P:L[0]??``,onChange:e=>F(e.target.value),children:L.map(e=>(0,S.jsx)(`option`,{value:e,children:e},e))}):(0,S.jsx)(`input`,{id:`c-database`,placeholder:`SQLite, DuckDB and H2 take a file path`,autoComplete:`off`,spellCheck:!1,value:P,onChange:e=>F(e.target.value)})})})}),(0,S.jsxs)(`div`,{className:`test-row`,children:[(0,S.jsx)(`button`,{type:`button`,className:`btn sm ghost`,hidden:!be,disabled:$.isPending,onClick:ve,children:$.isPending&&$.variables===`databases`?`Loading…`:`Load databases…`}),(0,S.jsx)(`span`,{className:`test-result`,children:R&&(0,S.jsx)(`span`,{className:R.ok?`test-ok`:`test-fail`,children:R.text})})]}),Z&&(0,S.jsxs)(`fieldset`,{className:`duck-files`,children:[(0,S.jsx)(`legend`,{children:`Files it may read`}),(0,S.jsx)(i,{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,S.jsx)(`textarea`,{id:`c-paths`,rows:3,spellCheck:!1,placeholder:`s3://sales/2026/
|
|
4
4
|
https://data.example.com/prices.parquet`,value:U,onChange:e=>se(e.target.value)})}),(0,S.jsxs)(`div`,{className:`grid2`,children:[(0,S.jsx)(i,{id:`c-storage`,label:`Object storage`,children:(0,S.jsxs)(`select`,{id:`c-storage`,value:W,onChange:e=>ce(e.target.value),children:[(0,S.jsx)(`option`,{value:`s3`,children:`S3 or S3-compatible`}),(0,S.jsx)(`option`,{value:`gcs`,children:`Google Cloud Storage`}),(0,S.jsx)(`option`,{value:`r2`,children:`Cloudflare R2`})]})}),(0,S.jsx)(i,{id:`c-region`,label:`Region`,children:(0,S.jsx)(`input`,{id:`c-region`,placeholder:`us-east-1`,spellCheck:!1,value:G,onChange:e=>le(e.target.value)})})]}),(0,S.jsxs)(`div`,{className:`grid2`,children:[(0,S.jsx)(i,{id:`c-endpoint`,label:`Endpoint`,hint:`Only for an S3-compatible service, e.g. minio.internal:9000.`,children:(0,S.jsx)(`input`,{id:`c-endpoint`,spellCheck:!1,value:K,onChange:e=>ue(e.target.value)})}),(0,S.jsx)(i,{id:`c-url-style`,label:`URL style`,children:(0,S.jsxs)(`select`,{id:`c-url-style`,value:q,onChange:e=>de(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.jsxs)(`label`,{className:`switch`,children:[(0,S.jsx)(`input`,{id:`c-auto-views`,type:`checkbox`,checked:X,onChange:e=>he(e.target.checked)}),`A view for each file and folder`,(0,S.jsx)(`span`,{className:`hint`,children:`— orders.parquet becomes orders; a folder of files, one view over all of them`})]}),(0,S.jsxs)(`label`,{className:`switch`,children:[(0,S.jsx)(`input`,{id:`c-ssl`,type:`checkbox`,checked:J,onChange:e=>fe(e.target.checked)}),`Use HTTPS for object storage`]}),(0,S.jsx)(i,{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,S.jsx)(`textarea`,{id:`c-views`,rows:3,spellCheck:!1,value:Y,onChange:e=>pe(e.target.value)})})]}),(0,S.jsxs)(`label`,{className:`switch`,children:[(0,S.jsx)(`input`,{id:`c-active`,type:`checkbox`,checked:I,onChange:e=>re(e.target.checked)}),`Active`,(0,S.jsx)(`span`,{className:`hint`,children:`— inactive connections refuse queries`})]}),(0,S.jsxs)(`div`,{className:`test-row`,children:[(0,S.jsx)(`button`,{type:`button`,className:`btn`,disabled:$.isPending,onClick:ye,children:$.isPending&&$.variables===`test`?`Testing…`:`Test connection`}),(0,S.jsx)(`span`,{className:`test-result`,children:B&&(0,S.jsx)(`span`,{className:B.ok?`test-ok`:`test-fail`,children:B.text})})]}),l&&o?.created_at&&(0,S.jsx)(`div`,{className:`hint`,children:`Created `+o.created_at+(o.updated_at&&o.updated_at!==o.created_at?` · last edited `+o.updated_at:``)}),(0,S.jsx)(a,{onCancel:d,children:(0,S.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:xe.isPending,children:l?`Save`:`Create`})})]})}function N({name:n}){let o=p(),{closeDrawer:s,showError:c,toast:l}=r(),[d,f]=(0,x.useState)(``),[m,h]=(0,x.useState)(``),g=e({mutationFn:async()=>t(await u.DELETE(`/api/v1/connections/{name}`,{params:{path:{name:n}},body:{reason:d.trim()}})),onSuccess:()=>{s(),l(`Deleted `+n),o.invalidateQueries({queryKey:[`connections`]})},onError:e=>te(c,e)}),_=!!d.trim()&&m===n;return(0,S.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{e.preventDefault(),_&&g.mutate()},children:[(0,S.jsxs)(`p`,{className:`d-desc`,children:[`This deletes `,(0,S.jsx)(`span`,{className:`name`,children:n}),` permanently. Every saved query that uses it will stop working. It moves to the Deleted tab, with the reason and who deleted it.`]}),(0,S.jsx)(i,{id:`del-reason`,label:`Reason`,hint:`Recorded in the audit log - required.`,children:(0,S.jsx)(`textarea`,{id:`del-reason`,placeholder:`Why is this connection being deleted?`,autoFocus:!0,value:d,onChange:e=>f(e.target.value)})}),(0,S.jsx)(i,{id:`del-confirm`,label:`Type "${n}" to confirm`,children:(0,S.jsx)(`input`,{id:`del-confirm`,type:`text`,autoComplete:`off`,spellCheck:!1,placeholder:n,value:m,onChange:e=>h(e.target.value)})}),(0,S.jsx)(a,{onCancel:s,children:(0,S.jsx)(`button`,{type:`submit`,className:`btn danger`,disabled:!_||g.isPending,children:`Delete`})})]})}export{j as ConnectionForm,A as ConnectionsPage};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{A as e,E as t,I as n,L as r,M as i,N as a,Q as o,R as s,S as c,T as ee,V as l,X as u,Z as d,l as f,n as p,o as m,r as h,t as g,u as _}from"./index-
|
|
1
|
+
import{A as e,E as t,I as n,L as r,M as i,N as a,Q as o,R as s,S as c,T as ee,V as l,X as u,Z as d,l as f,n as p,o as m,r as h,t as g,u as _}from"./index-D0zbyWWM.js";import{c as v,n as y,t as te}from"./SchemaBrowser-DykbAObL.js";import{a as b,i as x}from"./CodeBox-GGv4fKoR.js";import{n as ne,r as S,t as re}from"./Results-xBStmWdp.js";import{Impact as C,t as w}from"./forms-Cl7JC3vt.js";var T=o(d(),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 me(e){try{return localStorage.getItem(e)}catch{return null}}function z(e,t){try{localStorage.setItem(e,t)}catch{}}function B(){let i=s(),a=r(),{showError:o}=n(),u=_(),d=(0,T.useMemo)(()=>u.data??[],[u.data]),f=(0,T.useMemo)(()=>Object.fromEntries(d.map(e=>[e.name,e])),[d]),[p,m]=(0,T.useState)(ie);(0,T.useEffect)(()=>ae(p),[p]);let h=e=>m(t=>({...t,...e})),v=(0,T.useRef)(null),C=[...new Set(d.map(e=>e.db))].sort(),E=C.includes(p.type)?p.type:``,D=[...new Set(d.filter(e=>!E||e.db===E).map(R))].sort(),O=D.includes(p.host)?p.host:``,k=d.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)),z=l({queryKey:[`connections`,j,`databases`],queryFn:async()=>t(await c.POST(`/api/v1/connections/databases`,{body:{name:j}})),enabled:L,retry:!1,staleTime:3e5}),B=z.data?.databases??[],V=B.includes(p.database)?p.database:M?.database??``,H=M&&V&&V!==M.database?V:null,be=y(j||void 0,H),xe=(0,T.useMemo)(()=>Object.fromEntries((be.data?.tables??[]).map(e=>[e.name,e.columns.map(e=>e.name)])),[be.data]),[Se,Ce]=(0,T.useState)(null),[U,W]=(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(t={}){let n=t.connection??j,r=f[n];if(!n||!r){o(`Pick a connection first — add one on the Connections tab.`);return}let i=r.db===`mongo`,a=t.sql??p.sql;if(t.explain&&i){o(`EXPLAIN is not supported for Mongo connections yet.`);return}if(!a.trim()){o(i?`Write a query first.`:`Write some SQL to run.`,{errors:{sql:`This field is required`}});return}let s={};if(p.params.trim())try{s=JSON.parse(p.params)}catch(e){o(`Bound parameters must be valid JSON: `+e.message,{errors:{params:e.message}});return}let c=t.page??1,l=t.pageSize??(p.pageSize||10),u=`format=${encodeURIComponent(p.format)}&page=${c}&page_size=${l}${p.timeout?`&timeout=`+encodeURIComponent(p.timeout):``}`,d,m;if(i){let e;try{e=JSON.parse(a)}catch(e){o(`The query must be valid JSON: `+e.message,{errors:{sql:e.message}});return}if(!e||typeof e!=`object`||Array.isArray(e)||!e.collection){o(`The query must be a JSON object with a "collection" field, e.g. {"collection": "users", "filter": {}}.`,{errors:{sql:`collection is required`}});return}d=`/execute_mongo?${u}`,m={collection:e.collection,filter:e.filter??{},connection_name:n,params:s},e.projection&&(m.projection=e.projection),e.sort&&(m.sort=e.sort)}else d=`/execute_sql?${u}`,m={sql:t.explain?`EXPLAIN `+a:a,connection_name:n,params:s},n===j&&H&&(m.database=H);Oe(!!t.explain),h({page:c}),o(``),!t.explain&&c===1&&t.page===void 0&&K(oe(a,n,p.format)),W(t.explain?`explain`:`run`),De([`curl -X POST ${x(new URL(d,globalThis.location?.href??`http://localhost`).href)}`,` -H 'Content-Type: application/json'`,...e()?[` -H 'X-API-Key: YOUR_KEY_HERE' # replace with your own key`]:[],` -d ${x(JSON.stringify(m))}`].join(` \\
|
|
4
4
|
`));try{let{response:e,elapsed:n}=await ee(d,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(m)}),r=await ne(e,{page:c,size:l,format:p.format,elapsed:n,filename:t.explain?`explain`:`query`});r.error&&o(r.error,{status:r.status}),Ce(r);let i=r.rows?r.rows.length:null;we({status:r.status,rows:i,elapsed:n,format:p.format,widest:r.rows?S(r.rows):null}),t.explain||K(se(r.status,i,n))}catch(e){o(`Network error: `+e.message)}finally{W(!1)}}let ke=a.state,Y=ke?.preview,X=ke?.select,Ae=(0,T.useRef)(null);(0,T.useEffect)(()=>{if(!X||Ae.current===a.key||!f[X])return;Ae.current=a.key;let e=f[X];m(t=>({...t,type:e.db,host:R(e),connection:e.name,database:``})),i(a.pathname,{replace:!0,state:null})},[X,a.key,a.pathname,f,i]);let je=(0,T.useRef)(null);(0,T.useEffect)(()=>{if(!Y||je.current===a.key||!f[Y.connection])return;je.current=a.key;let e=f[Y.connection],t=e.db===`mongo`?JSON.stringify({collection:Y.table,filter:{}},null,2):`SELECT * FROM `+Y.table;m(n=>({...n,type:e.db,host:R(e),connection:e.name,database:``,sql:t,page:1})),J({sql:t,connection:e.name}),i(a.pathname,{replace:!0,state:null})},[Y,a.key,a.pathname,f,J,i]);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=v.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),v.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){o(`Pick a connection first — add one on the Connections tab.`);return}if(!p.sql.trim()){o(`Write a query first.`,{errors:{sql:`This field is required`}});return}$(!0)}let Le=P?ue(p.sql):b(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(me(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||!z.isSuccess,value:L&&z.isSuccess?V:``,onChange:e=>h({database:e.target.value}),children:M?L?z.isPending?(0,N.jsx)(`option`,{value:``,children:`Loading…`}):z.isError?(0,N.jsx)(`option`,{value:``,children:`Unavailable`}):B.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:!!U,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:!!U,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:v,id:`run-sql`,boxed:!1,value:p.sql,onChange:e=>h({sql:e}),dialect:M?.db,schema:xe,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)(ge,{}),(0,N.jsx)(`div`,{className:`runner-side`,children:(0,N.jsx)(he,{recent:(0,N.jsx)(_e,{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)(te,{connection:j,dialect:M?.db,database:H,editor:v,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)(ye,{connection:j,sql:p.sql,isMongo:P,onClose:()=>$(!1),onSaved:e=>{$(!1),i(`/queries/${encodeURIComponent(e)}`)}})}),(0,N.jsx)(re,{result:Se,running:!!U,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)(ve,{...Z,onApply:Pe,onClose:()=>Q(null)})]})}function he({recent:e,settings:t,schema:n}){let[r,i]=(0,T.useState)(()=>{let e=me(P);return e===`recent`||e===`settings`?e:`schema`}),a=e=>{i(e),z(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 ge(){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&&z(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 _e({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 ve({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 ye({connection:e,sql:t,isMongo:r,onClose:o,onSaved:s}){let{showError:c,toast:ee}=n(),l=m(),u=f(),d=v(),[p,h]=(0,T.useState)(``),[g,_]=(0,T.useState)(``),[y,te]=(0,T.useState)(``),[x,ne]=(0,T.useState)(``),[S,re]=(0,T.useState)(``),[w,E]=(0,T.useState)(()=>{let e=r?ue(t):b(t);return e.length?JSON.stringify(Object.fromEntries(e.map(e=>[e,{}])),null,2):``});function D(n){let i={};if(w.trim())try{i=JSON.parse(w)}catch(e){c(`query_parameters is not valid JSON: `+e.message,{errors:{query_parameters:e.message}});return}let a=p.trim(),o={author:g,description:y,tags:S.split(`,`).map(e=>e.trim()).filter(Boolean),connection_name:e||void 0,parameters:i,publish:n};if(r){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(o,{query_type:`mongo`,mongo_collection:e.collection,mongo_filter:e.filter??{}}),e.projection&&(o.mongo_projection=e.projection),e.sort&&(o.mongo_sort=e.sort)}else o.sql=t;let u=l.data?.find(e=>e.name===a),f=x.trim();if(f&&u&&(u.collection??``)!==f){c(`“${a}” already exists${u.collection?` in collection `+u.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={...o,name:a,...f?{collection:f}:{}};d.mutate(u?{kind:`version`,name:a,etag:null,body:o}:{kind:`create`,body:m},{onSuccess:e=>{c(``),ee(`Saved ${e.name} v${e.latest_version}${n?``:` as a draft`}`),s(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:o,children:`✕`})]}),(0,N.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{e.preventDefault(),D(!0)},children:[(0,N.jsx)(i,{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:p,onChange:e=>h(e.target.value)})}),(0,N.jsxs)(`div`,{className:`grid2`,children:[(0,N.jsx)(i,{id:`rs-author`,label:`Author`,children:(0,N.jsx)(`input`,{id:`rs-author`,required:!0,autoComplete:`off`,spellCheck:!1,value:g,onChange:e=>_(e.target.value)})}),(0,N.jsx)(i,{id:`rs-description`,label:`Description`,children:(0,N.jsx)(`input`,{id:`rs-description`,required:!0,autoComplete:`off`,spellCheck:!1,value:y,onChange:e=>te(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(u.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:u.data})]}),(0,N.jsx)(i,{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)(i,{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)(a,{onCancel:o,children:[(0,N.jsx)(`button`,{type:`button`,className:`btn`,disabled:d.isPending,title:`Save without publishing`,onClick:()=>D(!1),children:`Save as draft`}),(0,N.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:d.isPending,children:`Save`})]})]})]})}export{B as DesignerPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./useMutation-
|
|
1
|
+
import{t as e}from"./useMutation-BCy3Br67.js";import{D as t,E as n,I as r,M as i,N as a,O as o,P as s,Q as c,S as l,V as u,X as d,Y as f,Z as p,c as m,x as h}from"./index-D0zbyWWM.js";import{n as g,t as _}from"./icons-B_wbfO4Z.js";import{r as v,t as y}from"./shared-DygTbsGs.js";var b=c(p(),1),x=d();function S(){let e=y(),i=m(),{openDrawer:a,toast:o}=r(),c=v(),u=f(),d=i(`destinations.write`),p=()=>a({title:`New destination`,content:(0,x.jsx)(C,{})}),h=async e=>{try{let t=n(await l.POST(`/api/v1/destinations/{name}/test`,{params:{path:{name:e}}}));o(`✓ Wrote ${t.object} (${t.elapsed_ms} ms)`)}catch(e){c(e)}},b=async e=>{if(window.confirm(`Delete destination '${e}'? Its credentials are removed; files already written stay.`))try{t(await l.DELETE(`/api/v1/destinations/{name}`,{params:{path:{name:e}}})),o(`Deleted `+e),u.invalidateQueries({queryKey:[`destinations`]})}catch(e){c(e)}},S;return S=e.isPending?(0,x.jsx)(s,{text:`Loading destinations…`}):e.isError?(0,x.jsx)(`div`,{className:`hint`,children:e.error.message}):e.data.length?(0,x.jsx)(`div`,{style:{overflowX:`auto`},children:(0,x.jsxs)(`table`,{className:`grid`,children:[(0,x.jsx)(`thead`,{children:(0,x.jsx)(`tr`,{children:[`Name`,`Writes under`,`Storage`,`Credentials`,``].map((e,t)=>(0,x.jsx)(`th`,{children:e},t))})}),(0,x.jsx)(`tbody`,{children:e.data.map(e=>(0,x.jsxs)(`tr`,{"data-name":e.name,children:[(0,x.jsx)(`td`,{className:`name`,children:e.name}),(0,x.jsx)(`td`,{className:`mono`,children:e.url}),(0,x.jsx)(`td`,{className:`dim`,children:[e.storage??`local folder`,e.region,e.endpoint].filter(Boolean).join(` · `)}),(0,x.jsx)(`td`,{className:`dim`,children:e.user?`${e.user} · ${e.password??`no secret`}`:`none`}),(0,x.jsx)(`td`,{children:(0,x.jsxs)(`div`,{className:`actions`,hidden:!d,children:[(0,x.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>void h(e.name),children:`Test`}),(0,x.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,x.jsx)(C,{name:e.name})}),children:(0,x.jsx)(_,{})}),(0,x.jsx)(`button`,{type:`button`,className:`btn ghost sm icon danger`,title:`Delete ${e.name}`,"aria-label":`Delete`,onClick:()=>void b(e.name),children:(0,x.jsx)(g,{})})]})})]},e.name))})]})}):(0,x.jsxs)(`div`,{className:`empty`,children:[(0,x.jsx)(`strong`,{children:`No destinations yet`}),(0,x.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,x.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:p,children:`New destination`})]}),(0,x.jsxs)(x.Fragment,{children:[(0,x.jsxs)(`div`,{className:`page-head`,children:[(0,x.jsxs)(`div`,{className:`titles`,children:[(0,x.jsx)(`h1`,{children:`Destinations`}),(0,x.jsx)(`span`,{className:`sub`,children:`Buckets and folders exports may write to, and the credentials to write there.`})]}),(0,x.jsx)(`span`,{className:`spacer`}),d&&(0,x.jsx)(`button`,{id:`new-destination`,type:`button`,className:`btn primary`,onClick:p,children:`New destination`})]}),(0,x.jsx)(`div`,{id:`destinations-table`,className:`panel`,children:S})]})}function C({name:e}){let t=u({queryKey:[`destinations`,`detail`,e],queryFn:async()=>o(await l.GET(`/api/v1/destinations/{name}`,{params:{path:{name:e}}})),enabled:!!e,retry:!1,gcTime:0});return e&&t.isPending?(0,x.jsx)(s,{}):e&&t.isError?(0,x.jsx)(`div`,{className:`hint`,children:t.error.message}):(0,x.jsx)(w,{name:e,existing:t.data?.data,etag:t.data?.etag??null},e??``)}function w({name:t,existing:o,etag:s}){let c=!!t,{closeDrawer:u,showError:d,toast:p}=r(),m=v(),g=f(),[_,y]=(0,b.useState)(t??``),[S,C]=(0,b.useState)(o?.url??``),[w,T]=(0,b.useState)(o?.region??``),[E,D]=(0,b.useState)(o?.endpoint??``),[O,k]=(0,b.useState)(o?.url_style??``),[A,j]=(0,b.useState)(o?.use_ssl!==!1),[M,N]=(0,b.useState)(o?.account_id??``),[P,F]=(0,b.useState)(o?.user??``),[I,L]=(0,b.useState)(o?.password??``),[R,z]=(0,b.useState)(null),B=/^(s3|gs|gcs|r2):\/\//.test(S),V=e=>({url:S.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()=>n(c?await l.PATCH(`/api/v1/destinations/{name}`,{params:{path:{name:t},header:s?{"If-Match":s}:{}},body:V(null)}):await l.POST(`/api/v1/destinations`,{body:{name:_.trim(),...V(void 0)}})),onSuccess:e=>{d(``),g.invalidateQueries({queryKey:[`destinations`]}),u(),p((c?`Updated `:`Created `)+e.name)},onError:m}),U=e({mutationFn:async()=>c&&I===o?.password?n(await l.POST(`/api/v1/destinations/{name}/test`,{params:{path:{name:t}}})):n(await l.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 h,e.message)})});return(0,x.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{if(e.preventDefault(),!c&&!_.trim()){d(`A name is required.`,{errors:{name:`This field is required`}});return}H.mutate()},children:[(0,x.jsx)(i,{id:`d-name`,label:`Name`,hint:c?void 0:`How exports refer to it, e.g. partner-acme.`,children:(0,x.jsx)(`input`,{id:`d-name`,value:_,placeholder:`partner-acme`,autoComplete:`off`,spellCheck:!1,disabled:c,autoFocus:!c,onChange:e=>y(e.target.value)})}),(0,x.jsx)(i,{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,x.jsx)(`input`,{id:`d-url`,value:S,placeholder:`s3://acme-exchange/from-us/`,autoComplete:`off`,spellCheck:!1,onChange:e=>C(e.target.value)})}),B&&(0,x.jsxs)(`fieldset`,{className:`duck-files`,children:[(0,x.jsx)(`legend`,{children:`Object storage`}),(0,x.jsxs)(`div`,{className:`grid2`,children:[(0,x.jsx)(i,{id:`d-user`,label:`Access key ID`,children:(0,x.jsx)(`input`,{id:`d-user`,value:P,autoComplete:`off`,spellCheck:!1,onChange:e=>F(e.target.value)})}),(0,x.jsx)(i,{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,x.jsx)(`input`,{id:`d-password`,type:`password`,value:I,autoComplete:`off`,onChange:e=>L(e.target.value)})})]}),(0,x.jsxs)(`div`,{className:`grid2`,children:[(0,x.jsx)(i,{id:`d-region`,label:`Region`,children:(0,x.jsx)(`input`,{id:`d-region`,value:w,placeholder:`eu-west-1`,onChange:e=>T(e.target.value)})}),(0,x.jsx)(i,{id:`d-endpoint`,label:`Endpoint`,hint:`Only for an S3-compatible service, e.g. minio.internal:9000.`,children:(0,x.jsx)(`input`,{id:`d-endpoint`,value:E,onChange:e=>D(e.target.value)})})]}),(0,x.jsxs)(`div`,{className:`grid2`,children:[(0,x.jsx)(i,{id:`d-url-style`,label:`URL style`,children:(0,x.jsxs)(`select`,{id:`d-url-style`,value:O,onChange:e=>k(e.target.value),children:[(0,x.jsx)(`option`,{value:``,children:`Default`}),(0,x.jsx)(`option`,{value:`vhost`,children:`Virtual-hosted (bucket.host)`}),(0,x.jsx)(`option`,{value:`path`,children:`Path (host/bucket)`})]})}),(0,x.jsx)(i,{id:`d-account`,label:`Account ID`,hint:`Cloudflare R2 only.`,children:(0,x.jsx)(`input`,{id:`d-account`,value:M,onChange:e=>N(e.target.value)})})]}),(0,x.jsxs)(`label`,{className:`switch`,children:[(0,x.jsx)(`input`,{id:`d-ssl`,type:`checkbox`,checked:A,onChange:e=>j(e.target.checked)}),`Use HTTPS`]})]}),(0,x.jsxs)(`div`,{className:`test-row`,children:[(0,x.jsx)(`button`,{type:`button`,className:`btn`,disabled:U.isPending||!S.trim(),onClick:()=>U.mutate(),children:U.isPending?`Testing…`:`Test`}),(0,x.jsx)(`span`,{className:`test-result`,children:R&&(0,x.jsx)(`span`,{className:R.ok?`test-ok`:`test-fail`,children:R.text})})]}),(0,x.jsxs)(`div`,{className:`hint`,children:[`Test writes one small file, `,`_queryapigate_probe.csv`,`, under the prefix - overwritten each time.`]}),(0,x.jsx)(a,{onCancel:u,children:(0,x.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:H.isPending,children:c?`Save`:`Create`})})]})}export{S as DestinationsPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{X as e}from"./index-
|
|
1
|
+
import{X as e}from"./index-D0zbyWWM.js";var t=e();function n(){return(0,t.jsx)(`span`,{className:`tag experimental`,title:`Experimental: may change in any minor release, always noted in the changelog`,children:`experimental`})}export{n as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./useMutation-yxhEJUOh.js";import{D as t,E as n,I as r,M as i,N as a,O as o,P as s,Q as c,S as l,V as u,X as d,Y as f,Z as p,c as m,o as h}from"./index-DRaDyzll.js";import{t as g}from"./Time-BHophAO8.js";import{n as _,t as v}from"./maskRules-BcPINA1z.js";import{n as y,t as b}from"./icons-BVIbiOCh.js";import{n as x,r as S,t as C}from"./shared-CtL1a_C1.js";var w=c(p(),1),T=d(),E=[`parquet`,`csv`,`ndjson`];function D(){let i=x(),a=m(),{openDrawer:o,toast:c}=r(),u=S(),d=f(),p=a(`exports.write`),h=()=>o({title:`New export`,wide:!0,content:(0,T.jsx)(k,{})}),g=e({mutationFn:async e=>n(await l.POST(`/api/v1/exports/{name}/runs`,{params:{path:{name:e}}})),onSuccess:e=>{c(e.object?`✓ ${e.rows} ${e.rows===1?`row`:`rows`} written to ${e.object}`:`✓ No new rows - nothing written`),d.invalidateQueries({queryKey:[`exports`]})},onError:e=>{u(e),d.invalidateQueries({queryKey:[`exports`]})}}),_=async e=>{if(window.confirm(`Delete export '${e}'? Files it already wrote stay where they are.`))try{t(await l.DELETE(`/api/v1/exports/{name}`,{params:{path:{name:e}}})),c(`Deleted `+e),d.invalidateQueries({queryKey:[`exports`]})}catch(e){u(e)}},v;return v=i.isPending?(0,T.jsx)(s,{text:`Loading exports…`}):i.isError?(0,T.jsx)(`div`,{className:`hint`,children:i.error.message}):i.data.length?(0,T.jsx)(`div`,{style:{overflowX:`auto`},children:(0,T.jsxs)(`table`,{className:`grid`,children:[(0,T.jsx)(`thead`,{children:(0,T.jsx)(`tr`,{children:[`Name`,`Query`,`Writes to`,`Format`,`Watermark`,``].map((e,t)=>(0,T.jsx)(`th`,{children:e},t))})}),(0,T.jsx)(`tbody`,{children:i.data.map(e=>(0,T.jsxs)(`tr`,{"data-name":e.name,children:[(0,T.jsx)(`td`,{children:(0,T.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,T.jsx)(`span`,{className:`name`,children:e.name}),e.running&&(0,T.jsx)(`span`,{className:`tag`,children:`running`})]})}),(0,T.jsx)(`td`,{className:`mono`,children:e.query}),(0,T.jsxs)(`td`,{className:`mono`,children:[e.destination,`:`,e.path]}),(0,T.jsx)(`td`,{children:e.format}),(0,T.jsx)(`td`,{className:e.incremental?`mono`:`dim`,children:e.incremental?`${e.incremental.column} > ${e.watermark??e.incremental.start??`(start)`}`:`everything, each run`}),(0,T.jsx)(`td`,{children:(0,T.jsxs)(`div`,{className:`actions`,children:[a(`exports.run`)&&(0,T.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,T.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>o({title:`Runs`,kicker:e.name,wide:!0,content:(0,T.jsx)(O,{name:e.name})}),children:`Runs`}),p&&(0,T.jsxs)(T.Fragment,{children:[(0,T.jsx)(`button`,{type:`button`,className:`btn ghost sm icon`,title:`Edit ${e.name}`,"aria-label":`Edit`,onClick:()=>o({title:`Edit export`,kicker:e.name,wide:!0,content:(0,T.jsx)(k,{name:e.name})}),children:(0,T.jsx)(b,{})}),(0,T.jsx)(`button`,{type:`button`,className:`btn ghost sm icon danger`,title:`Delete ${e.name}`,"aria-label":`Delete`,onClick:()=>void _(e.name),children:(0,T.jsx)(y,{})})]})]})})]},e.name))})]})}):(0,T.jsxs)(`div`,{className:`empty`,children:[(0,T.jsx)(`strong`,{children:`No exports yet`}),(0,T.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.`}),p&&(0,T.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:h,children:`New export`})]}),(0,T.jsxs)(T.Fragment,{children:[(0,T.jsxs)(`div`,{className:`page-head`,children:[(0,T.jsxs)(`div`,{className:`titles`,children:[(0,T.jsx)(`h1`,{children:`Exports`}),(0,T.jsx)(`span`,{className:`sub`,children:`Saved queries delivered as files to a bucket or a folder.`})]}),(0,T.jsx)(`span`,{className:`spacer`}),p&&(0,T.jsx)(`button`,{id:`new-export`,type:`button`,className:`btn primary`,onClick:h,children:`New export`})]}),(0,T.jsx)(`div`,{id:`exports-table`,className:`panel`,children:v})]})}function O({name:e}){let t=u({queryKey:[`exports`,e,`runs`],queryFn:async()=>n(await l.GET(`/api/v1/exports/{name}/runs`,{params:{path:{name:e}}})).items,retry:!1});return t.isPending?(0,T.jsx)(s,{text:`Loading runs…`}):t.isError?(0,T.jsx)(`div`,{className:`hint`,children:t.error.message}):t.data.length?(0,T.jsxs)(`table`,{className:`grid`,id:`export-runs`,children:[(0,T.jsx)(`thead`,{children:(0,T.jsx)(`tr`,{children:[`When`,`Status`,`Rows`,`Written`,`By`].map(e=>(0,T.jsx)(`th`,{children:e},e))})}),(0,T.jsx)(`tbody`,{children:t.data.map(e=>(0,T.jsxs)(`tr`,{children:[(0,T.jsx)(`td`,{className:`mono`,style:{whiteSpace:`nowrap`},children:(0,T.jsx)(g,{value:e.executed_at})}),(0,T.jsxs)(`td`,{children:[(0,T.jsx)(`span`,{className:`dot ${e.status===`success`?`ok`:`bad`}`}),` `,e.status]}),(0,T.jsx)(`td`,{className:`mono`,children:e.rows??`—`}),(0,T.jsx)(`td`,{className:e.object?`mono`:`dim`,children:e.status===`error`?e.error:e.object??`nothing - no new rows`}),(0,T.jsx)(`td`,{className:`dim`,children:e.key_name??`—`})]},String(e.run_id??e.executed_at)))})]}):(0,T.jsx)(`div`,{className:`hint`,children:`No runs yet.`})}function k({name:e}){let t=u({queryKey:[`exports`,`detail`,e],queryFn:async()=>o(await l.GET(`/api/v1/exports/{name}`,{params:{path:{name:e}}})),enabled:!!e,retry:!1,gcTime:0});return e&&t.isPending?(0,T.jsx)(s,{}):e&&t.isError?(0,T.jsx)(`div`,{className:`hint`,children:t.error.message}):(0,T.jsx)(A,{name:e,existing:t.data?.data,etag:t.data?.etag??null},e??``)}function A({name:t,existing:o,etag:s}){let c=!!t,{closeDrawer:u,showError:d,toast:p}=r(),m=S(),g=f(),y=h(),b=C(),[x,D]=(0,w.useState)(t??``),[O,k]=(0,w.useState)(o?.query??``),[A,j]=(0,w.useState)(o?.destination??``),[M,N]=(0,w.useState)(o?.format??`parquet`),[P,F]=(0,w.useState)(o?.path??`{name}/{date}/{name}_{run}.parquet`),[I,L]=(0,w.useState)(o?.params&&Object.keys(o.params).length?JSON.stringify(o.params):``),[R,z]=(0,w.useState)(!!o?.incremental),[B,V]=(0,w.useState)(o?.incremental?.column??``),[H,U]=(0,w.useState)(o?.incremental?.parameter??``),[W,G]=(0,w.useState)(String(o?.incremental?.start??``)),[K,q]=(0,w.useState)(!o||o.skip_empty),[J,Y]=(0,w.useState)((o?.mask??[]).map(e=>({column:e.column,action:e.action}))),[X,Z]=(0,w.useState)(o?.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 r={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 n(c?await l.PATCH(`/api/v1/exports/{name}`,{params:{path:{name:t},header:s?{"If-Match":s}:{}},body:r}):await l.POST(`/api/v1/exports`,{body:{name:x.trim(),...r}}))},onSuccess:e=>{d(``),g.invalidateQueries({queryKey:[`exports`]}),u(),p((c?`Updated `:`Created `)+e.name)},onError:m}),ee=e=>{if(e.preventDefault(),!c&&!x.trim()){d(`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,T.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:ee,children:[(0,T.jsxs)(`div`,{className:`grid2`,children:[(0,T.jsx)(i,{id:`x-name`,label:`Name`,children:(0,T.jsx)(`input`,{id:`x-name`,value:x,placeholder:`acme-daily-orders`,autoComplete:`off`,spellCheck:!1,disabled:c,autoFocus:!c,onChange:e=>D(e.target.value)})}),(0,T.jsx)(i,{id:`x-description`,label:`Description`,hint:`Optional.`,children:(0,T.jsx)(`input`,{id:`x-description`,value:X,onChange:e=>Z(e.target.value)})})]}),(0,T.jsxs)(`div`,{className:`grid2`,children:[(0,T.jsx)(i,{id:`x-query`,label:`Saved query`,hint:`Its published version runs.`,children:(0,T.jsxs)(`select`,{id:`x-query`,value:O,onChange:e=>k(e.target.value),children:[(0,T.jsx)(`option`,{value:``,children:`Choose a query…`}),te.map(e=>(0,T.jsx)(`option`,{value:e,children:e},e))]})}),(0,T.jsx)(i,{id:`x-destination`,label:`Destination`,children:(0,T.jsxs)(`select`,{id:`x-destination`,value:A,onChange:e=>j(e.target.value),children:[(0,T.jsx)(`option`,{value:``,children:`Choose a destination…`}),ne.map(e=>(0,T.jsx)(`option`,{value:e,children:e},e))]})})]}),(0,T.jsxs)(`div`,{className:`grid2`,children:[(0,T.jsx)(i,{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,T.jsx)(`input`,{id:`x-path`,value:P,spellCheck:!1,onChange:e=>F(e.target.value)})}),(0,T.jsx)(i,{id:`x-format`,label:`Format`,children:(0,T.jsx)(`select`,{id:`x-format`,value:M,onChange:e=>Q(e.target.value),children:E.map(e=>(0,T.jsx)(`option`,{value:e,children:e},e))})})]}),(0,T.jsx)(i,{id:`x-params`,label:`Parameters`,hint:`JSON, e.g. {"region": "EU"}. Optional.`,children:(0,T.jsx)(`input`,{id:`x-params`,className:`mono`,value:I,spellCheck:!1,onChange:e=>L(e.target.value)})}),(0,T.jsxs)(`label`,{className:`switch`,children:[(0,T.jsx)(`input`,{id:`x-incremental`,type:`checkbox`,checked:R,onChange:e=>z(e.target.checked)}),`Incremental`,(0,T.jsx)(`span`,{className:`hint`,children:`— each run delivers only what changed since the last one`})]}),R&&(0,T.jsxs)(`div`,{className:`grid2`,style:{gridTemplateColumns:`1fr 1fr 1fr`},children:[(0,T.jsx)(i,{id:`x-column`,label:`Column`,hint:`Its largest value delivered is remembered, e.g. updated_at.`,children:(0,T.jsx)(`input`,{id:`x-column`,value:B,spellCheck:!1,onChange:e=>V(e.target.value)})}),(0,T.jsx)(i,{id:`x-parameter`,label:`Parameter`,hint:`Bound to it next run: WHERE updated_at > :since.`,children:(0,T.jsx)(`input`,{id:`x-parameter`,value:H,spellCheck:!1,onChange:e=>U(e.target.value)})}),(0,T.jsx)(i,{id:`x-start`,label:`Start`,hint:`Bound on the first run.`,children:(0,T.jsx)(`input`,{id:`x-start`,value:W,spellCheck:!1,onChange:e=>G(e.target.value)})})]}),(0,T.jsx)(_,{id:`x-mask`,rules:J,onChange:Y,who:`this export writes`}),(0,T.jsxs)(`label`,{className:`switch`,children:[(0,T.jsx)(`input`,{id:`x-skip-empty`,type:`checkbox`,checked:K,onChange:e=>q(e.target.checked)}),`Skip empty runs`,(0,T.jsx)(`span`,{className:`hint`,children:`— a run with no rows writes no file`})]}),(0,T.jsx)(a,{onCancel:u,children:(0,T.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:$.isPending,children:c?`Save`:`Create`})})]})}export{D as ExportsPage};
|
|
1
|
+
import{t as e}from"./useMutation-BCy3Br67.js";import{D as t,E as n,I as r,M as i,N as a,O as o,P as s,Q as c,S as l,V as u,X as d,Y as f,Z as p,c as m,o as h}from"./index-D0zbyWWM.js";import{t as g}from"./Time-zMnS1kSm.js";import{n as _,t as v}from"./maskRules-Cw-l_UG8.js";import{n as y,t as b}from"./icons-B_wbfO4Z.js";import{n as x,r as S,t as C}from"./shared-DygTbsGs.js";var w=c(p(),1),T=d(),E=[`parquet`,`csv`,`ndjson`];function D(){let i=x(),a=m(),{openDrawer:o,toast:c}=r(),u=S(),d=f(),p=a(`exports.write`),h=()=>o({title:`New export`,wide:!0,content:(0,T.jsx)(k,{})}),g=e({mutationFn:async e=>n(await l.POST(`/api/v1/exports/{name}/runs`,{params:{path:{name:e}}})),onSuccess:e=>{c(e.object?`✓ ${e.rows} ${e.rows===1?`row`:`rows`} written to ${e.object}`:`✓ No new rows - nothing written`),d.invalidateQueries({queryKey:[`exports`]})},onError:e=>{u(e),d.invalidateQueries({queryKey:[`exports`]})}}),_=async e=>{if(window.confirm(`Delete export '${e}'? Files it already wrote stay where they are.`))try{t(await l.DELETE(`/api/v1/exports/{name}`,{params:{path:{name:e}}})),c(`Deleted `+e),d.invalidateQueries({queryKey:[`exports`]})}catch(e){u(e)}},v;return v=i.isPending?(0,T.jsx)(s,{text:`Loading exports…`}):i.isError?(0,T.jsx)(`div`,{className:`hint`,children:i.error.message}):i.data.length?(0,T.jsx)(`div`,{style:{overflowX:`auto`},children:(0,T.jsxs)(`table`,{className:`grid`,children:[(0,T.jsx)(`thead`,{children:(0,T.jsx)(`tr`,{children:[`Name`,`Query`,`Writes to`,`Format`,`Watermark`,``].map((e,t)=>(0,T.jsx)(`th`,{children:e},t))})}),(0,T.jsx)(`tbody`,{children:i.data.map(e=>(0,T.jsxs)(`tr`,{"data-name":e.name,children:[(0,T.jsx)(`td`,{children:(0,T.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,T.jsx)(`span`,{className:`name`,children:e.name}),e.running&&(0,T.jsx)(`span`,{className:`tag`,children:`running`})]})}),(0,T.jsx)(`td`,{className:`mono`,children:e.query}),(0,T.jsxs)(`td`,{className:`mono`,children:[e.destination,`:`,e.path]}),(0,T.jsx)(`td`,{children:e.format}),(0,T.jsx)(`td`,{className:e.incremental?`mono`:`dim`,children:e.incremental?`${e.incremental.column} > ${e.watermark??e.incremental.start??`(start)`}`:`everything, each run`}),(0,T.jsx)(`td`,{children:(0,T.jsxs)(`div`,{className:`actions`,children:[a(`exports.run`)&&(0,T.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,T.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>o({title:`Runs`,kicker:e.name,wide:!0,content:(0,T.jsx)(O,{name:e.name})}),children:`Runs`}),p&&(0,T.jsxs)(T.Fragment,{children:[(0,T.jsx)(`button`,{type:`button`,className:`btn ghost sm icon`,title:`Edit ${e.name}`,"aria-label":`Edit`,onClick:()=>o({title:`Edit export`,kicker:e.name,wide:!0,content:(0,T.jsx)(k,{name:e.name})}),children:(0,T.jsx)(b,{})}),(0,T.jsx)(`button`,{type:`button`,className:`btn ghost sm icon danger`,title:`Delete ${e.name}`,"aria-label":`Delete`,onClick:()=>void _(e.name),children:(0,T.jsx)(y,{})})]})]})})]},e.name))})]})}):(0,T.jsxs)(`div`,{className:`empty`,children:[(0,T.jsx)(`strong`,{children:`No exports yet`}),(0,T.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.`}),p&&(0,T.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:h,children:`New export`})]}),(0,T.jsxs)(T.Fragment,{children:[(0,T.jsxs)(`div`,{className:`page-head`,children:[(0,T.jsxs)(`div`,{className:`titles`,children:[(0,T.jsx)(`h1`,{children:`Exports`}),(0,T.jsx)(`span`,{className:`sub`,children:`Saved queries delivered as files to a bucket or a folder.`})]}),(0,T.jsx)(`span`,{className:`spacer`}),p&&(0,T.jsx)(`button`,{id:`new-export`,type:`button`,className:`btn primary`,onClick:h,children:`New export`})]}),(0,T.jsx)(`div`,{id:`exports-table`,className:`panel`,children:v})]})}function O({name:e}){let t=u({queryKey:[`exports`,e,`runs`],queryFn:async()=>n(await l.GET(`/api/v1/exports/{name}/runs`,{params:{path:{name:e}}})).items,retry:!1});return t.isPending?(0,T.jsx)(s,{text:`Loading runs…`}):t.isError?(0,T.jsx)(`div`,{className:`hint`,children:t.error.message}):t.data.length?(0,T.jsxs)(`table`,{className:`grid`,id:`export-runs`,children:[(0,T.jsx)(`thead`,{children:(0,T.jsx)(`tr`,{children:[`When`,`Status`,`Rows`,`Written`,`By`].map(e=>(0,T.jsx)(`th`,{children:e},e))})}),(0,T.jsx)(`tbody`,{children:t.data.map(e=>(0,T.jsxs)(`tr`,{children:[(0,T.jsx)(`td`,{className:`mono`,style:{whiteSpace:`nowrap`},children:(0,T.jsx)(g,{value:e.executed_at})}),(0,T.jsxs)(`td`,{children:[(0,T.jsx)(`span`,{className:`dot ${e.status===`success`?`ok`:`bad`}`}),` `,e.status]}),(0,T.jsx)(`td`,{className:`mono`,children:e.rows??`—`}),(0,T.jsx)(`td`,{className:e.object?`mono`:`dim`,children:e.status===`error`?e.error:e.object??`nothing - no new rows`}),(0,T.jsx)(`td`,{className:`dim`,children:e.key_name??`—`})]},String(e.run_id??e.executed_at)))})]}):(0,T.jsx)(`div`,{className:`hint`,children:`No runs yet.`})}function k({name:e}){let t=u({queryKey:[`exports`,`detail`,e],queryFn:async()=>o(await l.GET(`/api/v1/exports/{name}`,{params:{path:{name:e}}})),enabled:!!e,retry:!1,gcTime:0});return e&&t.isPending?(0,T.jsx)(s,{}):e&&t.isError?(0,T.jsx)(`div`,{className:`hint`,children:t.error.message}):(0,T.jsx)(A,{name:e,existing:t.data?.data,etag:t.data?.etag??null},e??``)}function A({name:t,existing:o,etag:s}){let c=!!t,{closeDrawer:u,showError:d,toast:p}=r(),m=S(),g=f(),y=h(),b=C(),[x,D]=(0,w.useState)(t??``),[O,k]=(0,w.useState)(o?.query??``),[A,j]=(0,w.useState)(o?.destination??``),[M,N]=(0,w.useState)(o?.format??`parquet`),[P,F]=(0,w.useState)(o?.path??`{name}/{date}/{name}_{run}.parquet`),[I,L]=(0,w.useState)(o?.params&&Object.keys(o.params).length?JSON.stringify(o.params):``),[R,z]=(0,w.useState)(!!o?.incremental),[B,V]=(0,w.useState)(o?.incremental?.column??``),[H,U]=(0,w.useState)(o?.incremental?.parameter??``),[W,G]=(0,w.useState)(String(o?.incremental?.start??``)),[K,q]=(0,w.useState)(!o||o.skip_empty),[J,Y]=(0,w.useState)((o?.mask??[]).map(e=>({column:e.column,action:e.action}))),[X,Z]=(0,w.useState)(o?.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 r={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 n(c?await l.PATCH(`/api/v1/exports/{name}`,{params:{path:{name:t},header:s?{"If-Match":s}:{}},body:r}):await l.POST(`/api/v1/exports`,{body:{name:x.trim(),...r}}))},onSuccess:e=>{d(``),g.invalidateQueries({queryKey:[`exports`]}),u(),p((c?`Updated `:`Created `)+e.name)},onError:m}),ee=e=>{if(e.preventDefault(),!c&&!x.trim()){d(`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,T.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:ee,children:[(0,T.jsxs)(`div`,{className:`grid2`,children:[(0,T.jsx)(i,{id:`x-name`,label:`Name`,children:(0,T.jsx)(`input`,{id:`x-name`,value:x,placeholder:`acme-daily-orders`,autoComplete:`off`,spellCheck:!1,disabled:c,autoFocus:!c,onChange:e=>D(e.target.value)})}),(0,T.jsx)(i,{id:`x-description`,label:`Description`,hint:`Optional.`,children:(0,T.jsx)(`input`,{id:`x-description`,value:X,onChange:e=>Z(e.target.value)})})]}),(0,T.jsxs)(`div`,{className:`grid2`,children:[(0,T.jsx)(i,{id:`x-query`,label:`Saved query`,hint:`Its published version runs.`,children:(0,T.jsxs)(`select`,{id:`x-query`,value:O,onChange:e=>k(e.target.value),children:[(0,T.jsx)(`option`,{value:``,children:`Choose a query…`}),te.map(e=>(0,T.jsx)(`option`,{value:e,children:e},e))]})}),(0,T.jsx)(i,{id:`x-destination`,label:`Destination`,children:(0,T.jsxs)(`select`,{id:`x-destination`,value:A,onChange:e=>j(e.target.value),children:[(0,T.jsx)(`option`,{value:``,children:`Choose a destination…`}),ne.map(e=>(0,T.jsx)(`option`,{value:e,children:e},e))]})})]}),(0,T.jsxs)(`div`,{className:`grid2`,children:[(0,T.jsx)(i,{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,T.jsx)(`input`,{id:`x-path`,value:P,spellCheck:!1,onChange:e=>F(e.target.value)})}),(0,T.jsx)(i,{id:`x-format`,label:`Format`,children:(0,T.jsx)(`select`,{id:`x-format`,value:M,onChange:e=>Q(e.target.value),children:E.map(e=>(0,T.jsx)(`option`,{value:e,children:e},e))})})]}),(0,T.jsx)(i,{id:`x-params`,label:`Parameters`,hint:`JSON, e.g. {"region": "EU"}. Optional.`,children:(0,T.jsx)(`input`,{id:`x-params`,className:`mono`,value:I,spellCheck:!1,onChange:e=>L(e.target.value)})}),(0,T.jsxs)(`label`,{className:`switch`,children:[(0,T.jsx)(`input`,{id:`x-incremental`,type:`checkbox`,checked:R,onChange:e=>z(e.target.checked)}),`Incremental`,(0,T.jsx)(`span`,{className:`hint`,children:`— each run delivers only what changed since the last one`})]}),R&&(0,T.jsxs)(`div`,{className:`grid2`,style:{gridTemplateColumns:`1fr 1fr 1fr`},children:[(0,T.jsx)(i,{id:`x-column`,label:`Column`,hint:`Its largest value delivered is remembered, e.g. updated_at.`,children:(0,T.jsx)(`input`,{id:`x-column`,value:B,spellCheck:!1,onChange:e=>V(e.target.value)})}),(0,T.jsx)(i,{id:`x-parameter`,label:`Parameter`,hint:`Bound to it next run: WHERE updated_at > :since.`,children:(0,T.jsx)(`input`,{id:`x-parameter`,value:H,spellCheck:!1,onChange:e=>U(e.target.value)})}),(0,T.jsx)(i,{id:`x-start`,label:`Start`,hint:`Bound on the first run.`,children:(0,T.jsx)(`input`,{id:`x-start`,value:W,spellCheck:!1,onChange:e=>G(e.target.value)})})]}),(0,T.jsx)(_,{id:`x-mask`,rules:J,onChange:Y,who:`this export writes`}),(0,T.jsxs)(`label`,{className:`switch`,children:[(0,T.jsx)(`input`,{id:`x-skip-empty`,type:`checkbox`,checked:K,onChange:e=>q(e.target.checked)}),`Skip empty runs`,(0,T.jsx)(`span`,{className:`hint`,children:`— a run with no rows writes no file`})]}),(0,T.jsx)(a,{onCancel:u,children:(0,T.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:$.isPending,children:c?`Save`:`Create`})})]})}export{D as ExportsPage};
|