queryapigate 0.40.0__tar.gz → 0.41.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.40.0/queryapigate.egg-info → queryapigate-0.41.0}/PKG-INFO +4 -2
- {queryapigate-0.40.0 → queryapigate-0.41.0}/pyproject.toml +2 -1
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/__init__.py +1 -1
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/config.py +4 -1
- queryapigate-0.41.0/queryapigate/console_dist/assets/AccessMapPage-BchGwtBA.js +2 -0
- queryapigate-0.40.0/queryapigate/console_dist/assets/AdministratorsPage-tRdiDtwP.js → queryapigate-0.41.0/queryapigate/console_dist/assets/AdministratorsPage-POqoMkbx.js +1 -1
- queryapigate-0.40.0/queryapigate/console_dist/assets/AgentsPage-BXB_zpeV.js → queryapigate-0.41.0/queryapigate/console_dist/assets/AgentsPage-M9N0xdeE.js +1 -1
- queryapigate-0.40.0/queryapigate/console_dist/assets/AlertsPage-BQ9p_Dq6.js → queryapigate-0.41.0/queryapigate/console_dist/assets/AlertsPage-XaMYNy94.js +1 -1
- queryapigate-0.41.0/queryapigate/console_dist/assets/ApiKeyDetailPage-CuWfrg7s.js +1 -0
- queryapigate-0.41.0/queryapigate/console_dist/assets/ApiKeysPage-4Tn9wAVl.js +1 -0
- queryapigate-0.41.0/queryapigate/console_dist/assets/ApprovalsPage-BpG0_mCJ.js +1 -0
- queryapigate-0.40.0/queryapigate/console_dist/assets/AuditLogPage-CY7hvnbo.js → queryapigate-0.41.0/queryapigate/console_dist/assets/AuditLogPage-sV_If6-v.js +1 -1
- queryapigate-0.41.0/queryapigate/console_dist/assets/CachingPage-6sAn-XyO.js +1 -0
- queryapigate-0.40.0/queryapigate/console_dist/assets/CodeBox-DYcLv1L2.js → queryapigate-0.41.0/queryapigate/console_dist/assets/CodeBox-Cwh5KHLp.js +1 -1
- queryapigate-0.41.0/queryapigate/console_dist/assets/ConnectionsPage-DO4N8S24.js +4 -0
- queryapigate-0.40.0/queryapigate/console_dist/assets/DesignerPage-BSJIQmK3.js → queryapigate-0.41.0/queryapigate/console_dist/assets/DesignerPage-C8Y-IxVI.js +3 -3
- queryapigate-0.40.0/queryapigate/console_dist/assets/DestinationsPage-BSXcub5N.js → queryapigate-0.41.0/queryapigate/console_dist/assets/DestinationsPage-CW-XmEqb.js +1 -1
- queryapigate-0.40.0/queryapigate/console_dist/assets/ExportsPage-DGEmu0q6.js → queryapigate-0.41.0/queryapigate/console_dist/assets/ExportsPage-DxroTrQZ.js +1 -1
- queryapigate-0.40.0/queryapigate/console_dist/assets/HelpPage-C6zb288K.js → queryapigate-0.41.0/queryapigate/console_dist/assets/HelpPage-CvwlMOvX.js +1 -1
- queryapigate-0.40.0/queryapigate/console_dist/assets/HomePage-DfzPo4Rd.js → queryapigate-0.41.0/queryapigate/console_dist/assets/HomePage-BEg_rfvB.js +3 -3
- queryapigate-0.41.0/queryapigate/console_dist/assets/KeyActivity-EO98IG9y.js +1 -0
- queryapigate-0.40.0/queryapigate/console_dist/assets/Managed-BIEk4csv.js → queryapigate-0.41.0/queryapigate/console_dist/assets/Managed-BwU65gDK.js +1 -1
- queryapigate-0.41.0/queryapigate/console_dist/assets/McpPage-upBu1Zmx.js +8 -0
- queryapigate-0.40.0/queryapigate/console_dist/assets/MetricsPage-DLxBzyME.js → queryapigate-0.41.0/queryapigate/console_dist/assets/MetricsPage-DO9gA7sY.js +1 -1
- queryapigate-0.41.0/queryapigate/console_dist/assets/QueryInfo-F9F7if8P.js +2 -0
- queryapigate-0.41.0/queryapigate/console_dist/assets/RepositoryPage-gSUZcW25.js +4 -0
- queryapigate-0.40.0/queryapigate/console_dist/assets/Results-CSkpK-aU.js → queryapigate-0.41.0/queryapigate/console_dist/assets/Results-3iK2cKFL.js +1 -1
- queryapigate-0.41.0/queryapigate/console_dist/assets/RolesPage-BNpqKDS6.js +1 -0
- queryapigate-0.41.0/queryapigate/console_dist/assets/SchemaBrowser-CAIraoYm.js +13 -0
- queryapigate-0.40.0/queryapigate/console_dist/assets/SettingsPage-zY-iEam4.js → queryapigate-0.41.0/queryapigate/console_dist/assets/SettingsPage-B2ZGXWK4.js +1 -1
- queryapigate-0.40.0/queryapigate/console_dist/assets/Time-BJ30-QTG.js → queryapigate-0.41.0/queryapigate/console_dist/assets/Time-Bbp6q-9f.js +1 -1
- queryapigate-0.40.0/queryapigate/console_dist/assets/WorkspacesPage-V8eNKfjx.js → queryapigate-0.41.0/queryapigate/console_dist/assets/WorkspacesPage-u6taj7Y0.js +1 -1
- queryapigate-0.41.0/queryapigate/console_dist/assets/api-Dc-2iLvt.js +1 -0
- queryapigate-0.40.0/queryapigate/console_dist/assets/forms-_JA3mZYi.js → queryapigate-0.41.0/queryapigate/console_dist/assets/forms--qfP4qjO.js +2 -2
- queryapigate-0.40.0/queryapigate/console_dist/assets/forms-BRvatu9L.js → queryapigate-0.41.0/queryapigate/console_dist/assets/forms-CfcbrGmf.js +1 -1
- queryapigate-0.40.0/queryapigate/console_dist/assets/index-BKsSejEw.js → queryapigate-0.41.0/queryapigate/console_dist/assets/index--QHzN3S_.js +2 -2
- queryapigate-0.41.0/queryapigate/console_dist/assets/index-Cv64XKNs.css +1 -0
- queryapigate-0.40.0/queryapigate/console_dist/assets/shared-4Q-pesYm.js → queryapigate-0.41.0/queryapigate/console_dist/assets/shared-CnZhEfLp.js +1 -1
- queryapigate-0.40.0/queryapigate/console_dist/assets/useMutation-C3jit8ca.js → queryapigate-0.41.0/queryapigate/console_dist/assets/useMutation-DQTq-cNw.js +1 -1
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/console_dist/index.html +2 -2
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/databases.py +3 -3
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/engine.py +16 -3
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/experimental.py +6 -5
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/masking.py +1 -1
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/runners.py +76 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/schema.py +32 -5
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/sqlflow.py +1 -1
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/sqltools.py +11 -4
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/tableguard.py +48 -17
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/tracing.py +2 -1
- {queryapigate-0.40.0 → queryapigate-0.41.0/queryapigate.egg-info}/PKG-INFO +4 -2
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate.egg-info/SOURCES.txt +35 -31
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate.egg-info/requires.txt +4 -1
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_experimental.py +1 -1
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_integration.py +30 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_tableguard.py +59 -0
- queryapigate-0.40.0/queryapigate/console_dist/assets/AccessMapPage-DfbWk8Gw.js +0 -3
- queryapigate-0.40.0/queryapigate/console_dist/assets/ApiKeysPage-BvdNAcrr.js +0 -1
- queryapigate-0.40.0/queryapigate/console_dist/assets/ApprovalsPage-BrBvohc8.js +0 -1
- queryapigate-0.40.0/queryapigate/console_dist/assets/CachingPage-CPUe1LnT.js +0 -1
- queryapigate-0.40.0/queryapigate/console_dist/assets/ConnectionsPage-B1yREx7-.js +0 -4
- queryapigate-0.40.0/queryapigate/console_dist/assets/McpPage-Bsv20Cbz.js +0 -8
- queryapigate-0.40.0/queryapigate/console_dist/assets/RepositoryPage-CJsIhYyZ.js +0 -4
- queryapigate-0.40.0/queryapigate/console_dist/assets/RolesPage-GedXTVCu.js +0 -1
- queryapigate-0.40.0/queryapigate/console_dist/assets/SchemaBrowser-b0q_hy3W.js +0 -13
- queryapigate-0.40.0/queryapigate/console_dist/assets/index-CLKlS9mk.css +0 -1
- {queryapigate-0.40.0 → queryapigate-0.41.0}/LICENSE +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/NOTICE +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/README.md +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/__main__.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/accesslog.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/adminroles.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/admins.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/agentwrites.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/alerts.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/apikeys.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/app.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/auditchain.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/auditstream.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/aws.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/broadcast.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/bundle.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/cache.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/cli.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/collection_admin.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/configcode.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/console.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/console_dist/assets/Experimental-B3dnaeV7.js +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/console_dist/assets/StatTile-Cft4kJPH.js +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/console_dist/assets/client-ZHNiMG8H.js +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/console_dist/assets/icons-CFXE8hAz.js +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/console_dist/assets/maskRules-CvKQIPgN.js +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/console_dist/assets/reach-BAOLd75Y.js +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/console_dist/assets/state-O13cl7cO.js +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/console_dist/favicon.svg +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/cors.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/db.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/definitions.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/deprecations.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/descriptions.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/destinations.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/duckfiles.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/embeddings.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/errors.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/events.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/examples.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/exporting.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/exports.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/formats.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/governance.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/gunicorn_conf.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/history.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/instances.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/jwtauth.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/lib/h2-2.2.224.jar +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/limits.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/logging_setup.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/mcp_server.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/metrics.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/mongodocs.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/mongotools.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/oidc.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/openapi.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/params.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/parquet.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/pool.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/postman.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/quotas.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/ratelimit.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/rediscache.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/rekey.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/scope.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/secheaders.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/secretrefs.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/services/__init__.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/services/access.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/services/administrators.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/services/audit.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/services/collections.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/services/connections.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/services/descriptions.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/services/destinations.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/services/exports.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/services/mcp.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/services/queries.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/services/workspaces.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/sso.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/store.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/usage.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/v1.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/v1_spec.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/vault.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate/workspaces.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate.egg-info/dependency_links.txt +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate.egg-info/entry_points.txt +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/queryapigate.egg-info/top_level.txt +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/setup.cfg +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_adhoc_history.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_adminroles.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_admins.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_agent_writes.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_alerts.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_api_administrators.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_api_keys.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_api_v1.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_audit_chain.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_audit_log.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_audit_siem.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_audit_stream.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_aws.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_backup_restore.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_broadcast.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_cli.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_collection_bundle.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_collection_postman.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_collections.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_configcode.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_connection_encryption.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_console.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_cors_and_rate_limit.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_data_protection.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_databases.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_db.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_dependency_floors.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_deprecations.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_descriptions.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_destinations.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_docs_site.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_duckdb.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_duckdb_files.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_embeddings.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_errors.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_events.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_events_server.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_examples.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_export_security.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_exporting.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_exports.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_fips.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_helm.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_history.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_howto.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_instances.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_jdbc.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_jwt.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_key_rotation.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_limits.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_masking.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_masking_requests.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_mcp.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_mcp_keycloak.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_mcp_oauth.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_mongo_streaming.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_mongotools.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_multinode.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_observability.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_oidc.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_params.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_parquet.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_pool.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_publishing.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_query_window.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_queryapigate.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_quotas.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_rekey.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_release_check.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_response_cache.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_roles.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_rolling_upgrade.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_secretrefs.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_security_headers.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_settings.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_shared_rate_limit.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_sql_guard_fuzz.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_sql_guard_writes.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_sqlflow.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_sso.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_sso_keycloak.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_supply_chain.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_tracing.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_upgrades.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_usage.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_vault.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_workspace_config_cli.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_workspace_isolation.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_workspace_observability.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_workspace_store.py +0 -0
- {queryapigate-0.40.0 → queryapigate-0.41.0}/tests/test_workspaces_api.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: queryapigate
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.41.0
|
|
4
4
|
Summary: Turn SQL into governed REST APIs over MySQL, PostgreSQL, ClickHouse, SQLite, DuckDB, H2, MongoDB, and Parquet/CSV/JSON files - JSON, CSV, Parquet or Excel out, with scoped keys, an admin Console and MCP for AI agents.
|
|
5
5
|
Author-email: Anantha Raju C <arcswdev@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -43,6 +43,8 @@ Provides-Extra: duckdb
|
|
|
43
43
|
Requires-Dist: duckdb>=1.2; extra == "duckdb"
|
|
44
44
|
Provides-Extra: mongo
|
|
45
45
|
Requires-Dist: pymongo>=4.0; extra == "mongo"
|
|
46
|
+
Provides-Extra: trino
|
|
47
|
+
Requires-Dist: trino>=0.330; extra == "trino"
|
|
46
48
|
Provides-Extra: flow
|
|
47
49
|
Requires-Dist: sqlglot<26,>=25.2; extra == "flow"
|
|
48
50
|
Provides-Extra: encryption
|
|
@@ -54,7 +56,7 @@ Requires-Dist: mcp<2,>=1.21.1; extra == "mcp"
|
|
|
54
56
|
Provides-Extra: jwt
|
|
55
57
|
Requires-Dist: PyJWT[crypto]>=2.14; extra == "jwt"
|
|
56
58
|
Provides-Extra: all
|
|
57
|
-
Requires-Dist: queryapigate[clickhouse,duckdb,encryption,flow,h2,jwt,mongo,mysql,postgres]; extra == "all"
|
|
59
|
+
Requires-Dist: queryapigate[clickhouse,duckdb,encryption,flow,h2,jwt,mongo,mysql,postgres,trino]; extra == "all"
|
|
58
60
|
Provides-Extra: otel
|
|
59
61
|
Requires-Dist: opentelemetry-sdk>=1.27; extra == "otel"
|
|
60
62
|
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.27; extra == "otel"
|
|
@@ -37,6 +37,7 @@ clickhouse = ["clickhouse-driver>=0.2"]
|
|
|
37
37
|
h2 = ["JayDeBeApi>=1.2", "JPype1>=1.4"] # also needs a Java runtime
|
|
38
38
|
duckdb = ["duckdb>=1.2"] # allowed_directories/allowed_paths, which lock every connection (duckfiles.py), arrived in 1.2
|
|
39
39
|
mongo = ["pymongo>=4.0"]
|
|
40
|
+
trino = ["trino>=0.330"] # the trino DB-API client (runners._Trino); experimental, ADR 0026
|
|
40
41
|
flow = ["sqlglot>=25.2,<26"] # the Access tab's tables and joins (sqlflow.py) and column masking's tracing (masking.py)
|
|
41
42
|
encryption = ["cryptography>=41"] # only needed when QUERYAPIGATE_SECRET_KEY is set - see store.py's _get_fernet()
|
|
42
43
|
redis = ["redis>=5"] # only needed when QUERYAPIGATE_REDIS_URL is set - see rediscache.py
|
|
@@ -46,7 +47,7 @@ mcp = ["mcp>=1.21.1,<2"] # only needed for `queryapigate mcp` - see mcp_server.
|
|
|
46
47
|
jwt = ["PyJWT[crypto]>=2.14"] # only needed when QUERYAPIGATE_JWT_SECRET or QUERYAPIGATE_JWT_JWKS_URL is set - see jwtauth.py.
|
|
47
48
|
# 2.14 is the floor because that is where PyJWKClient stops refetching the JWKS for
|
|
48
49
|
# every unknown key id: before it, forged tokens could make the server hammer the provider.
|
|
49
|
-
all = ["queryapigate[mysql,postgres,clickhouse,h2,duckdb,mongo,flow,encryption,jwt]"]
|
|
50
|
+
all = ["queryapigate[mysql,postgres,clickhouse,h2,duckdb,mongo,trino,flow,encryption,jwt]"]
|
|
50
51
|
# only needed when an OTLP endpoint is set (OTEL_EXPORTER_OTLP_ENDPOINT) - see tracing.py
|
|
51
52
|
otel = ["opentelemetry-sdk>=1.27", "opentelemetry-exporter-otlp-proto-http>=1.27"]
|
|
52
53
|
server = ["gunicorn>=21"]
|
|
@@ -9,7 +9,7 @@ from pathlib import Path
|
|
|
9
9
|
|
|
10
10
|
from . import experimental
|
|
11
11
|
|
|
12
|
-
SUPPORTED_DB_TYPES = ('mysql', 'postgres', 'clickhouse', 'sqlite', 'h2', 'jdbc', 'duckdb', 'mongo')
|
|
12
|
+
SUPPORTED_DB_TYPES = ('mysql', 'postgres', 'clickhouse', 'sqlite', 'h2', 'jdbc', 'duckdb', 'mongo', 'trino')
|
|
13
13
|
PASSWORD_MASK = '********'
|
|
14
14
|
CONNECT_TIMEOUT = 10 # seconds
|
|
15
15
|
HISTORY_LIMIT = 50 # executions remembered per saved-query version (QUERYAPIGATE_HISTORY_LIMIT)
|
|
@@ -48,6 +48,9 @@ EXAMPLE_CONNECTIONS = {
|
|
|
48
48
|
'user': 'default', 'password': '', 'active': False},
|
|
49
49
|
'example-h2': {'db': 'h2', 'host': 'localhost', 'database': 'test', 'user': 'SA', 'password': '',
|
|
50
50
|
'active': False},
|
|
51
|
+
'example-trino': {'db': 'trino', 'host': 'localhost', 'port': 8080, 'database': 'tpch',
|
|
52
|
+
'user': 'queryapigate', 'options': {'schema': 'tiny', 'http_scheme': 'http'},
|
|
53
|
+
'active': False},
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/forms--qfP4qjO.js","assets/client-ZHNiMG8H.js","assets/index--QHzN3S_.js","assets/index-Cv64XKNs.css","assets/CodeBox-Cwh5KHLp.js","assets/api-Dc-2iLvt.js","assets/useMutation-DQTq-cNw.js","assets/maskRules-CvKQIPgN.js","assets/SchemaBrowser-CAIraoYm.js","assets/reach-BAOLd75Y.js"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{B as e,H as t,z as n}from"./client-ZHNiMG8H.js";import{D as r,M as i,P as a,c as o,d as s,g as c,k as l,s as u}from"./index--QHzN3S_.js";import{t as d}from"./Time-Bbp6q-9f.js";import{t as f}from"./QueryInfo-F9F7if8P.js";import{a as p}from"./reach-BAOLd75Y.js";import{i as m,n as h,o as g,r as _}from"./SchemaBrowser-CAIraoYm.js";var v=t(e(),1),y=n(),b=(0,v.lazy)(()=>a(()=>import(`./forms--qfP4qjO.js`).then(e=>({default:e.QueryForm})),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9]))),x={query:`Q`,collection:`C`,connection:`W`},S=[`Database`,`Created`,`Last modified`,`Last used`,`Version`],C=[`database`,`created`,`modified`,`lastUsed`,`version`],w=()=>new Date().toISOString().slice(0,10);function T(){let e=i(),{openDrawer:t}=l(),n=u(),a=o(),d=c(),x=s(),[S,C]=(0,v.useState)(m),w=e=>{let t={...S,...e};g(t),C(t)},T=x.data??[],D=e=>T.find(t=>t.name===e)?.db??null,O=h(S.conn||void 0),k=O.data?.tables??[],A=_(S.conn||void 0,D(S.conn),S.table&&!S.tableMatches?[S.table]:[]),j=S.table?S.tableMatches??(A.data?A.data[S.table]??[]:null):null,M=n.data??[],N=[...new Set(M.map(e=>D(e.connection_name)).filter(e=>!!e))].sort(),P=a.data??{},F=d.data??{},I=Object.keys(P).sort(),L=Object.keys(F).sort(),R,z=!0;S.conn?O.isPending?R=(0,y.jsx)(`option`,{value:``,children:`Loading tables…`}):O.isError?R=(0,y.jsx)(`option`,{value:``,children:`Schema unavailable`}):(z=!1,R=(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(`option`,{value:``,children:`All tables (${k.length})`}),k.map(e=>(0,y.jsx)(`option`,{value:e.name,children:`${e.name} · ${e.columns.length}${e.columns.length===1?` col`:` cols`}`},e.name))]})):R=(0,y.jsx)(`option`,{value:``,children:`Select a connection first`});let B=(e,t)=>p(e,t.name,t.collection,t.connection_name),V;if(n.isPending)V=(0,y.jsx)(r,{});else if(!M.length)V=(0,y.jsx)(`div`,{className:`empty`,children:(0,y.jsx)(`strong`,{children:`No saved queries yet`})});else if(!I.length&&!L.length)V=(0,y.jsxs)(`div`,{className:`empty`,children:[(0,y.jsx)(`strong`,{children:`No scoped API keys or roles yet`}),(0,y.jsx)(`span`,{children:`Every query is reachable by the admin key only until a scoped key is created.`})]});else{let n=S.q.trim().toLowerCase(),i=[...M].sort((e,t)=>(e.collection||``)+e.name<(t.collection||``)+t.name?-1:1),a=e=>e.toLowerCase().includes(n),o=n?i.filter(e=>a(e.name)||e.collection&&a(e.collection)):i,s=n?I.filter(a):I,c=n?L.filter(a):L,l=o.length||s.length||c.length,u=o.length?o:l?i:[],d=s.length?s:l?I:[],p=c.length?c:l?L:[];if(S.conn&&(u=u.filter(e=>e.connection_name===S.conn)),S.table&&j===null)V=(0,y.jsx)(r,{text:`Checking which queries reference ${S.table}…`});else if(j&&(u=u.filter(e=>j.includes(e.name))),u.length){let n=u.map(e=>({q:e,dbType:D(e.connection_name),reach:d.filter(t=>B(P[t],e).length).length}));S.db&&(n=n.filter(e=>e.dbType===S.db)),S.reach===`reachable`?n=n.filter(e=>e.reach>0):S.reach===`unreachable`&&(n=n.filter(e=>e.reach===0));let r=S.sort;if(r.field){let e=e=>{switch(r.field){case`query`:return e.q.name.toLowerCase();case`database`:return(e.dbType||``).toLowerCase();case`created`:return e.q.created_at||``;case`modified`:return e.q.updated_at||``;case`lastUsed`:return e.q.last_used_at||``;case`version`:return e.q.latest_version;case`connection`:return(e.q.connection_name||``).toLowerCase();case`reach`:return e.reach;case`col`:{let t=r.colType===`key`?P[r.colName]:F[r.colName];return t&&B(t,e.q).length?1:0}default:return``}};n=[...n].sort((t,n)=>{let i=e(t),a=e(n);return i<a?-r.dir:i>a?r.dir:0})}V=n.length?(0,y.jsx)(E,{rows:n,keyCols:d,roleCols:p,keyMap:P,roleMap:F,st:S,onSort:e=>w({sort:e}),onOpen:t=>e(`/queries/${encodeURIComponent(t)}`),onInfo:e=>t({title:e.q.name,kicker:`${e.q.collection||`uncollected`} · ${e.q.version_count}${e.q.version_count===1?` version`:` versions`}`,content:(0,y.jsx)(f,{name:e.q.name,dbType:e.dbType})})}):(0,y.jsx)(`div`,{className:`empty`,children:(0,y.jsx)(`span`,{children:`No queries match these filters.`})})}else{let i=n?`Nothing matches “${n}”.`:S.table?`“${S.table}” on ${S.conn} is not exposed by any saved query yet.`:S.conn?`No saved query uses “${S.conn}” yet.`:`Nothing matches these filters.`;V=(0,y.jsxs)(`div`,{className:`empty`,children:[(0,y.jsx)(`span`,{children:i}),!n&&S.conn?(0,y.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:()=>{let n=D(S.conn)===`mongo`,i=S.table?n?JSON.stringify({collection:S.table,filter:{}},null,2):`SELECT * FROM `+S.table:void 0;e(`/queries`),t({title:`New API`,kicker:`POST /api/v1/queries`,content:(0,y.jsx)(v.Suspense,{fallback:(0,y.jsx)(r,{}),children:(0,y.jsx)(b,{starter:{connection:S.conn,sql:i}})})})},children:`New API`+(S.table?` on `+S.table:``)}):null]})}}return(0,y.jsxs)(y.Fragment,{children:[(0,y.jsxs)(`div`,{className:`page-head`,children:[(0,y.jsxs)(`div`,{className:`titles`,children:[(0,y.jsx)(`h1`,{children:`Access map`}),(0,y.jsxs)(`span`,{className:`sub`,children:[`Which API keys can reach which saved queries - the whole point of collections and connection grants, in one place.`,` `,(0,y.jsxs)(`span`,{className:`legend`,children:[(0,y.jsx)(`span`,{className:`amap-dot`,children:`Q`}),` named query \xA0`,(0,y.jsx)(`span`,{className:`amap-dot`,children:`C`}),` `,`collection \xA0`,(0,y.jsx)(`span`,{className:`amap-dot conn`,children:`W`}),` whole connection`]})]})]}),(0,y.jsx)(`span`,{className:`spacer`}),(0,y.jsxs)(`select`,{id:`accessmap-conn-filter`,"aria-label":`Connection`,value:T.some(e=>e.name===S.conn)?S.conn:``,onChange:e=>w({conn:e.target.value,table:``,tableMatches:null}),children:[(0,y.jsx)(`option`,{value:``,children:`All connections`}),[...T].sort((e,t)=>e.name<t.name?-1:1).map(e=>(0,y.jsx)(`option`,{value:e.name,children:e.name},e.name))]}),(0,y.jsx)(`select`,{id:`accessmap-table-filter`,"aria-label":`Table`,disabled:z,value:k.some(e=>e.name===S.table)?S.table:``,onChange:e=>w({table:e.target.value,tableMatches:null}),children:R}),(0,y.jsxs)(`select`,{id:`accessmap-db-filter`,"aria-label":`Database`,value:N.includes(S.db)?S.db:``,onChange:e=>w({db:e.target.value}),children:[(0,y.jsx)(`option`,{value:``,children:`All databases`}),N.map(e=>(0,y.jsx)(`option`,{value:e,children:e},e))]}),(0,y.jsxs)(`select`,{id:`accessmap-reach-filter`,"aria-label":`Reach`,value:S.reach,onChange:e=>w({reach:e.target.value}),children:[(0,y.jsx)(`option`,{value:``,children:`Any reach`}),(0,y.jsx)(`option`,{value:`reachable`,children:`Reachable by a key`}),(0,y.jsx)(`option`,{value:`unreachable`,children:`Reachable by no key`})]}),(0,y.jsx)(`input`,{id:`accessmap-filter`,className:`search`,type:`search`,placeholder:`Filter by query or key…`,value:S.q,onChange:e=>w({q:e.target.value})})]}),(0,y.jsx)(`div`,{id:`accessmap-body`,className:`panel`,children:V})]})}function E({rows:e,keyCols:t,roleCols:n,keyMap:r,roleMap:i,st:a,onSort:o,onOpen:s,onInfo:c}){let l=a.sort,u=(e,t=null,n=null)=>l.field===e&&l.colType===t&&l.colName===n,f=(e,t=null,n=null)=>u(e,t,n)?l.dir===1?` ▲`:` ▼`:``,m=(e,t=null,n=null)=>{let r=u(e,t,n);o(r&&l.dir===1?{field:e,colType:t,colName:n,dir:-1}:r?{field:null,colType:null,colName:null,dir:1}:{field:e,colType:t,colName:n,dir:1})},h=(e,t,n)=>(0,y.jsx)(`th`,{className:t,onClick:()=>m(n),children:e+f(n)},n),g=(e,t,n,r)=>(0,y.jsxs)(`th`,{className:`amap-key-head amap-sortable`+(n?` revoked`:``),title:n?e+n:e,onClick:()=>m(`col`,r,e),children:[(0,y.jsxs)(`span`,{className:`amap-key-name`,children:[e,f(`col`,r,e),n?(0,y.jsx)(`span`,{className:`dim`,children:n}):null]}),(0,y.jsxs)(`span`,{className:`amap-key-sub`,children:[(t.allow_writes?`Read/write`:`Read-only`)+` · `+(t.rate_limit||`server default`),t.mask?.length?(0,y.jsx)(`span`,{className:`tag`,title:t.mask.map(e=>`${e.column}: ${e.action}`).join(`, `),children:`masked`}):null]})]},r+`:`+e),_=(e,t,n,r)=>{let i=p(e,t.name,t.collection,t.connection_name);if(!i.length)return(0,y.jsx)(`td`,{className:`amap-cell`},r);let a=[...new Set(i.map(e=>x[e.kind]))],o=a.length===1&&a[0]===`W`;return(0,y.jsx)(`td`,{className:`amap-cell`,title:i.map(e=>e.label).join(`, `),children:(0,y.jsx)(`span`,{className:`amap-dot`+(n?` role`:``)+(o?` conn`:``),children:a.join(`·`)})},r)},v=w();return(0,y.jsx)(`div`,{style:{overflow:`auto`},children:(0,y.jsxs)(`table`,{className:`amap`,children:[(0,y.jsxs)(`thead`,{children:[(0,y.jsxs)(`tr`,{children:[(0,y.jsx)(`th`,{colSpan:3+S.length}),t.length?(0,y.jsx)(`th`,{colSpan:t.length,className:`amap-group`,children:`API KEYS`}):null,n.length?(0,y.jsx)(`th`,{colSpan:n.length,className:`amap-group role`,title:`What a key created from each role would reach - a role grants nothing on its own`,children:`ROLES`}):null]}),(0,y.jsxs)(`tr`,{children:[h(`Query`,`amap-query amap-sortable`,`query`),C.map((e,t)=>h(S[t],`amap-sortable`,e)),h(`Connection`,`amap-sortable`,`connection`),h(`Reach`,`num amap-sortable`,`reach`),t.map(e=>{let t=r[e],n=!!(t.expires_at&&t.expires_at<v);return g(e,t,t.active?n?` (expired ${t.expires_at})`:``:` (revoked)`,`key`)}),n.map(e=>g(e,i[e],``,`role`))]})]}),(0,y.jsx)(`tbody`,{children:e.map(e=>{let a=e.q;return(0,y.jsxs)(`tr`,{children:[(0,y.jsxs)(`td`,{className:`amap-query`,children:[(0,y.jsxs)(`div`,{className:`amap-q-row`,children:[(0,y.jsx)(`button`,{type:`button`,className:`amap-q-name`,title:`Open ${a.name} in API Repository`,onClick:()=>s(a.name),children:a.name}),(0,y.jsx)(`button`,{type:`button`,className:`amap-info`,title:`Details for ${a.name}`,"aria-label":`Details for ${a.name}`,onClick:()=>c(e),children:`i`})]}),a.collection?(0,y.jsx)(`span`,{className:`amap-coll`,children:a.collection}):null]}),(0,y.jsx)(`td`,{children:e.dbType?(0,y.jsx)(`span`,{className:`tag`,children:e.dbType}):(0,y.jsx)(`span`,{className:`dim`,children:`—`})}),(0,y.jsx)(`td`,{className:`mono dim`,children:(0,y.jsx)(d,{value:a.created_at})}),(0,y.jsx)(`td`,{className:`mono dim`,children:(0,y.jsx)(d,{value:a.updated_at})}),(0,y.jsx)(`td`,{className:a.last_used_at?`mono`:`mono dim`,children:(0,y.jsx)(d,{value:a.last_used_at,fallback:`never`})}),(0,y.jsx)(`td`,{className:`mono`,children:`v`+a.latest_version}),(0,y.jsx)(`td`,{className:`mono dim`,children:a.connection_name||`—`}),(0,y.jsx)(`td`,{className:`num`,title:`Reachable by an actual API key (roles alone reach nothing)`,children:e.reach?String(e.reach):`—`}),t.map(e=>_(r[e],a,!1,`k:`+e)),n.map(e=>_(i[e],a,!0,`r:`+e))]},a.name)})}),(0,y.jsx)(`tfoot`,{children:(0,y.jsxs)(`tr`,{children:[(0,y.jsx)(`td`,{className:`amap-query`,children:`Reaches`}),S.map(e=>(0,y.jsx)(`td`,{},e)),(0,y.jsx)(`td`,{}),(0,y.jsx)(`td`,{}),t.map(t=>(0,y.jsx)(`td`,{className:`num`,children:`${e.filter(e=>p(r[t],e.q.name,e.q.collection,e.q.connection_name).length).length} / ${e.length}`},t)),n.map(t=>(0,y.jsx)(`td`,{className:`num`,children:`${e.filter(e=>p(i[t],e.q.name,e.q.collection,e.q.connection_name).length).length} / ${e.length}`},t))]})})]})})}export{T as AccessMapPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{B as e,H as t,R as n,c as r,h as i,l as a,n as o,s,t as c,v as l,z as u}from"./client-ZHNiMG8H.js";import{t as d}from"./useMutation-C3jit8ca.js";import{D as f,E as p,T as m,f as h,h as g,k as _,l as v}from"./index-BKsSejEw.js";import{t as y}from"./Time-BJ30-QTG.js";import{t as b}from"./forms-BRvatu9L.js";import{n as x,t as S}from"./icons-CFXE8hAz.js";var C=t(e(),1),w=u(),T=[{value:`owner`,label:`Owner`,text:`everything, including administrators`},{value:`admin`,label:`Admin`,text:`connections, API keys and roles, queries, cache, settings`},{value:`developer`,label:`Developer`,text:`saved queries and SQL; no keys, no connection changes`},{value:`auditor`,label:`Auditor`,text:`read only - audit log, history, configuration; no SQL`}],E=e=>e===null?`Workspaces only`:T.find(t=>t.value===e)?.label??e,D=90,O=e=>new Date(Date.now()+e*864e5).toISOString().slice(0,10),k=`Store this now — it cannot be shown again. Paste it into the API key box at the bottom of the sidebar, or send it as the X-API-Key header.`;function A(){let{showError:e}=_();return t=>t instanceof c?e(t.message,{status:t.status}):e(t.message)}function j(e){let t=h(`admins.read`,`other administrators`);return l({queryKey:[`administrators`],queryFn:async()=>(await t.check(),s(await o.GET(`/api/v1/administrators`)).items),enabled:e,retry:!1})}function M(e){return l({queryKey:[`administrators`,e,`tokens`],queryFn:async()=>s(await o.GET(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:e}}})).items,retry:!1})}function N(){let e=g(),t=v(),n=!!e.data&&t(`admins.read`),r=j(n),{openDrawer:i}=_(),a=()=>i({title:`New administrator`,content:(0,w.jsx)(I,{})}),o;return o=e.isPending?(0,w.jsx)(f,{}):e.isError?(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`strong`,{children:`Not signed in as an administrator`}),(0,w.jsx)(`span`,{children:`Apply an admin token (qagadm_…) or the shared admin key in the box at the bottom of the sidebar.`})]}):n?r.isPending?(0,w.jsx)(f,{text:`Loading administrators…`}):r.isError?(0,w.jsx)(`div`,{className:`hint`,children:r.error.message}):r.data.length?(0,w.jsx)(P,{items:r.data,self:e.data.via===`token`||e.data.via===`sso`?e.data.name:null}):(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`strong`,{children:`No administrators yet`}),(0,w.jsx)(`span`,{children:`Everyone shares QUERYAPIGATE_API_KEY today, so the audit log can only say “admin”. Give each person their own sign-in, with a role that fits what they do — then the shared key becomes a break-glass key.`}),(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:a,children:`New administrator`})]}):(0,w.jsx)(F,{name:e.data.name??``,role:e.data.role}),(0,w.jsxs)(w.Fragment,{children:[(0,w.jsxs)(`div`,{className:`page-head`,children:[(0,w.jsxs)(`div`,{className:`titles`,children:[(0,w.jsx)(`h1`,{children:`Administrators`}),(0,w.jsx)(`span`,{className:`sub`,id:`administrators-sub`,children:n?`The people and pipelines that manage this server, each with a role and their own tokens.`:`Your account and your admin tokens.`})]}),(0,w.jsx)(`span`,{className:`spacer`}),n&&(0,w.jsx)(`button`,{id:`new-administrator`,type:`button`,className:`btn primary`,onClick:a,children:`New administrator`})]}),(0,w.jsx)(`div`,{id:`administrators-table`,className:`panel`,children:o})]})}function P({items:e,self:t}){let{openDrawer:i,toast:a}=_(),s=A(),c=n(),l=async e=>{if(window.confirm(`Remove administrator '${e}'? Every token of theirs stops working immediately.`))try{r(await o.DELETE(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),a(`Removed `+e),c.invalidateQueries({queryKey:[`administrators`]})}catch(e){s(e)}};return(0,w.jsx)(`div`,{style:{overflowX:`auto`},children:(0,w.jsxs)(`table`,{className:`grid`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsx)(`tr`,{children:[`Name`,`Role`,`Email`,`Last seen`,`Tokens`,``].map((e,t)=>(0,w.jsx)(`th`,{children:e},t))})}),(0,w.jsx)(`tbody`,{children:e.map(e=>(0,w.jsxs)(`tr`,{"data-name":e.name,children:[(0,w.jsx)(`td`,{children:(0,w.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,w.jsx)(`span`,{className:`dot ${e.active?`ok`:`off`}`,title:e.active?`Active`:`Inactive`}),(0,w.jsx)(`span`,{className:`name`,children:e.name}),e.name===t&&(0,w.jsx)(`span`,{className:`tag`,children:`you`}),e.sso?.status===`linked`&&(0,w.jsx)(`span`,{className:`tag`,title:`Signs in through ${e.sso.issuer??`SSO`}; role from their groups`,children:`SSO`}),e.sso?.status===`pending`&&(0,w.jsx)(`span`,{className:`tag`,title:`Linked at their next SSO sign-in, by their verified email`,children:`SSO pending`})]})}),(0,w.jsx)(`td`,{children:E(e.role)}),(0,w.jsx)(`td`,{className:e.email?void 0:`dim`,children:e.email??`—`}),(0,w.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,w.jsx)(y,{value:e.last_seen_at,fallback:`never`})}),(0,w.jsx)(`td`,{className:`mono`,children:e.tokens}),(0,w.jsx)(`td`,{children:(0,w.jsxs)(`div`,{className:`actions`,children:[(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>i({title:`Admin tokens`,kicker:e.name,wide:!0,content:(0,w.jsx)(R,{name:e.name})}),children:`Tokens`}),(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm icon`,title:`Edit ${e.name}`,"aria-label":`Edit`,onClick:()=>i({title:`Edit administrator`,kicker:e.name,content:(0,w.jsx)(I,{name:e.name})}),children:(0,w.jsx)(S,{})}),(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm icon danger`,title:`Remove ${e.name}`,"aria-label":`Remove`,onClick:()=>void l(e.name),children:(0,w.jsx)(x,{})})]})})]},e.name))})]})})}function F({name:e,role:t}){return(0,w.jsxs)(`div`,{className:`form`,style:{padding:16},children:[(0,w.jsxs)(`div`,{children:[`Signed in as `,(0,w.jsx)(`strong`,{children:e}),` · `,E(t),` — `,T.find(e=>e.value===t)?.text,`.`]}),(0,w.jsx)(R,{name:e})]})}function I({name:e}){let t=l({queryKey:[`administrators`,`detail`,e],queryFn:async()=>a(await o.GET(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),enabled:!!e,retry:!1,gcTime:0});return e&&t.isPending?(0,w.jsx)(f,{}):e&&t.isError?(0,w.jsx)(`div`,{className:`hint`,children:t.error.message}):(0,w.jsx)(L,{name:e,existing:t.data?.data,etag:t.data?.etag??null},e??``)}function L({name:e,existing:t,etag:r}){let a=!!e,{closeDrawer:c,openDrawer:l,showError:u,toast:f}=_(),h=A(),g=n(),[v,y]=(0,C.useState)(e??``),[x,S]=(0,C.useState)(t?.role??`developer`),[E,j]=(0,C.useState)(t?.email??``),[M,N]=(0,C.useState)(!t||t.active),P=i(),F=t?.sso?.status===`linked`,[I,L]=(0,C.useState)(t?.sso?.status??`none`),R=d({mutationFn:async()=>{let n={role:x,email:E.trim()||null};if(a)return s(await o.PATCH(`/api/v1/administrators/{name}`,{params:{path:{name:e},header:r?{"If-Match":r}:{}},body:{...F?{email:n.email}:n,active:M,...I===(t?.sso?.status??`none`)?{}:{sso:I===`pending`?`pending`:null}}})),null;let i=s(await o.POST(`/api/v1/administrators`,{body:{name:v.trim(),...n}}));return s(await o.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:i.name}},body:{label:`first token`,expires_at:O(D)}}))},onSuccess:t=>{if(u(``),g.invalidateQueries({queryKey:[`administrators`]}),!t){c(),f(`Updated `+e);return}l({title:`Administrator created`,kicker:t.admin,content:(0,w.jsx)(b,{secret:t.secret,label:`Admin token for ${t.admin} (expires ${t.expires_at})`,hint:k})})},onError:h});return(0,w.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{if(e.preventDefault(),!a&&!v.trim()){u(`A name is required.`,{errors:{name:`This field is required`}});return}R.mutate()},children:[(0,w.jsx)(m,{id:`a-name`,label:`Name`,hint:a?void 0:`A person (alice) or a pipeline (ci-deploy). Shown in the audit log.`,children:(0,w.jsx)(`input`,{id:`a-name`,value:v,placeholder:`alice`,autoComplete:`off`,spellCheck:!1,disabled:a,autoFocus:!a,onChange:e=>y(e.target.value)})}),(0,w.jsx)(m,{id:`a-role`,label:`Role`,hint:F?`From their groups at the identity provider, at every sign-in - change it there.`:T.find(e=>e.value===x)?.text,children:(0,w.jsx)(`select`,{id:`a-role`,value:x,disabled:F,onChange:e=>S(e.target.value),children:T.map(e=>(0,w.jsx)(`option`,{value:e.value,children:e.label},e.value))})}),(0,w.jsx)(m,{id:`a-email`,label:`Email`,hint:`Optional.`,children:(0,w.jsx)(`input`,{id:`a-email`,type:`email`,value:E,autoComplete:`off`,onChange:e=>j(e.target.value)})}),a&&(0,w.jsxs)(`label`,{className:`switch`,children:[(0,w.jsx)(`input`,{id:`a-active`,type:`checkbox`,checked:M,onChange:e=>N(e.target.checked)}),`Active`,(0,w.jsx)(`span`,{className:`hint`,children:`— unchecking stops every token of theirs immediately`})]}),a&&P.data?.sso&&(0,w.jsx)(m,{id:`a-sso`,label:`Sign-in through SSO`,hint:I===`linked`?`They sign in through the identity provider. Unlinking ends their sessions; their next SSO sign-in makes a new administrator.`:`Pending: their next SSO sign-in, with a verified email matching the one above, is bound to this account - once.`,children:(0,w.jsxs)(`select`,{id:`a-sso`,value:I,onChange:e=>L(e.target.value),children:[F&&(0,w.jsx)(`option`,{value:`linked`,children:`Linked`}),!F&&(0,w.jsx)(`option`,{value:`pending`,children:`Link at their next sign-in`}),(0,w.jsx)(`option`,{value:`none`,children:F?`Unlink`:`Not through SSO`})]})}),!a&&(0,w.jsxs)(`div`,{className:`hint`,children:[`Their first admin token, valid for `,D,` days, is shown once after you create them.`]}),(0,w.jsx)(p,{onCancel:c,children:(0,w.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:R.isPending,children:a?`Save`:`Create`})})]})}function R({name:e}){let t=M(e),i=n(),{toast:a}=_(),c=A(),[l,u]=(0,C.useState)(``),[p,h]=(0,C.useState)(O(D)),[g,v]=(0,C.useState)(null),x=()=>void i.invalidateQueries({queryKey:[`administrators`]}),S=d({mutationFn:async()=>s(await o.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:e}},body:{label:l.trim()||null,expires_at:p||null}})),onSuccess:e=>{v(e),u(``),x()},onError:c}),T=async t=>{if(window.confirm(`Revoke ${t.label?`'${t.label}'`:t.id}? Anything using it stops working now.`))try{r(await o.DELETE(`/api/v1/administrators/{name}/tokens/{token_id}`,{params:{path:{name:e,token_id:t.id}}})),a(`Revoked `+(t.label??t.id)),x()}catch(e){c(e)}},E=new Date().toISOString().slice(0,10);return(0,w.jsxs)(`div`,{className:`form`,id:`admin-tokens`,children:[t.isPending?(0,w.jsx)(f,{text:`Loading tokens…`}):t.isError?(0,w.jsx)(`div`,{className:`hint`,children:t.error.message}):t.data.length?(0,w.jsxs)(`table`,{className:`grid`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsx)(`tr`,{children:[`Label`,`Expires`,`Last used`,``].map((e,t)=>(0,w.jsx)(`th`,{children:e},t))})}),(0,w.jsx)(`tbody`,{children:t.data.map(e=>(0,w.jsxs)(`tr`,{"data-token":e.id,children:[(0,w.jsx)(`td`,{title:e.id,children:e.label??(0,w.jsx)(`span`,{className:`dim mono`,children:e.id})}),(0,w.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:e.expires_at?(0,w.jsx)(`span`,{style:e.expired?{color:`var(--danger)`}:void 0,children:e.expires_at+(e.expired?` (expired)`:e.expires_at===E?` (today)`:``)}):(0,w.jsx)(`span`,{className:`dim`,children:`never`})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(y,{value:e.last_used_at,fallback:`never`})}),(0,w.jsx)(`td`,{children:(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm danger`,onClick:()=>void T(e),children:`Revoke`})})]},e.id))})]}):(0,w.jsx)(`div`,{className:`hint`,children:`No tokens. Issue one below to sign in with.`}),g?(0,w.jsx)(b,{secret:g.secret,label:`New token${g.label?` '${g.label}'`:``} (${g.expires_at?`expires ${g.expires_at}`:`never expires`})`,hint:k,onDone:()=>v(null)}):(0,w.jsxs)(`form`,{className:`grid2`,noValidate:!0,onSubmit:e=>{e.preventDefault(),S.mutate()},children:[(0,w.jsx)(m,{id:`t-label`,label:`New token: label`,hint:`What it's for - laptop, ci.`,children:(0,w.jsx)(`input`,{id:`t-label`,value:l,autoComplete:`off`,onChange:e=>u(e.target.value)})}),(0,w.jsx)(m,{id:`t-expires`,label:`Expires`,hint:`Leave blank for no expiry.`,children:(0,w.jsx)(`input`,{id:`t-expires`,type:`date`,value:p,onChange:e=>h(e.target.value)})}),(0,w.jsx)(`div`,{children:(0,w.jsx)(`button`,{type:`submit`,className:`btn`,disabled:S.isPending,children:`Issue token`})})]})]})}export{N as AdministratorsPage};
|
|
1
|
+
import{B as e,H as t,R as n,c as r,h as i,l as a,n as o,s,t as c,v as l,z as u}from"./client-ZHNiMG8H.js";import{t as d}from"./useMutation-DQTq-cNw.js";import{D as f,E as p,T as m,f as h,h as g,k as _,l as v}from"./index--QHzN3S_.js";import{t as y}from"./Time-Bbp6q-9f.js";import{t as b}from"./forms-CfcbrGmf.js";import{n as x,t as S}from"./icons-CFXE8hAz.js";var C=t(e(),1),w=u(),T=[{value:`owner`,label:`Owner`,text:`everything, including administrators`},{value:`admin`,label:`Admin`,text:`connections, API keys and roles, queries, cache, settings`},{value:`developer`,label:`Developer`,text:`saved queries and SQL; no keys, no connection changes`},{value:`auditor`,label:`Auditor`,text:`read only - audit log, history, configuration; no SQL`}],E=e=>e===null?`Workspaces only`:T.find(t=>t.value===e)?.label??e,D=90,O=e=>new Date(Date.now()+e*864e5).toISOString().slice(0,10),k=`Store this now — it cannot be shown again. Paste it into the API key box at the bottom of the sidebar, or send it as the X-API-Key header.`;function A(){let{showError:e}=_();return t=>t instanceof c?e(t.message,{status:t.status}):e(t.message)}function j(e){let t=h(`admins.read`,`other administrators`);return l({queryKey:[`administrators`],queryFn:async()=>(await t.check(),s(await o.GET(`/api/v1/administrators`)).items),enabled:e,retry:!1})}function M(e){return l({queryKey:[`administrators`,e,`tokens`],queryFn:async()=>s(await o.GET(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:e}}})).items,retry:!1})}function N(){let e=g(),t=v(),n=!!e.data&&t(`admins.read`),r=j(n),{openDrawer:i}=_(),a=()=>i({title:`New administrator`,content:(0,w.jsx)(I,{})}),o;return o=e.isPending?(0,w.jsx)(f,{}):e.isError?(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`strong`,{children:`Not signed in as an administrator`}),(0,w.jsx)(`span`,{children:`Apply an admin token (qagadm_…) or the shared admin key in the box at the bottom of the sidebar.`})]}):n?r.isPending?(0,w.jsx)(f,{text:`Loading administrators…`}):r.isError?(0,w.jsx)(`div`,{className:`hint`,children:r.error.message}):r.data.length?(0,w.jsx)(P,{items:r.data,self:e.data.via===`token`||e.data.via===`sso`?e.data.name:null}):(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`strong`,{children:`No administrators yet`}),(0,w.jsx)(`span`,{children:`Everyone shares QUERYAPIGATE_API_KEY today, so the audit log can only say “admin”. Give each person their own sign-in, with a role that fits what they do — then the shared key becomes a break-glass key.`}),(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:a,children:`New administrator`})]}):(0,w.jsx)(F,{name:e.data.name??``,role:e.data.role}),(0,w.jsxs)(w.Fragment,{children:[(0,w.jsxs)(`div`,{className:`page-head`,children:[(0,w.jsxs)(`div`,{className:`titles`,children:[(0,w.jsx)(`h1`,{children:`Administrators`}),(0,w.jsx)(`span`,{className:`sub`,id:`administrators-sub`,children:n?`The people and pipelines that manage this server, each with a role and their own tokens.`:`Your account and your admin tokens.`})]}),(0,w.jsx)(`span`,{className:`spacer`}),n&&(0,w.jsx)(`button`,{id:`new-administrator`,type:`button`,className:`btn primary`,onClick:a,children:`New administrator`})]}),(0,w.jsx)(`div`,{id:`administrators-table`,className:`panel`,children:o})]})}function P({items:e,self:t}){let{openDrawer:i,toast:a}=_(),s=A(),c=n(),l=async e=>{if(window.confirm(`Remove administrator '${e}'? Every token of theirs stops working immediately.`))try{r(await o.DELETE(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),a(`Removed `+e),c.invalidateQueries({queryKey:[`administrators`]})}catch(e){s(e)}};return(0,w.jsx)(`div`,{style:{overflowX:`auto`},children:(0,w.jsxs)(`table`,{className:`grid`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsx)(`tr`,{children:[`Name`,`Role`,`Email`,`Last seen`,`Tokens`,``].map((e,t)=>(0,w.jsx)(`th`,{children:e},t))})}),(0,w.jsx)(`tbody`,{children:e.map(e=>(0,w.jsxs)(`tr`,{"data-name":e.name,children:[(0,w.jsx)(`td`,{children:(0,w.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,w.jsx)(`span`,{className:`dot ${e.active?`ok`:`off`}`,title:e.active?`Active`:`Inactive`}),(0,w.jsx)(`span`,{className:`name`,children:e.name}),e.name===t&&(0,w.jsx)(`span`,{className:`tag`,children:`you`}),e.sso?.status===`linked`&&(0,w.jsx)(`span`,{className:`tag`,title:`Signs in through ${e.sso.issuer??`SSO`}; role from their groups`,children:`SSO`}),e.sso?.status===`pending`&&(0,w.jsx)(`span`,{className:`tag`,title:`Linked at their next SSO sign-in, by their verified email`,children:`SSO pending`})]})}),(0,w.jsx)(`td`,{children:E(e.role)}),(0,w.jsx)(`td`,{className:e.email?void 0:`dim`,children:e.email??`—`}),(0,w.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,w.jsx)(y,{value:e.last_seen_at,fallback:`never`})}),(0,w.jsx)(`td`,{className:`mono`,children:e.tokens}),(0,w.jsx)(`td`,{children:(0,w.jsxs)(`div`,{className:`actions`,children:[(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>i({title:`Admin tokens`,kicker:e.name,wide:!0,content:(0,w.jsx)(R,{name:e.name})}),children:`Tokens`}),(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm icon`,title:`Edit ${e.name}`,"aria-label":`Edit`,onClick:()=>i({title:`Edit administrator`,kicker:e.name,content:(0,w.jsx)(I,{name:e.name})}),children:(0,w.jsx)(S,{})}),(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm icon danger`,title:`Remove ${e.name}`,"aria-label":`Remove`,onClick:()=>void l(e.name),children:(0,w.jsx)(x,{})})]})})]},e.name))})]})})}function F({name:e,role:t}){return(0,w.jsxs)(`div`,{className:`form`,style:{padding:16},children:[(0,w.jsxs)(`div`,{children:[`Signed in as `,(0,w.jsx)(`strong`,{children:e}),` · `,E(t),` — `,T.find(e=>e.value===t)?.text,`.`]}),(0,w.jsx)(R,{name:e})]})}function I({name:e}){let t=l({queryKey:[`administrators`,`detail`,e],queryFn:async()=>a(await o.GET(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),enabled:!!e,retry:!1,gcTime:0});return e&&t.isPending?(0,w.jsx)(f,{}):e&&t.isError?(0,w.jsx)(`div`,{className:`hint`,children:t.error.message}):(0,w.jsx)(L,{name:e,existing:t.data?.data,etag:t.data?.etag??null},e??``)}function L({name:e,existing:t,etag:r}){let a=!!e,{closeDrawer:c,openDrawer:l,showError:u,toast:f}=_(),h=A(),g=n(),[v,y]=(0,C.useState)(e??``),[x,S]=(0,C.useState)(t?.role??`developer`),[E,j]=(0,C.useState)(t?.email??``),[M,N]=(0,C.useState)(!t||t.active),P=i(),F=t?.sso?.status===`linked`,[I,L]=(0,C.useState)(t?.sso?.status??`none`),R=d({mutationFn:async()=>{let n={role:x,email:E.trim()||null};if(a)return s(await o.PATCH(`/api/v1/administrators/{name}`,{params:{path:{name:e},header:r?{"If-Match":r}:{}},body:{...F?{email:n.email}:n,active:M,...I===(t?.sso?.status??`none`)?{}:{sso:I===`pending`?`pending`:null}}})),null;let i=s(await o.POST(`/api/v1/administrators`,{body:{name:v.trim(),...n}}));return s(await o.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:i.name}},body:{label:`first token`,expires_at:O(D)}}))},onSuccess:t=>{if(u(``),g.invalidateQueries({queryKey:[`administrators`]}),!t){c(),f(`Updated `+e);return}l({title:`Administrator created`,kicker:t.admin,content:(0,w.jsx)(b,{secret:t.secret,label:`Admin token for ${t.admin} (expires ${t.expires_at})`,hint:k})})},onError:h});return(0,w.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{if(e.preventDefault(),!a&&!v.trim()){u(`A name is required.`,{errors:{name:`This field is required`}});return}R.mutate()},children:[(0,w.jsx)(m,{id:`a-name`,label:`Name`,hint:a?void 0:`A person (alice) or a pipeline (ci-deploy). Shown in the audit log.`,children:(0,w.jsx)(`input`,{id:`a-name`,value:v,placeholder:`alice`,autoComplete:`off`,spellCheck:!1,disabled:a,autoFocus:!a,onChange:e=>y(e.target.value)})}),(0,w.jsx)(m,{id:`a-role`,label:`Role`,hint:F?`From their groups at the identity provider, at every sign-in - change it there.`:T.find(e=>e.value===x)?.text,children:(0,w.jsx)(`select`,{id:`a-role`,value:x,disabled:F,onChange:e=>S(e.target.value),children:T.map(e=>(0,w.jsx)(`option`,{value:e.value,children:e.label},e.value))})}),(0,w.jsx)(m,{id:`a-email`,label:`Email`,hint:`Optional.`,children:(0,w.jsx)(`input`,{id:`a-email`,type:`email`,value:E,autoComplete:`off`,onChange:e=>j(e.target.value)})}),a&&(0,w.jsxs)(`label`,{className:`switch`,children:[(0,w.jsx)(`input`,{id:`a-active`,type:`checkbox`,checked:M,onChange:e=>N(e.target.checked)}),`Active`,(0,w.jsx)(`span`,{className:`hint`,children:`— unchecking stops every token of theirs immediately`})]}),a&&P.data?.sso&&(0,w.jsx)(m,{id:`a-sso`,label:`Sign-in through SSO`,hint:I===`linked`?`They sign in through the identity provider. Unlinking ends their sessions; their next SSO sign-in makes a new administrator.`:`Pending: their next SSO sign-in, with a verified email matching the one above, is bound to this account - once.`,children:(0,w.jsxs)(`select`,{id:`a-sso`,value:I,onChange:e=>L(e.target.value),children:[F&&(0,w.jsx)(`option`,{value:`linked`,children:`Linked`}),!F&&(0,w.jsx)(`option`,{value:`pending`,children:`Link at their next sign-in`}),(0,w.jsx)(`option`,{value:`none`,children:F?`Unlink`:`Not through SSO`})]})}),!a&&(0,w.jsxs)(`div`,{className:`hint`,children:[`Their first admin token, valid for `,D,` days, is shown once after you create them.`]}),(0,w.jsx)(p,{onCancel:c,children:(0,w.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:R.isPending,children:a?`Save`:`Create`})})]})}function R({name:e}){let t=M(e),i=n(),{toast:a}=_(),c=A(),[l,u]=(0,C.useState)(``),[p,h]=(0,C.useState)(O(D)),[g,v]=(0,C.useState)(null),x=()=>void i.invalidateQueries({queryKey:[`administrators`]}),S=d({mutationFn:async()=>s(await o.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:e}},body:{label:l.trim()||null,expires_at:p||null}})),onSuccess:e=>{v(e),u(``),x()},onError:c}),T=async t=>{if(window.confirm(`Revoke ${t.label?`'${t.label}'`:t.id}? Anything using it stops working now.`))try{r(await o.DELETE(`/api/v1/administrators/{name}/tokens/{token_id}`,{params:{path:{name:e,token_id:t.id}}})),a(`Revoked `+(t.label??t.id)),x()}catch(e){c(e)}},E=new Date().toISOString().slice(0,10);return(0,w.jsxs)(`div`,{className:`form`,id:`admin-tokens`,children:[t.isPending?(0,w.jsx)(f,{text:`Loading tokens…`}):t.isError?(0,w.jsx)(`div`,{className:`hint`,children:t.error.message}):t.data.length?(0,w.jsxs)(`table`,{className:`grid`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsx)(`tr`,{children:[`Label`,`Expires`,`Last used`,``].map((e,t)=>(0,w.jsx)(`th`,{children:e},t))})}),(0,w.jsx)(`tbody`,{children:t.data.map(e=>(0,w.jsxs)(`tr`,{"data-token":e.id,children:[(0,w.jsx)(`td`,{title:e.id,children:e.label??(0,w.jsx)(`span`,{className:`dim mono`,children:e.id})}),(0,w.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:e.expires_at?(0,w.jsx)(`span`,{style:e.expired?{color:`var(--danger)`}:void 0,children:e.expires_at+(e.expired?` (expired)`:e.expires_at===E?` (today)`:``)}):(0,w.jsx)(`span`,{className:`dim`,children:`never`})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(y,{value:e.last_used_at,fallback:`never`})}),(0,w.jsx)(`td`,{children:(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm danger`,onClick:()=>void T(e),children:`Revoke`})})]},e.id))})]}):(0,w.jsx)(`div`,{className:`hint`,children:`No tokens. Issue one below to sign in with.`}),g?(0,w.jsx)(b,{secret:g.secret,label:`New token${g.label?` '${g.label}'`:``} (${g.expires_at?`expires ${g.expires_at}`:`never expires`})`,hint:k,onDone:()=>v(null)}):(0,w.jsxs)(`form`,{className:`grid2`,noValidate:!0,onSubmit:e=>{e.preventDefault(),S.mutate()},children:[(0,w.jsx)(m,{id:`t-label`,label:`New token: label`,hint:`What it's for - laptop, ci.`,children:(0,w.jsx)(`input`,{id:`t-label`,value:l,autoComplete:`off`,onChange:e=>u(e.target.value)})}),(0,w.jsx)(m,{id:`t-expires`,label:`Expires`,hint:`Leave blank for no expiry.`,children:(0,w.jsx)(`input`,{id:`t-expires`,type:`date`,value:p,onChange:e=>h(e.target.value)})}),(0,w.jsx)(`div`,{children:(0,w.jsx)(`button`,{type:`submit`,className:`btn`,disabled:S.isPending,children:`Issue token`})})]})]})}export{N as AdministratorsPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{B as e,H as t,n,s as r,v as i,z as a}from"./client-ZHNiMG8H.js";import{D as o}from"./index
|
|
1
|
+
import{B as e,H as t,n,s as r,v as i,z as a}from"./client-ZHNiMG8H.js";import{D as o}from"./index--QHzN3S_.js";import{t as s}from"./Time-Bbp6q-9f.js";import{t as c}from"./CodeBox-Cwh5KHLp.js";var l=t(e(),1),u=a(),d=[{id:`1h`,label:`Last hour`,hours:1},{id:`24h`,label:`Last 24 hours`,hours:24},{id:`7d`,label:`Last 7 days`,hours:168},{id:`30d`,label:`Last 30 days`,hours:720}],f=e=>e.toLocaleString();function p({refusals:e}){let t=Object.entries(e);return t.length?(0,u.jsx)(`span`,{style:{display:`inline-flex`,flexWrap:`wrap`,gap:4},children:t.map(([e,t])=>(0,u.jsx)(`span`,{className:`tag`,title:`${t} call${t===1?``:`s`} failed with ${e}`,children:`${e} × ${t}`},e))}):(0,u.jsx)(`span`,{className:`dim`,children:`none`})}function m({writes:e}){let t=Object.entries(e??{}).filter(([,e])=>e).map(([e,t])=>`${t} ${e}`);return(0,u.jsx)(`div`,{className:`hint`,children:t.length?t.join(` · `):`None.`})}function h({agent:e}){let t=Object.entries(e.queries);return(0,u.jsxs)(`div`,{className:`agent-detail`,children:[(0,u.jsx)(`p`,{className:`sub-h`,children:`Writes asked for`}),(0,u.jsx)(m,{writes:e.writes}),(0,u.jsx)(`p`,{className:`sub-h`,style:{marginTop:12},children:`Saved queries`}),t.length?(0,u.jsx)(`div`,{className:`hint`,children:t.map(([e,t])=>`${e} × ${t}`).join(` · `)}):(0,u.jsx)(`div`,{className:`hint`,children:`None - only ad-hoc SQL.`}),(0,u.jsx)(`p`,{className:`sub-h`,style:{marginTop:12},children:`Most frequent ad-hoc statements`}),e.top_statements.length?e.top_statements.map((e,t)=>(0,u.jsxs)(`div`,{className:`agent-statement`,children:[(0,u.jsx)(`div`,{className:`hint`,children:`${e.count} call${e.count===1?``:`s`} · ${f(e.rows)} rows`+(e.errors?` · ${e.errors} failed`:``)}),e.sql?(0,u.jsx)(c,{sql:e.sql}):(0,u.jsx)(`span`,{className:`mono dim`,children:`sha256 ${e.sql_sha256}`})]},t)):(0,u.jsx)(`div`,{className:`hint`,children:e.adhoc_calls?`Run history keeps no SQL text here (QUERYAPIGATE_HISTORY_ADHOC_SQL is none).`:`No ad-hoc SQL.`})]})}function g(){let[e,t]=(0,l.useState)(`24h`),[a,c]=(0,l.useState)(null),m=d.find(t=>t.id===e).hours,g=i({queryKey:[`usage`,`agents`,e],queryFn:async()=>r(await n.GET(`/api/v1/usage/agents`,{params:{query:{hours:m}}})),retry:!1}),_=g.data?.items??[],v;v=g.isPending?(0,u.jsx)(o,{text:`Loading agents…`}):g.isError?(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`Couldn’t load agent usage`}),(0,u.jsx)(`span`,{children:g.error.message})]}):_.length?(0,u.jsx)(`div`,{style:{overflowX:`auto`},children:(0,u.jsxs)(`table`,{className:`grid`,children:[(0,u.jsx)(`thead`,{children:(0,u.jsx)(`tr`,{children:[`Agent`,`Calls`,`Rows`,`Failed`,`Masked`,`Refused or failed with`,`Last seen`].map(e=>(0,u.jsx)(`th`,{children:e},e))})}),(0,u.jsx)(`tbody`,{children:_.map(e=>(0,u.jsxs)(l.Fragment,{children:[(0,u.jsxs)(`tr`,{children:[(0,u.jsxs)(`td`,{children:[(0,u.jsx)(`button`,{type:`button`,className:`agent-name`,"aria-expanded":a===e.agent,onClick:()=>c(a===e.agent?null:e.agent),children:e.agent}),(0,u.jsx)(`div`,{className:`dim`,style:{fontSize:12},children:e.connections.join(`, `)})]}),(0,u.jsx)(`td`,{className:`num`,children:f(e.calls)}),(0,u.jsx)(`td`,{className:`num`,children:f(e.rows)}),(0,u.jsx)(`td`,{className:`num`,children:f(e.errors)}),(0,u.jsx)(`td`,{className:`num`,children:f(e.masked_calls)}),(0,u.jsx)(`td`,{children:(0,u.jsx)(p,{refusals:e.refusals})}),(0,u.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,u.jsx)(s,{value:e.last_seen,fallback:``})})]}),a===e.agent&&(0,u.jsx)(`tr`,{children:(0,u.jsx)(`td`,{colSpan:7,children:(0,u.jsx)(h,{agent:e})})})]},e.agent))})]})}):(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`No agent called the MCP server in this window`}),(0,u.jsx)(`span`,{children:`Runs over MCP - saved-query tools and execute_sql - appear here, per API key or signed-in user.`})]});let y=[];return g.data&&g.data.sample_rate<1&&y.push(`Run history keeps ${Math.round(g.data.sample_rate*100)}% of successful runs, so successful calls and rows are a sample; failures are all here.`),g.data?.truncated&&y.push(`More runs than one summary reads (100,000) - narrow the window.`),(0,u.jsxs)(u.Fragment,{children:[(0,u.jsxs)(`div`,{className:`page-head`,children:[(0,u.jsxs)(`div`,{className:`titles`,children:[(0,u.jsx)(`h1`,{children:`Agents`}),(0,u.jsx)(`span`,{className:`sub`,children:`What each AI agent did over MCP - per API key or signed-in user, from run history.`})]}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>void g.refetch(),children:`Refresh`})]}),(0,u.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,u.jsxs)(`div`,{className:`panel-bar`,children:[(0,u.jsx)(`select`,{"aria-label":`Window`,value:e,onChange:e=>t(e.target.value),children:d.map(e=>(0,u.jsx)(`option`,{value:e.id,children:e.label},e.id))}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`span`,{className:`panel-count`,children:g.data?`${_.length} agent${_.length===1?``:`s`}`:``})]}),y.map(e=>(0,u.jsx)(`div`,{className:`hint`,style:{padding:`6px 12px`},children:e},e)),v]})]})}export{g as AgentsPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{B as e,H as t,z as n}from"./client-ZHNiMG8H.js";import{C as r,D as i,M as a,
|
|
1
|
+
import{B as e,H as t,z as n}from"./client-ZHNiMG8H.js";import{C as r,D as i,M as a,N as o,S as s,x as c,y as l}from"./index--QHzN3S_.js";import{t as u}from"./Time-Bbp6q-9f.js";import{n as d}from"./state-O13cl7cO.js";var f=t(e(),1),p=n(),m=[{id:`critical`,label:`Critical`},{id:`warning`,label:`Warning`},{id:`info`,label:`For your information`}],h={key:`Open API keys`,query:`Open query`,connection:`Open connections`,settings:`Open settings`,export:`Open exports`,agent:`Open agents`,approvals:`Open approvals`,audit:`Open audit log`};function g(){let e=a();return t=>{t.type===`query`?e(`/queries/${encodeURIComponent(t.name)}`):t.type===`key`?e(`/api-keys`):t.type===`connection`?e(`/connections`):t.type===`export`?e(`/exports`):t.type===`agent`?e(`/agents`):t.type===`approvals`?e(`/approvals`):t.type===`audit`?e(`/audit-log`):(d(t.name),e(`/settings`))}}function _({alert:e,dismissed:t}){let n=g();return(0,p.jsxs)(`div`,{className:`alert-row `+e.severity,"data-alert":e.id,children:[(0,p.jsx)(`span`,{className:`sev `+e.severity,children:e.severity}),(0,p.jsxs)(`div`,{className:`alert-what`,children:[(0,p.jsx)(`strong`,{children:e.title}),(0,p.jsx)(`span`,{className:`alert-detail`,children:e.detail}),e.since&&!e.detail.includes(e.since)&&(0,p.jsxs)(`span`,{className:`alert-since`,children:[e.kind===`key_expiring`?`Expires `:`Since `,(0,p.jsx)(u,{value:e.since})]})]}),(0,p.jsxs)(`div`,{className:`actions`,children:[e.target&&(0,p.jsx)(`button`,{type:`button`,className:`btn sm outlined`,onClick:()=>n(e.target),children:h[e.target.type]??`Open`}),t?(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>s(e.id),children:`Restore`}):(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,title:`Hide it in this browser until it clears`,onClick:()=>c(e.id),children:`Dismiss`})]})]})}function v({alerts:e}){return(0,p.jsx)(p.Fragment,{children:m.map(({id:t,label:n})=>{let r=e.filter(e=>e.severity===t);return r.length?(0,p.jsxs)(`div`,{className:`panel alerts-group`,id:`alerts-`+t,children:[(0,p.jsxs)(`h2`,{children:[n,` `,(0,p.jsx)(`span`,{className:`count`,children:r.length})]}),r.map(e=>(0,p.jsx)(_,{alert:e},e.id))]},t):null})})}function y(){let e=a(),t=o(),n=l.find(e=>e.id===t.tab),{query:s,active:c,dismissed:d}=r(),[m,h]=(0,f.useState)(!1),g=e=>!n||n.kinds.includes(e.kind),y=c.filter(g),b=d.filter(g),x=e=>c.filter(t=>!e||e.includes(t.kind)),S;S=s.isPending?(0,p.jsx)(i,{text:`Checking…`}):s.isError?(0,p.jsx)(`div`,{className:`panel`,children:(0,p.jsxs)(`div`,{className:`empty`,children:[(0,p.jsx)(`strong`,{children:`Couldn’t check for alerts`}),(0,p.jsx)(`span`,{children:s.error.message}),(0,p.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>void s.refetch(),children:`Retry`})]})}):(0,p.jsxs)(p.Fragment,{children:[n&&(0,p.jsx)(`p`,{className:`alerts-about`,children:n.about}),y.length?(0,p.jsx)(v,{alerts:y}):(0,p.jsx)(`div`,{className:`panel`,id:`alerts-clear`,children:(0,p.jsxs)(`div`,{className:`empty`,children:[(0,p.jsx)(`strong`,{children:`All clear`}),(0,p.jsx)(`span`,{children:n?`Nothing to report from this check right now.`:`Nothing needs your attention right now.`})]})}),b.length>0&&(0,p.jsxs)(`div`,{className:`alerts-dismissed`,children:[(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>h(e=>!e),children:`${b.length} dismissed in this browser · ${m?`Hide`:`Show`}`}),m&&(0,p.jsx)(`div`,{className:`panel alerts-group`,id:`alerts-dismissed`,children:b.map(e=>(0,p.jsx)(_,{alert:e,dismissed:!0},e.id))})]})]});let C=(t,r,i)=>(0,p.jsxs)(`button`,{type:`button`,role:`tab`,"aria-selected":(n?.id??`all`)===t,className:`minitab`+((n?.id??`all`)===t?` active`:``),onClick:()=>e(t===`all`?`/alerts`:`/alerts/`+t),children:[r,s.data?(0,p.jsx)(`span`,{className:`n`+(i.length?` on`:``)+(i.some(e=>e.severity===`critical`)?` critical`:``),children:i.length}):null]},t);return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)(`div`,{className:`page-head`,children:[(0,p.jsxs)(`div`,{className:`titles`,children:[(0,p.jsx)(`h1`,{children:`Alerts`}),(0,p.jsxs)(`span`,{className:`sub`,children:[`What needs attention now, checked every minute. Each clears by itself once its cause does.`,s.data?(0,p.jsxs)(p.Fragment,{children:[` Last checked `,(0,p.jsx)(u,{value:s.data.checked_at}),`.`]}):null]})]}),(0,p.jsx)(`span`,{className:`spacer`}),(0,p.jsx)(`button`,{id:`refresh-alerts`,type:`button`,className:`btn`,onClick:()=>void s.refetch(),children:`Check now`})]}),(0,p.jsxs)(`div`,{className:`minitabs alerts-tabs`,role:`tablist`,"aria-label":`Checks`,children:[C(`all`,`All`,x()),l.map(e=>C(e.id,e.label,x(e.kinds)))]}),S]})}export{y as AlertsPage};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e,s as t,v as n,z as r}from"./client-ZHNiMG8H.js";import{A as i,D as a,N as o,c as s,d as c,k as l,s as u}from"./index--QHzN3S_.js";import{t as d}from"./Time-Bbp6q-9f.js";import{t as f}from"./QueryInfo-F9F7if8P.js";import{a as p}from"./reach-BAOLd75Y.js";import{i as m,t as h}from"./KeyActivity-EO98IG9y.js";var g=r(),_={query:`named query`,collection:`collection`,connection:`connection`};function v({term:e,children:t}){return(0,g.jsxs)(`div`,{children:[(0,g.jsx)(`dt`,{children:e}),(0,g.jsx)(`dd`,{children:t})]})}function y({title:e,sub:t,children:n}){return(0,g.jsxs)(`section`,{className:`panel`,style:{marginTop:16},children:[(0,g.jsxs)(`div`,{className:`panel-bar`,children:[(0,g.jsx)(`strong`,{children:e}),t?(0,g.jsx)(`span`,{className:`dim`,children:t}):null]}),n]})}function b(){let{name:r=``}=o(),b=s(),x=u(),S=c(),C=m(r),{openDrawer:w}=l(),T=new Date().toISOString().slice(0,10),E=n({queryKey:[`apikey-history`,r],queryFn:async()=>t(await e.GET(`/api/v1/history`,{params:{query:{key:r,limit:50}}})).items,retry:!1});if(b.isPending)return(0,g.jsx)(a,{text:`Loading API key…`});let D=b.data?.[r];if(!D)return(0,g.jsxs)(g.Fragment,{children:[(0,g.jsx)(`div`,{className:`page-head`,children:(0,g.jsxs)(`div`,{className:`titles`,children:[(0,g.jsx)(i,{to:`/api-keys`,className:`sub`,children:`← API keys`}),(0,g.jsx)(`h1`,{children:r||`API key`})]})}),(0,g.jsx)(`div`,{className:`panel`,children:(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`No such API key`}),(0,g.jsx)(`span`,{children:`It may have been revoked and removed, or the name is wrong.`}),(0,g.jsx)(i,{to:`/api-keys`,className:`btn sm`,children:`Back to API keys`})]})})]});let O=!!(D.expires_at&&D.expires_at<T),k=D.active?O?`Expired`:`Active`:`Revoked`,A=(x.data??[]).map(e=>({q:e,via:p(D,e.name,e.collection,e.connection_name)})).filter(e=>e.via.length),j=(S.data??[]).map(e=>e.name),M=D.connections===`*`?j:D.connections??[],N=A.map(e=>e.q.connection_name).filter(e=>e!=null&&!M.includes(e)),P=[...new Set([...M,...N])].sort(),F=e=>(S.data??[]).find(t=>t.name===e)?.db??null,I=e=>w({title:e.name,kicker:`${e.collection||`uncollected`} · ${e.connection_name||`no connection`}`,content:(0,g.jsx)(f,{name:e.name,dbType:e.connection_name?F(e.connection_name):null})}),L=D.allowed_tables??[],R=D.allowed_ips??[],z=D.usage_this_period;return(0,g.jsxs)(g.Fragment,{children:[(0,g.jsxs)(`div`,{className:`page-head`,children:[(0,g.jsxs)(`div`,{className:`titles`,children:[(0,g.jsx)(i,{to:`/api-keys`,className:`sub`,children:`← API keys`}),(0,g.jsxs)(`h1`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,g.jsx)(`span`,{className:`dot ${D.active&&!O?`ok`:`off`}`,title:k}),r,D.example?(0,g.jsx)(`span`,{className:`tag`,children:`example`}):null]}),(0,g.jsxs)(`span`,{className:`sub`,children:[k,D.created_from_role?` · created from role ${D.created_from_role}`:``]})]}),(0,g.jsx)(`span`,{className:`spacer`}),(0,g.jsx)(i,{to:`/api-keys`,className:`btn sm`,children:`Back`})]}),(0,g.jsxs)(`dl`,{className:`meta`,children:[(0,g.jsx)(v,{term:`status`,children:k}),(0,g.jsx)(v,{term:`access`,children:D.allow_writes?`Read / write`:`Read-only`}),(0,g.jsx)(v,{term:`created`,children:(0,g.jsx)(d,{value:D.created_at})}),(0,g.jsx)(v,{term:`last used`,children:(0,g.jsx)(d,{value:D.last_used_at,fallback:`never`})}),(0,g.jsx)(v,{term:`expires`,children:D.expires_at?D.expires_at+(O?` (expired)`:``):`never`}),(0,g.jsx)(v,{term:`rate limit`,children:D.rate_limit||`server default`}),(0,g.jsx)(v,{term:`query timeout`,children:D.effective_limits?.query_timeout==null?`server default`:`${D.effective_limits.query_timeout}s`}),(0,g.jsx)(v,{term:`row limit (page)`,children:D.effective_limits?.max_rows?.page??`—`}),(0,g.jsx)(v,{term:`max concurrent`,children:D.effective_limits?.max_concurrent??`unlimited`}),(0,g.jsx)(v,{term:`allowed IPs`,children:R.length?R.join(`, `):`any`})]}),(0,g.jsx)(y,{title:`APIs it can reach`,sub:D.queries===`*`?`all saved queries`:`${A.length} saved ${A.length===1?`query`:`queries`}`,children:D.queries===`*`?(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`Every saved query`}),(0,g.jsx)(`span`,{children:`This key carries a wildcard query grant, so it can run any saved query.`})]}):A.length?(0,g.jsx)(`div`,{style:{overflowX:`auto`},children:(0,g.jsxs)(`table`,{className:`grid`,children:[(0,g.jsx)(`thead`,{children:(0,g.jsx)(`tr`,{children:[`API endpoint`,`Query`,`Connection`,`Collection`,`Granted via`,`Activity (30d)`].map(e=>(0,g.jsx)(`th`,{children:e},e))})}),(0,g.jsx)(`tbody`,{children:A.map(({q:e,via:t})=>(0,g.jsxs)(`tr`,{"data-query":e.name,children:[(0,g.jsx)(`td`,{className:`mono`,children:e.published_version?e.endpoint:(0,g.jsxs)(`span`,{className:`dim`,title:`No published version yet`,children:[e.endpoint,` (draft)`]})}),(0,g.jsx)(`td`,{children:(0,g.jsx)(`button`,{type:`button`,className:`as-link name`,title:`Show the SQL for ${e.name}`,onClick:()=>I(e),children:e.name})}),(0,g.jsx)(`td`,{className:`dim`,children:e.connection_name||`—`}),(0,g.jsx)(`td`,{className:`dim`,children:e.collection||`—`}),(0,g.jsx)(`td`,{children:t.map((e,t)=>(0,g.jsx)(`span`,{className:`tag`,style:{marginRight:4},children:e.kind===`query`?_.query:e.label},t))}),(0,g.jsx)(`td`,{className:`spark-cell`,children:(0,g.jsx)(h,{label:e.name,items:C.by[e.name],isPending:C.isPending,isError:C.isError})})]},e.name))})]})}):(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`No saved queries`}),(0,g.jsx)(`span`,{children:`This key reaches no saved query by name or collection. It may still run ad-hoc SQL on the connections below.`})]})}),(0,g.jsx)(y,{title:`Databases it can reach`,sub:D.connections===`*`?`all connections`:`${P.length} connection(s)`,children:P.length?(0,g.jsx)(`div`,{style:{overflowX:`auto`},children:(0,g.jsxs)(`table`,{className:`grid`,children:[(0,g.jsx)(`thead`,{children:(0,g.jsx)(`tr`,{children:[`Connection`,`Type`,`Reached by`].map(e=>(0,g.jsx)(`th`,{children:e},e))})}),(0,g.jsx)(`tbody`,{children:P.map(e=>(0,g.jsxs)(`tr`,{"data-conn":e,children:[(0,g.jsx)(`td`,{className:`name`,children:e}),(0,g.jsx)(`td`,{children:F(e)?(0,g.jsx)(`span`,{className:`tag`,children:F(e)}):(0,g.jsx)(`span`,{className:`dim`,children:`—`})}),(0,g.jsx)(`td`,{className:`dim`,children:D.connections===`*`?`all connections`:M.includes(e)?`direct grant (ad-hoc SQL)`:`saved query only`})]},e))})]})}):(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`No connections`}),(0,g.jsx)(`span`,{children:`This key cannot reach any connection directly or through a saved query.`})]})}),(0,g.jsxs)(y,{title:`Tables it can reach`,sub:L.length?`${L.length} allowed`:`all tables on reachable connections`,children:[L.length?(0,g.jsx)(`div`,{style:{padding:`12px 14px`,display:`flex`,flexWrap:`wrap`,gap:6},children:L.map(e=>(0,g.jsx)(`span`,{className:`tag mono`,children:e},e))}):(0,g.jsx)(`div`,{style:{padding:`12px 14px`},className:`dim`,children:`No table allow-list — this key may read every table on the connections it reaches.`}),D.allow_writes?(0,g.jsx)(`div`,{style:{padding:`0 14px 12px`},children:(0,g.jsxs)(`span`,{className:`tag`,children:[`writes: `,D.allowed_write_ops?.length?D.allowed_write_ops.join(`, `):`all operations`]})}):null,D.mask?.length?(0,g.jsx)(`div`,{style:{padding:`0 14px 12px`,display:`flex`,flexWrap:`wrap`,gap:6},children:D.mask.map((e,t)=>(0,g.jsxs)(`span`,{className:`tag`,title:`${e.column}: ${e.action}`,children:[`masked `,e.column]},t))}):null]}),(0,g.jsx)(y,{title:`Usage history`,sub:z?`${D.usage.queries} queries total · ${D.usage.rows.toLocaleString()} rows`:`${D.usage.queries} queries · ${D.usage.errors} failed · since this process started`,children:E.isPending?(0,g.jsx)(a,{text:`Loading usage…`}):E.isError?(0,g.jsx)(`div`,{style:{padding:`12px 14px`},className:`dim`,children:`Usage history isn’t available for this key (the run log may be off, or your role can’t read it).`}):E.data.length?(0,g.jsx)(`div`,{style:{overflowX:`auto`},children:(0,g.jsxs)(`table`,{className:`grid`,children:[(0,g.jsx)(`thead`,{children:(0,g.jsx)(`tr`,{children:[`When`,`What`,`Connection`,`Via`,`Rows`,`Duration`,`Result`].map(e=>(0,g.jsx)(`th`,{children:e},e))})}),(0,g.jsx)(`tbody`,{children:E.data.map((e,t)=>(0,g.jsxs)(`tr`,{children:[(0,g.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:(0,g.jsx)(d,{value:e.executed_at})}),(0,g.jsx)(`td`,{className:`name`,children:e.kind===`adhoc`?(0,g.jsx)(`span`,{className:`dim`,children:`ad-hoc SQL`}):e.query}),(0,g.jsx)(`td`,{className:`dim`,children:e.connection_name||`—`}),(0,g.jsx)(`td`,{className:`dim`,children:e.transport||`—`}),(0,g.jsx)(`td`,{className:`mono`,children:e.rows??`—`}),(0,g.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:e.duration_ms==null?`—`:`${e.duration_ms} ms`}),(0,g.jsx)(`td`,{style:e.status===`error`?{color:`var(--danger)`}:void 0,children:e.status===`error`?(0,g.jsx)(`span`,{title:e.error??void 0,children:e.code||`error`}):`ok`})]},e.request_id??t))})]})}):(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`No runs recorded`}),(0,g.jsx)(`span`,{children:`This key hasn’t run a query yet, or its runs have aged out of the log.`})]})})]})}export{b as ApiKeyDetailPage};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{z as e}from"./client-ZHNiMG8H.js";import{A as t,D as n,c as r,l as i,u as a}from"./index--QHzN3S_.js";import{t as o}from"./Time-Bbp6q-9f.js";import{t as s}from"./reach-BAOLd75Y.js";import{r as c,t as l}from"./KeyActivity-EO98IG9y.js";import{i as u,n as d,o as f}from"./forms-CfcbrGmf.js";import{t as p}from"./icons-CFXE8hAz.js";import{t as m}from"./Managed-BwU65gDK.js";var h=e();function g(e,t){let n=new Date(Date.now()+6048e5).toISOString().slice(0,10);return!!(e&&e>=t&&e<=n)}function _({usage:e}){let t=[`calls`,`rows`].map(t=>({what:t,used:e[t],limit:e[`${t}_limit`]})).filter(e=>e.limit!=null),n=t.reduce((e,t)=>t.used/t.limit>e.used/e.limit?t:e,t[0]),r=Math.min(1,n.used/n.limit),i=e.per===`day`?`today`:`this month`;return(0,h.jsxs)(`div`,{className:`quota-usage`,title:t.map(e=>`${e.used.toLocaleString()} of ${e.limit.toLocaleString()} ${e.what}`).join(` · `)+` ${i} · resets ${e.resets_at}`,children:[(0,h.jsxs)(`span`,{className:`dim`,children:[n.used.toLocaleString(),` / `,n.limit.toLocaleString(),` `,n.what,` `,i]}),(0,h.jsx)(`div`,{className:`quota-bar${r>=1?` full`:r>=.8?` near`:``}`,children:(0,h.jsx)(`span`,{style:{width:`${Math.round(r*100)}%`}})})]})}function v(e){if(!e.queries)return null;let t=[e.queries+(e.queries===1?` query`:` queries`)];return e.errors&&t.push(e.errors+` failed`),t.join(` · `)}function y(){let e=i(),y=r(),b=a(),x=c(),S=d(),C=u(),w=Object.keys(y.data??{}).sort(),T=new Date().toISOString().slice(0,10),E;return E=y.isPending?(0,h.jsx)(n,{text:`Loading API keys…`}):y.isError?(0,h.jsxs)(`div`,{className:`empty`,children:[(0,h.jsx)(`strong`,{children:`Couldn’t load API keys`}),(0,h.jsx)(`span`,{children:y.error.message}),(0,h.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>y.refetch(),children:`Retry`})]}):w.length?(0,h.jsx)(`div`,{style:{overflowX:`auto`},children:(0,h.jsxs)(`table`,{className:`grid`,children:[(0,h.jsx)(`thead`,{children:(0,h.jsx)(`tr`,{children:[`Name`,`Scope`,`Access`,`Rate limit`,`IPs`,`Expires`,`Last used`,`Usage`,`Activity (30d)`,``].map((e,t)=>(0,h.jsx)(`th`,{children:e},t))})}),(0,h.jsx)(`tbody`,{children:w.map(n=>{let r=y.data[n],i=!!(r.expires_at&&r.expires_at<T),a=!i&&g(r.expires_at,T),c=r.allowed_ips??[],u=v(r.usage);return(0,h.jsxs)(`tr`,{"data-name":n,children:[(0,h.jsx)(`td`,{children:(0,h.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,h.jsx)(`span`,{className:`dot ${r.active&&!i?`ok`:`off`}`,title:r.active?i?`Expired`:`Active`:`Revoked`}),(0,h.jsx)(`span`,{className:`name`,title:r.created_at?`Created `+r.created_at+(r.created_from_role?` from role `+r.created_from_role:``):void 0,children:n}),(0,h.jsx)(m,{kind:`api_keys`,name:n}),r.previous_expires_at?(0,h.jsx)(`span`,{className:`tag`,title:`Rotated: the old secret works until then too`,children:`rotating`}):null,r.mask?.length?(0,h.jsx)(`span`,{className:`tag`,title:r.mask.map(e=>`${e.column}: ${e.action}`).join(`, `),children:`masked`}):null]})}),(0,h.jsx)(`td`,{children:(0,h.jsx)(f,{entry:r,collections:b.data?.collections})}),(0,h.jsx)(s,{entry:r}),(0,h.jsx)(`td`,{className:r.rate_limit?`mono`:`mono dim`,style:{whiteSpace:`nowrap`},children:r.rate_limit||`server default`}),(0,h.jsx)(`td`,{className:`dim`,children:c.length?(0,h.jsx)(`span`,{title:c.join(`, `),children:c.length+(c.length===1?` IP`:` IPs`)}):(0,h.jsx)(`span`,{className:`dim`,children:`any`})}),(0,h.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:r.expires_at?(0,h.jsx)(`span`,{style:i?{color:`var(--danger)`}:a?{color:`var(--warn)`}:void 0,children:r.expires_at+(i?` (expired)`:a?` (expires soon)`:``)}):(0,h.jsx)(`span`,{className:`dim`,children:`never`})}),(0,h.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,h.jsx)(o,{value:r.last_used_at,fallback:`never`})}),r.usage_this_period?(0,h.jsxs)(`td`,{style:{whiteSpace:`nowrap`},children:[u,(0,h.jsx)(_,{usage:r.usage_this_period})]}):u?(0,h.jsx)(`td`,{title:`${r.usage.rows}${r.usage.rows===1?` row`:` rows`} returned in total · since this process started`,style:{whiteSpace:`nowrap`},children:u}):(0,h.jsx)(`td`,{className:`dim`,style:{whiteSpace:`nowrap`},children:`No activity yet`}),(0,h.jsx)(`td`,{className:`spark-cell`,children:(0,h.jsx)(l,{label:n,items:x.by[n],isPending:x.isPending,isError:x.isError,to:`/api-keys/${encodeURIComponent(n)}`})}),(0,h.jsx)(`td`,{children:(0,h.jsxs)(`div`,{className:`actions`,children:[(0,h.jsx)(t,{to:`/api-keys/${encodeURIComponent(n)}`,className:`btn ghost sm`,title:`Full details of ${n}`,children:`Details`}),(0,h.jsx)(`button`,{hidden:!e(`access.write`),type:`button`,className:`btn ghost sm icon`,title:`Edit ${n}`,"aria-label":`Edit`,onClick:()=>S.editKey(n),children:(0,h.jsx)(p,{})}),(0,h.jsx)(`button`,{hidden:!e(`access.write`),type:`button`,className:`btn ghost sm danger`,onClick:()=>void C(n),children:`Revoke`})]})})]},n)})})]})}):(0,h.jsxs)(`div`,{className:`empty`,children:[(0,h.jsx)(`strong`,{children:`No scoped API keys yet`}),(0,h.jsx)(`span`,{children:`QUERYAPIGATE_API_KEY is a full-access admin key. Create a scoped one to limit a caller to specific connections, read-only.`}),(0,h.jsx)(`button`,{hidden:!e(`access.write`),type:`button`,className:`btn primary`,onClick:()=>S.newKey(),children:`New API key`})]}),(0,h.jsxs)(h.Fragment,{children:[(0,h.jsxs)(`div`,{className:`page-head`,children:[(0,h.jsxs)(`div`,{className:`titles`,children:[(0,h.jsx)(`h1`,{children:`API keys`}),(0,h.jsx)(`span`,{className:`sub`,id:`apikeys-sub`,children:(w.length?w.length+(w.length===1?` key. `:` keys. `):``)+`Scope each one to connections, collections or roles.`})]}),(0,h.jsx)(`span`,{className:`spacer`}),(0,h.jsx)(`button`,{hidden:!e(`access.write`),id:`new-apikey`,type:`button`,className:`btn primary`,onClick:()=>S.newKey(),children:`New API key`})]}),(0,h.jsx)(`div`,{id:`apikeys-table`,className:`panel`,children:E})]})}export{y as ApiKeysPage};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{B as e,H as t,R as n,n as r,s as i,t as a,v as o,z as s}from"./client-ZHNiMG8H.js";import{t as c}from"./useMutation-DQTq-cNw.js";import{D as l,M as u,N as d,T as f,f as p,k as m}from"./index--QHzN3S_.js";import{t as h}from"./Time-Bbp6q-9f.js";import{t as g}from"./CodeBox-Cwh5KHLp.js";var _=t(e(),1),v=s(),y={pending:`Waiting`,approved:`Approved - not run yet`,running:`Running`,done:`Done`,failed:`Failed`,rejected:`Rejected`,expired:`Expired`};function b(e){let t=p(`writes.approve`,`write requests`);return o({queryKey:[`write-requests`,e||`all`],queryFn:async()=>(await t.check(),i(await r.GET(`/api/v1/write-requests`,{params:{query:e?{status:e}:{}}})).items),enabled:t.ready,retry:!1})}var x=e=>JSON.stringify(e);function S({args:e}){let t=Object.entries(e);return t.length?(0,v.jsx)(`table`,{className:`grid approval-args`,children:(0,v.jsx)(`tbody`,{children:t.map(([e,t])=>(0,v.jsxs)(`tr`,{children:[(0,v.jsx)(`td`,{className:`mono`,children:e}),(0,v.jsx)(`td`,{className:`mono`,children:x(t)})]},e))})}):(0,v.jsx)(`span`,{className:`dim`,children:`none`})}function C({id:e}){let{closeDrawer:t,showError:s,toast:u}=m(),d=n(),[p,b]=(0,_.useState)(``),C=o({queryKey:[`write-requests`,`one`,e],queryFn:async()=>i(await r.GET(`/api/v1/write-requests/{id}`,{params:{path:{id:e}}})),retry:!1}),w=c({mutationFn:async t=>i(t?await r.POST(`/api/v1/write-requests/{id}/approve`,{params:{path:{id:e}}}):await r.POST(`/api/v1/write-requests/{id}/reject`,{params:{path:{id:e}},body:p.trim()?{reason:p.trim()}:{}})),onSuccess:e=>{s(``),d.invalidateQueries({queryKey:[`write-requests`]}),d.invalidateQueries({queryKey:[`alerts`]}),t(),u(e.status===`approved`?`Approved - ${e.caller} may run it now`:`Rejected`)},onError:e=>s(e.message,e instanceof a?{status:e.status}:{})});if(C.isPending)return(0,v.jsx)(l,{});if(C.isError)return(0,v.jsx)(`div`,{className:`hint`,children:C.error.message});let T=C.data,E=T.status===`pending`;return(0,v.jsxs)(`div`,{className:`form`,children:[(0,v.jsxs)(`p`,{className:`hint`,children:[(0,v.jsx)(`strong`,{children:T.caller}),` asked to run `,(0,v.jsx)(`strong`,{children:T.query}),` (version `,T.version,`) on`,` `,(0,v.jsx)(`span`,{className:`mono`,children:T.connection}),`. It changes at most `,T.max_affected_rows,` row`,T.max_affected_rows===1?``:`s`,`; past that it is rolled back.`]}),(0,v.jsx)(f,{label:`SQL`,children:(0,v.jsx)(g,{sql:T.sql})}),(0,v.jsx)(f,{label:`With exactly these arguments`,hint:`An approval is for these alone - no other write.`,children:(0,v.jsx)(S,{args:T.arguments})}),(0,v.jsxs)(`div`,{className:`hint`,children:[`Asked `,(0,v.jsx)(h,{value:T.created_at}),` · `,y[T.status]??T.status,E||T.status===`approved`?(0,v.jsxs)(v.Fragment,{children:[` `,`· expires `,(0,v.jsx)(h,{value:T.expires_at})]}):null,T.decided_by?` · ${T.status===`rejected`?`rejected`:`approved`} by ${T.decided_by}`:``,T.reason?` - ${T.reason}`:``]}),T.result?(0,v.jsx)(f,{label:`Result`,children:(0,v.jsx)(`span`,{className:`mono`,children:x(T.result)})}):null,E?(0,v.jsxs)(v.Fragment,{children:[(0,v.jsx)(f,{id:`approval-reason`,label:`Reason`,hint:`Optional for a rejection - the agent is told.`,children:(0,v.jsx)(`textarea`,{id:`approval-reason`,rows:2,value:p,onChange:e=>b(e.target.value)})}),(0,v.jsxs)(`div`,{className:`form-actions`,children:[(0,v.jsx)(`button`,{type:`button`,className:`btn primary`,disabled:w.isPending,onClick:()=>w.mutate(!0),children:`Approve`}),(0,v.jsx)(`button`,{type:`button`,className:`btn danger`,disabled:w.isPending,onClick:()=>w.mutate(!1),children:`Reject`}),(0,v.jsx)(`button`,{type:`button`,className:`btn`,onClick:t,children:`Close`})]})]}):(0,v.jsx)(`div`,{className:`form-actions`,children:(0,v.jsx)(`button`,{type:`button`,className:`btn`,onClick:t,children:`Close`})})]})}function w(){let{id:e}=d(),t=u(),{openDrawer:n}=m(),[r,i]=(0,_.useState)(`pending`),a=b(r),o=a.data??[],s=e=>n({title:`Approve a write`,kicker:e,wide:!0,content:(0,v.jsx)(C,{id:e})});(0,_.useEffect)(()=>{e&&(s(e),t(`/approvals`,{replace:!0}))},[e]);let c;return c=a.isPending?(0,v.jsx)(l,{text:`Loading write requests…`}):a.isError?(0,v.jsxs)(`div`,{className:`empty`,children:[(0,v.jsx)(`strong`,{children:`Couldn’t load write requests`}),(0,v.jsx)(`span`,{children:a.error.message})]}):o.length?(0,v.jsx)(`div`,{style:{overflowX:`auto`},children:(0,v.jsxs)(`table`,{className:`grid`,children:[(0,v.jsx)(`thead`,{children:(0,v.jsx)(`tr`,{children:[`Asked`,`Agent`,`Tool`,`Arguments`,r===`pending`?`Expires`:`Status`].map(e=>(0,v.jsx)(`th`,{children:e},e))})}),(0,v.jsx)(`tbody`,{children:o.map(e=>(0,v.jsxs)(`tr`,{"data-request":e.id,children:[(0,v.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,v.jsx)(h,{value:e.created_at,fallback:``})}),(0,v.jsx)(`td`,{className:`mono`,children:e.caller}),(0,v.jsx)(`td`,{children:(0,v.jsx)(`button`,{type:`button`,className:`agent-name`,onClick:()=>s(e.id),children:e.query})}),(0,v.jsx)(`td`,{className:`mono dim approval-args-cell`,children:x(e.arguments)}),(0,v.jsx)(`td`,{className:`dim`,style:{whiteSpace:`nowrap`},children:r===`pending`?(0,v.jsx)(h,{value:e.expires_at}):y[e.status]??e.status})]},e.id))})]})}):(0,v.jsxs)(`div`,{className:`empty`,children:[(0,v.jsx)(`strong`,{children:r===`pending`?`No writes are waiting for approval`:`No write requests yet`}),(0,v.jsx)(`span`,{children:`When an AI agent calls a write tool, its write waits here for a person to approve it - the agent is given a link to this screen.`})]}),(0,v.jsxs)(v.Fragment,{children:[(0,v.jsxs)(`div`,{className:`page-head`,children:[(0,v.jsxs)(`div`,{className:`titles`,children:[(0,v.jsx)(`h1`,{children:`Approvals`}),(0,v.jsx)(`span`,{className:`sub`,children:`Writes AI agents asked to make over MCP. Each runs only once a person approves it - with exactly the arguments shown.`})]}),(0,v.jsx)(`span`,{className:`spacer`}),(0,v.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>void a.refetch(),children:`Refresh`})]}),(0,v.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,v.jsxs)(`div`,{className:`panel-bar`,children:[(0,v.jsxs)(`select`,{"aria-label":`Show`,value:r,onChange:e=>i(e.target.value),children:[(0,v.jsx)(`option`,{value:`pending`,children:`Waiting`}),(0,v.jsx)(`option`,{value:``,children:`All`}),(0,v.jsx)(`option`,{value:`done`,children:`Done`}),(0,v.jsx)(`option`,{value:`rejected`,children:`Rejected`}),(0,v.jsx)(`option`,{value:`expired`,children:`Expired`}),(0,v.jsx)(`option`,{value:`failed`,children:`Failed`})]}),(0,v.jsx)(`span`,{className:`spacer`}),(0,v.jsx)(`span`,{className:`panel-count`,children:a.data?`${o.length} request${o.length===1?``:`s`}`:``})]}),c]})]})}export{w as ApprovalsPage};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{B as e,H as t,n,s as r,v as i,z as a}from"./client-ZHNiMG8H.js";import{D as o,k as s}from"./index
|
|
1
|
+
import{B as e,H as t,n,s as r,v as i,z as a}from"./client-ZHNiMG8H.js";import{D as o,k as s}from"./index--QHzN3S_.js";import{t as c}from"./Time-Bbp6q-9f.js";var l=t(e(),1),u=a();function d(e){return/^(create|load|save)/.test(e)?`ok`:/^(delete|unload|revoke)/.test(e)?`bad`:``}function f(e){if(e==null||e===``)return`none`;let t=e=>e&&typeof e==`object`?JSON.stringify(e):String(e);return Array.isArray(e)?e.length?e.map(t).join(`, `):`none`:t(e)}var p=e=>e==null||e===``||Array.isArray(e)&&!e.length,m=e=>!!e&&typeof e==`object`&&!Array.isArray(e)&&(`from`in e||`to`in e);function h({changes:e}){let t=e&&typeof e==`object`?e:null,n=t?Object.keys(t).sort():[],r=n.filter(e=>m(t[e])||!p(t[e]));return r.length?(0,u.jsx)(`div`,{children:r.map(e=>{let n=t[e];return(0,u.jsx)(`div`,{className:`mono`,style:{fontSize:12,color:`var(--ink-2)`},children:m(n)?`${e}: ${f(n.from)} → ${f(n.to)}`:`${e}: ${f(n)}`},e)})}):(0,u.jsx)(`span`,{className:`dim`,children:n.length?`no other fields set`:`—`})}function g(e){let t=new Blob([JSON.stringify(e,null,2)+`
|
|
2
2
|
`],{type:`application/json`}),n=document.createElement(`a`);n.href=URL.createObjectURL(t),n.download=`queryapigate-audit-log.json`,n.click(),setTimeout(()=>URL.revokeObjectURL(n.href),0)}function _(){let{toast:e}=s(),t=i({queryKey:[`audit`],queryFn:async()=>r(await n.GET(`/api/v1/audit`)),retry:!1}),[a,f]=(0,l.useState)(``),[p,m]=(0,l.useState)(``),_=t.data?.items??[],v=p.trim().toLowerCase(),y=_.filter(e=>(!a||e.action===a)&&(!v||[e.timestamp,e.actor,e.target].join(` `).toLowerCase().includes(v))),b=t.data?.actions??[],x,S=``;return t.isPending?x=(0,u.jsx)(o,{text:`Loading audit log…`}):t.isError?x=(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`Couldn’t load the audit log`}),(0,u.jsx)(`span`,{children:t.error.message}),(0,u.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>t.refetch(),children:`Retry`})]}):_.length?(S=y.length===_.length?_.length+(_.length===1?` entry`:` entries`):`${y.length} of ${_.length} entries`,x=y.length?(0,u.jsx)(`div`,{style:{overflowX:`auto`},children:(0,u.jsxs)(`table`,{className:`grid`,children:[(0,u.jsx)(`thead`,{children:(0,u.jsx)(`tr`,{children:[`Time`,`Actor`,`Action`,`Target`,`Changes`].map(e=>(0,u.jsx)(`th`,{children:e},e))})}),(0,u.jsx)(`tbody`,{children:y.map((e,t)=>(0,u.jsxs)(`tr`,{children:[(0,u.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,u.jsx)(c,{value:e.timestamp,fallback:``})}),(0,u.jsx)(`td`,{className:`mono`,children:e.actor||`-`}),(0,u.jsx)(`td`,{children:(0,u.jsx)(`span`,{className:`tag act `+d(e.action),children:e.action||``})}),(0,u.jsx)(`td`,{children:(0,u.jsx)(`span`,{className:`name`,children:e.target||``})}),(0,u.jsx)(`td`,{children:(0,u.jsx)(h,{changes:e.changes})})]},t))})]})}):(0,u.jsx)(`div`,{className:`empty`,children:(0,u.jsx)(`span`,{children:`No entries match this filter.`})})):x=(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`No administrative changes recorded yet`}),(0,u.jsx)(`span`,{children:`Creating or changing a connection, saved query or API key will appear here.`})]}),(0,u.jsxs)(u.Fragment,{children:[(0,u.jsxs)(`div`,{className:`page-head`,children:[(0,u.jsxs)(`div`,{className:`titles`,children:[(0,u.jsx)(`h1`,{children:`Audit log`}),(0,u.jsx)(`span`,{className:`sub`,id:`auditlog-sub`,children:`Administrative actions, newest first.`+(t.data?` Keeps the last ${t.data.retention} entries.`:``)})]}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`button`,{id:`refresh-auditlog`,type:`button`,className:`btn`,onClick:()=>void t.refetch(),children:`Refresh`}),(0,u.jsx)(`button`,{id:`export-auditlog`,type:`button`,className:`btn`,onClick:()=>{g(y),e(`Exported `+y.length+(y.length===1?` entry`:` entries`))},children:`Export`})]}),(0,u.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,u.jsxs)(`div`,{className:`panel-bar`,children:[(0,u.jsxs)(`select`,{id:`auditlog-action-filter`,"aria-label":`Action`,value:b.includes(a)?a:``,onChange:e=>f(e.target.value),children:[(0,u.jsx)(`option`,{value:``,children:`All actions`}),b.map(e=>(0,u.jsx)(`option`,{value:e,children:e},e))]}),(0,u.jsx)(`input`,{id:`auditlog-filter`,className:`search`,style:{width:260},type:`search`,placeholder:`Filter by actor, target, time…`,value:p,onChange:e=>m(e.target.value)}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`span`,{className:`panel-count`,id:`auditlog-count`,children:S})]}),(0,u.jsx)(`div`,{id:`auditlog-table`,children:x})]})]})}export{_ as AuditLogPage};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{B as e,H as t,R as n,c as r,n as i,r as a,s as o,v as s,z as c}from"./client-ZHNiMG8H.js";import{M as l,_ as u,k as d,s as f}from"./index--QHzN3S_.js";import{a as p,t as m}from"./KeyActivity-EO98IG9y.js";import{i as h,n as g,t as _}from"./StatTile-Cft4kJPH.js";import{n as v,t as y}from"./Results-3iK2cKFL.js";var b=t(e(),1),x=c();function S(){return s({queryKey:[`cache`,`entries`],queryFn:async()=>o(await i.GET(`/api/v1/cache/entries`)).items,retry:!1})}function C(){let e=l(),t=n(),r=u(),i=g(),a=f(),o=p(),s=i.data??[],c=r.data?.find(e=>e.id===`cache`)?.rows[0]?.value??`…`,d=h(s,`queryapigate_cache_hits_total`),v=h(s,`queryapigate_cache_misses_total`),y=d+v,b=(a.data??[]).filter(e=>(e.cache_ttl??0)>0);return(0,x.jsxs)(x.Fragment,{children:[(0,x.jsxs)(`div`,{className:`page-head`,children:[(0,x.jsxs)(`div`,{className:`titles`,children:[(0,x.jsx)(`h1`,{children:`Caching`}),(0,x.jsxs)(`span`,{className:`sub`,children:[`The response cache backing saved queries' `,(0,x.jsx)(`code`,{children:`cache_ttl`}),` - see the Response cache row in Settings for the backend itself.`]})]}),(0,x.jsx)(`span`,{className:`spacer`}),(0,x.jsx)(`button`,{id:`refresh-caching`,type:`button`,className:`btn`,onClick:()=>{i.refetch(),t.invalidateQueries({queryKey:[`cache`,`entries`]})},children:`Refresh`})]}),(0,x.jsxs)(`div`,{id:`caching-body`,children:[(0,x.jsxs)(`div`,{className:`stat-tiles`,children:[(0,x.jsx)(_,{label:`Cache backend`,value:c}),(0,x.jsx)(_,{label:`Entries`,value:h(s,`queryapigate_cache_entries`)}),(0,x.jsx)(_,{label:`Hit rate`,value:y?(100*d/y).toFixed(1)+`%`:`—`}),(0,x.jsx)(_,{label:`Hits`,value:d}),(0,x.jsx)(_,{label:`Misses`,value:v})]}),(0,x.jsx)(`p`,{className:`sub-h`,style:{marginTop:14},children:`Saved queries with cache_ttl set`}),b.length?(0,x.jsx)(`div`,{className:`panel`,style:{overflowX:`auto`},children:(0,x.jsxs)(`table`,{className:`grid`,children:[(0,x.jsx)(`thead`,{children:(0,x.jsx)(`tr`,{children:[`Query`,`Collection`,`Connection`,`cache_ttl`,`Activity (30d)`].map((e,t)=>(0,x.jsx)(`th`,{className:t===3?`num`:``,children:e},e))})}),(0,x.jsx)(`tbody`,{children:b.map(t=>(0,x.jsxs)(`tr`,{children:[(0,x.jsx)(`td`,{children:(0,x.jsx)(`button`,{type:`button`,className:`target`,onClick:()=>e(`/queries/${encodeURIComponent(t.name)}`),children:t.name})}),(0,x.jsx)(`td`,{children:t.collection||(0,x.jsx)(`span`,{className:`dim`,children:`—`})}),(0,x.jsx)(`td`,{className:`mono`,children:t.connection_name||`—`}),(0,x.jsx)(`td`,{className:`mono num`,children:t.cache_ttl+`s`}),(0,x.jsx)(`td`,{className:`spark-cell`,children:(0,x.jsx)(m,{label:t.name,items:o.by[t.name],isPending:o.isPending,isError:o.isError})})]},t.name))})]})}):(0,x.jsx)(`div`,{className:`hint`,children:`No saved query declares a cache_ttl yet - set one on a read-only query to start caching its responses.`})]}),(0,x.jsx)(`div`,{id:`cache-entries-panel`,children:(0,x.jsx)(T,{})})]})}function w(e){return e<1024?e+` B`:(e/1024).toFixed(1)+` KB`}function T(){let e=n(),{openDrawer:t,showError:a,toast:o}=d(),s=S().data??[],c=()=>void e.invalidateQueries({queryKey:[`cache`,`entries`]}),l=e=>a(e.message),u=async()=>{if(window.confirm(`Evict all ${s.length} cache entries? The next call to each query will re-run for real.`))try{r(await i.DELETE(`/api/v1/cache/entries`)),o(`Cache cleared`),c()}catch(e){l(e)}},f=async e=>{try{r(await i.DELETE(`/api/v1/cache/entries/{key}`,{params:{path:{key:e}}})),o(`Entry deleted`),c()}catch(e){l(e)}};return(0,x.jsxs)(x.Fragment,{children:[(0,x.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:12},children:[(0,x.jsxs)(`div`,{className:`set-head`,style:{marginTop:14},children:[(0,x.jsx)(`h2`,{children:`Cache entries`}),(0,x.jsx)(`span`,{children:`${s.length}${s.length===1?` live entry`:` live entries`} right now.`})]}),(0,x.jsx)(`span`,{className:`spacer`}),(0,x.jsx)(`button`,{type:`button`,className:`btn sm`,disabled:!s.length,onClick:()=>void u(),children:`Clear cache`})]}),s.length?(0,x.jsx)(`div`,{className:`panel`,style:{overflowX:`auto`,marginTop:8},children:(0,x.jsxs)(`table`,{className:`grid`,children:[(0,x.jsx)(`thead`,{children:(0,x.jsx)(`tr`,{children:[`Query`,`Version`,`Connection`,`Format`,`Content type`,`Size`,`TTL left`,``].map((e,t)=>(0,x.jsx)(`th`,{className:t>=4&&t<=6?`num`:``,children:e},t))})}),(0,x.jsx)(`tbody`,{children:s.map(e=>{let n=e.meta??{};return(0,x.jsxs)(`tr`,{children:[(0,x.jsx)(`td`,{children:(0,x.jsx)(`button`,{type:`button`,className:`target`,onClick:()=>t({title:n.name||`Cache entry`,kicker:e.key,content:(0,x.jsx)(D,{entry:e})}),children:n.name||e.key.slice(0,12)+`…`})}),(0,x.jsx)(`td`,{className:`mono`,children:n.version===void 0?`—`:`v`+n.version}),(0,x.jsx)(`td`,{className:`mono`,children:n.connection||`—`}),(0,x.jsx)(`td`,{className:`mono`,children:n.format||`—`}),(0,x.jsx)(`td`,{className:`mono num`,children:e.content_type}),(0,x.jsx)(`td`,{className:`mono num`,children:w(e.size_bytes)}),(0,x.jsx)(`td`,{className:`mono num`,children:Math.max(0,Math.round(e.ttl_remaining_s))+`s`}),(0,x.jsx)(`td`,{children:(0,x.jsx)(`button`,{type:`button`,className:`btn sm ghost`,onClick:()=>void f(e.key),children:`Delete`})})]},e.key)})})]})}):(0,x.jsx)(`div`,{className:`hint`,children:`Nothing cached right now - call a query with a cache_ttl set, then check back.`})]})}var E=e=>({ok:!1,status:0,statusText:``,contentType:``,headers:[],page:1,size:0,hasMore:!1,elapsed:0,format:``,error:e});function D({entry:e}){let{showError:t}=d(),n=e.meta?.name||`cache-entry`,r=e.meta?.format||`json`,[i,o]=(0,b.useState)(null);return(0,b.useEffect)(()=>{let i=!0,s=performance.now();return a(`/api/v1/cache/entries/${encodeURIComponent(e.key)}`).then(e=>v(e,{page:1,size:0,format:r,elapsed:Math.round(performance.now()-s),filename:n})).then(e=>{i&&(e.error&&t(e.error,{status:e.status}),o(e))},e=>i&&o(E(`Network error: `+e.message))),()=>{i=!1}},[e.key,r,n,t]),(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(`div`,{className:`hint`,children:`The exact cached body, served with its real Content-Type - what a caller receives on a hit.`}),(0,x.jsx)(y,{result:i,running:!i,filename:n,onPage:()=>{},onPageSize:()=>{},layout:`drawer`})]})}export{C as CachingPage};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import{z as e}from"./client-ZHNiMG8H.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
|
-
`)}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(`
|
|
4
|
+
`)}var l=[`mysql`,`postgres`,`clickhouse`,`sqlite`,`duckdb`,`trino`],u=e();function d({sql:e}){return(0,u.jsx)(`div`,{className:`codebox`,children:(0,u.jsx)(`div`,{className:`ln-rows`,children:e.split(`
|
|
5
5
|
`).map((e,t)=>(0,u.jsxs)(`div`,{className:`ln-r`,children:[(0,u.jsx)(`span`,{className:`ln-n`,children:t+1}),(0,u.jsx)(`span`,{className:`ln-t`,children:e?r(e).map((e,t)=>e.cls?(0,u.jsx)(`span`,{className:e.cls,children:e.text},t):e.text):` `})]},t))})})}export{i as a,a as i,l as n,c as r,d as t};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{B as e,H as t,R as n,c as r,l as i,n as a,s as o,t as s,v as c,z as l}from"./client-ZHNiMG8H.js";import{t as u}from"./useMutation-DQTq-cNw.js";import{D as d,E as f,M as p,T as m,k as h,l as g,w as _}from"./index--QHzN3S_.js";import{t as v}from"./Time-Bbp6q-9f.js";import{n as y,t as b}from"./KeyActivity-EO98IG9y.js";import{n as x,t as S}from"./icons-CFXE8hAz.js";import{t as ee}from"./Managed-BwU65gDK.js";var C=t(e(),1),w=l(),T=[`mysql`,`postgres`,`clickhouse`,`sqlite`,`h2`,`duckdb`,`mongo`,`trino`],E=`files`,D=[`h2`,`jdbc`,`trino`],O=[`mysql`,`postgres`,`clickhouse`,`mongo`,`trino`];function k(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 A(e){return e.host?e.host+(e.port?`:`+e.port:``):``}function j(){return c({queryKey:[`connections`],queryFn:async()=>o(await a.GET(`/api/v1/connections`)).items,retry:!1})}function M(e){return c({queryKey:[`connections`,`deleted`],queryFn:async()=>o(await a.GET(`/api/v1/connections/deleted`)).items,enabled:e,retry:!1})}function N(){let e=g(),t=p(),{openDrawer:n}=h(),r=j(),[i,a]=(0,C.useState)(`all`),[o,s]=(0,C.useState)(``),c=M(!0),l=y(),u=r.data??[],f=u.filter(e=>e.active).length,m=c.data??[],T=o.trim().toLowerCase(),E=()=>n({title:`New connection`,kicker:`POST /api/v1/connections`,content:(0,w.jsx)(P,{})}),D,O=null;if(i===`deleted`){let e=m.filter(e=>!T||[e.name,e.deleted_by,e.reason,e.db,e.host].join(` `).toLowerCase().includes(T));m.length?(O=(0,w.jsx)(`span`,{children:`Showing ${e.length} of ${m.length}${m.length===1?` deleted connection`:` deleted connections`}`}),D=e.length?(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`,`Type`,`Host`,`Database`,`Deleted at`,`Deleted by`,`Reason`].map(e=>(0,w.jsx)(`th`,{children:e},e))})}),(0,w.jsx)(`tbody`,{children:e.map((e,t)=>(0,w.jsxs)(`tr`,{children:[(0,w.jsx)(`td`,{children:(0,w.jsx)(`span`,{className:`name`,children:e.name})}),(0,w.jsx)(`td`,{children:(0,w.jsx)(`span`,{className:`tag`,children:e.db||`?`})}),(0,w.jsx)(`td`,{className:`mono`,style:{whiteSpace:`nowrap`},children:A(e)||(0,w.jsx)(`span`,{className:`dim`,children:`—`})}),(0,w.jsx)(`td`,{className:`mono`,children:e.database||``}),(0,w.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,w.jsx)(v,{value:e.deleted_at})}),(0,w.jsx)(`td`,{className:`mono`,children:e.deleted_by||`—`}),(0,w.jsx)(`td`,{children:e.reason||`—`})]},`${e.name}-${t}`))})]})}):(0,w.jsx)(_,{children:`No deleted connections match “${T}”.`})):D=(0,w.jsx)(_,{title:`No deleted connections`,children:`Every connection deletion is recorded here, with who did it, when, and why.`})}else if(r.isPending)D=(0,w.jsx)(d,{text:`Loading connections…`});else if(r.isError)D=(0,w.jsx)(_,{title:`Couldn’t load connections`,children:(0,w.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>r.refetch(),children:`Retry`})});else if(!u.length)D=(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`strong`,{children:`No connections yet`}),(0,w.jsx)(`span`,{children:`Add a MySQL, PostgreSQL, ClickHouse, SQLite or H2 database to start running SQL.`}),(0,w.jsx)(`button`,{hidden:!e(`connections.write`),type:`button`,className:`btn primary`,onClick:E,children:`New connection`})]});else{let r=u.filter(e=>i===`active`&&!e.active||i===`inactive`&&e.active?!1:!T||[e.name,e.db,e.host,e.database,e.user].join(` `).toLowerCase().includes(T));O=(0,w.jsx)(`span`,{children:`Showing ${r.length} of ${u.length}${u.length===1?` connection`:` connections`}`}),D=r.length?(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`,`Type`,`Host`,`Database`,`User`,`Status`,`Usage`,`Activity (30d)`,`Created`,`Last modified`,``].map((e,t)=>(0,w.jsx)(`th`,{children:e},t))})}),(0,w.jsx)(`tbody`,{children:r.map(r=>{let i=k(r.usage);return(0,w.jsxs)(`tr`,{"data-name":r.name,children:[(0,w.jsx)(`td`,{children:(0,w.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,w.jsx)(`span`,{className:`name`,children:r.name}),(0,w.jsx)(ee,{kind:`connections`,name:r.name}),r.example&&(0,w.jsx)(`span`,{className:`tag example`,title:`Installed by the example APIs — removing the examples removes it`,children:`example`})]})}),(0,w.jsx)(`td`,{children:(0,w.jsx)(`span`,{className:`tag`,children:r.db||`?`})}),(0,w.jsx)(`td`,{className:`mono`,style:{whiteSpace:`nowrap`},children:A(r)||(0,w.jsx)(`span`,{className:`dim`,children:`—`})}),(0,w.jsx)(`td`,{className:`mono`,children:r.database||``}),(0,w.jsx)(`td`,{className:`mono dim`,children:r.user||`—`}),(0,w.jsx)(`td`,{children:(0,w.jsxs)(`span`,{className:`pill ${r.active?`ok`:`off`}`,children:[(0,w.jsx)(`i`,{}),r.active?`Active`:`Inactive`]})}),i?(0,w.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,w.jsx)(`td`,{className:`dim`,style:{whiteSpace:`nowrap`},children:`No activity yet`}),(0,w.jsx)(`td`,{className:`spark-cell`,children:(0,w.jsx)(b,{label:r.name,items:l.by[r.name],isPending:l.isPending,isError:l.isError})}),(0,w.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,w.jsx)(v,{value:r.created_at})}),(0,w.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,w.jsx)(v,{value:r.updated_at})}),(0,w.jsx)(`td`,{children:(0,w.jsxs)(`div`,{className:`actions`,children:[(0,w.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,w.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,w.jsx)(P,{name:r.name})}),children:(0,w.jsx)(S,{})}),(0,w.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,w.jsx)(I,{name:r.name})}),children:(0,w.jsx)(x,{})})]})})]},r.name)})})]})}):(0,w.jsx)(_,{children:T?`No connections match “${T}”.`:`No ${i} connections.`})}let N=[[`all`,`All`,u.length],[`active`,`Active`,f],[`inactive`,`Inactive`,u.length-f],[`deleted`,`Deleted`,m.length]];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:`Connections`}),(0,w.jsx)(`span`,{className:`sub`,children:`Databases this gateway can run saved queries against.`})]}),(0,w.jsx)(`span`,{className:`spacer`}),(0,w.jsx)(`button`,{id:`new-connection`,type:`button`,className:`btn primary`,hidden:i===`deleted`||!e(`connections.write`),onClick:E,children:`New connection`})]}),(0,w.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,w.jsxs)(`div`,{className:`panel-bar`,children:[(0,w.jsx)(`div`,{className:`seg`,id:`conn-tabs`,role:`group`,"aria-label":`Connection status`,children:N.map(([e,t,n])=>(0,w.jsxs)(`button`,{type:`button`,className:i===e?`on`:``,onClick:()=>a(e),children:[t,(0,w.jsx)(`span`,{className:`n`,children:n})]},e))}),(0,w.jsx)(`span`,{className:`spacer`}),(0,w.jsx)(`input`,{id:`conn-filter`,className:`search`,type:`search`,placeholder:i===`deleted`?`Filter by name, actor, reason…`:`Filter by name, type, host…`,value:o,onChange:e=>s(e.target.value)})]}),(0,w.jsx)(`div`,{id:`connections-table`,children:D}),(0,w.jsx)(`div`,{className:`panel-foot`,id:`connections-foot`,children:O})]})]})}function te(e,t){t instanceof s?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 P({name:e}){let t=!!e,r=n(),{closeDrawer:o,showError:s,toast:l}=h(),u=c({queryKey:[`connections`,`detail`,e],queryFn:async()=>i(await a.GET(`/api/v1/connections/{name}`,{params:{path:{name:e}}})),enabled:t,retry:!1});return t&&u.isPending?(0,w.jsx)(d,{}):t&&u.isError?(0,w.jsx)(`div`,{className:`hint`,children:u.error.message}):(0,w.jsx)(F,{name:e,existing:u.data?.data,etag:u.data?.etag??null,onSaved:e=>{s(``),o(),l((t?`Saved `:`Created `)+e),r.invalidateQueries({queryKey:[`connections`]})}})}function F({name:e,existing:t,etag:n,onSaved:r}){let i=!!e,{closeDrawer:c,showError:l}=h(),[d,p]=(0,C.useState)(e??``),[g,_]=(0,C.useState)(t?.db===`duckdb`&&t.database===`:memory:`?E:t?.db??`mysql`),v=g===E,y=v?`duckdb`:g,[b,x]=(0,C.useState)(t?.host??``),[S,ee]=(0,C.useState)(t?.port==null?``:String(t.port)),[k,A]=(0,C.useState)(t?.user??``),[j,M]=(0,C.useState)(i?t?.password??``:``),[N,P]=(0,C.useState)(!1),[F,I]=(0,C.useState)(t?.database??``),[L,ne]=(0,C.useState)(!t||t.active),[R,re]=(0,C.useState)(null),[z,B]=(0,C.useState)(null),[V,H]=(0,C.useState)(null),[ie,ae]=(0,C.useState)(!1),U=t?.options??{},[W,oe]=(0,C.useState)((U.allowed_paths??[]).join(`
|
|
2
|
+
`)),[G,se]=(0,C.useState)(U.storage??`s3`),[K,ce]=(0,C.useState)(U.region??``),[q,le]=(0,C.useState)(U.endpoint??``),[J,ue]=(0,C.useState)(U.url_style??``),[Y,de]=(0,C.useState)(U.use_ssl!==!1),[X,fe]=(0,C.useState)(U.views?JSON.stringify(U.views,null,2):``),[pe,me]=(0,C.useState)(t?U.auto_views===!0:null),he=pe??v,Z=y===`duckdb`,Q=e=>{if(!Z)return{};let t=W.split(`
|
|
3
|
+
`).map(e=>e.trim()).filter(Boolean),n=e;if(X.trim())try{n=JSON.parse(X)}catch{throw Error(`Views must be a JSON object of {"name": "SELECT ..."}.`)}return{allowed_paths:t.length?t:e,storage:G===`s3`?e:G,region:K.trim()||e,endpoint:q.trim()||e,url_style:J||e,use_ssl:Y?e:!1,auto_views:he?!0:e,views:n}},ge=()=>({db:y,host:b||void 0,port:S?Number(S):void 0,user:k||void 0,password:j,database:v?`:memory:`:F||void 0,...i?{name:e}:{},...Q(void 0)}),$=u({mutationFn:async e=>{let t=e===`test`?`/api/v1/connections/test`:`/api/v1/connections/databases`,n=await a.POST(t,{body:ge()});return{kind:e,data:o(n)}}}),_e=e=>`✗ `+(e instanceof s?e.message+(e.detail?`: `+e.detail:``):`Network error`),ve=()=>{B(null),$.mutate(`databases`,{onSuccess:({data:e})=>{let t=e.databases;re(t),B({ok:!0,text:`✓ ${t.length}${t.length===1?` database`:` databases`}`})},onError:e=>B({ok:!1,text:_e(e)})})},ye=()=>{H(null),$.mutate(`test`,{onSuccess:({data:e})=>{let t=e.elapsed_ms;H({ok:!0,text:`✓ Connected`+(t===void 0?``:` (${t} ms)`)})},onError:e=>H({ok:!1,text:_e(e)})})},be=O.includes(y);i&&be&&!ie&&(ae(!0),setTimeout(ve,0));let xe=u({mutationFn:async()=>{if(i){let t={db:y,active:L,host:b||null,port:S?Number(S):null,user:k||null,password:j,database:v?`:memory:`:F||null,...Q(null)};return o(await a.PATCH(`/api/v1/connections/{name}`,{params:{path:{name:e},header:n?{"If-Match":n}:void 0},body:t})),e}let t={...ge(),name:d.trim(),active:L};return o(await a.POST(`/api/v1/connections`,{body:t})).name},onSuccess:r,onError:e=>e instanceof s?te(l,e):l(e.message)});return(0,w.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{if(e.preventDefault(),!i&&!d.trim()){l(`A connection name is required.`,{errors:{name:`This field is required`}});return}xe.mutate()},children:[(0,w.jsx)(m,{id:`c-name`,label:`Name`,hint:i?`Renaming isn’t supported — create a new connection instead.`:`Referenced as connection_name by queries and the API.`,children:(0,w.jsx)(`input`,{id:`c-name`,placeholder:`reporting-db`,autoComplete:`off`,spellCheck:!1,disabled:i,autoFocus:!i,value:d,onChange:e=>p(e.target.value)})}),(0,w.jsx)(m,{id:`c-db`,label:`Database type`,children:(0,w.jsxs)(`select`,{id:`c-db`,value:g,onChange:e=>_(e.target.value),children:[T.map(e=>(0,w.jsx)(`option`,{value:e,children:D.includes(e)?`${e} (experimental)`:e},e)),(0,w.jsx)(`option`,{value:E,children:`Files (Parquet, CSV, JSON)`})]})}),(0,w.jsxs)(`div`,{className:`grid-host`,hidden:Z,children:[(0,w.jsx)(m,{id:`c-host`,label:`Host`,children:(0,w.jsx)(`input`,{id:`c-host`,placeholder:`localhost`,autoComplete:`off`,spellCheck:!1,autoFocus:i,value:b,onChange:e=>x(e.target.value)})}),(0,w.jsx)(m,{id:`c-port`,label:`Port`,children:(0,w.jsx)(`input`,{id:`c-port`,type:`number`,autoComplete:`off`,value:S,onChange:e=>ee(e.target.value)})})]}),(0,w.jsxs)(`div`,{className:`grid2`,children:[(0,w.jsx)(m,{id:`c-user`,label:Z?`Access key ID`:`User`,hint:Z?`For object storage; leave empty for public files.`:void 0,children:(0,w.jsx)(`input`,{id:`c-user`,autoComplete:`off`,spellCheck:!1,value:k,onChange:e=>A(e.target.value)})}),(0,w.jsx)(m,{id:`c-password`,label:Z?`Secret access key`:`Password`,hint:i?`Leave the mask to keep the stored password.`:"${ENV_VAR} references are resolved on the server.",children:(0,w.jsxs)(`div`,{className:`password-field`,children:[(0,w.jsx)(`input`,{id:`c-password`,type:N?`text`:`password`,autoComplete:`off`,spellCheck:!1,value:j,onChange:e=>M(e.target.value)}),(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm`,"aria-label":(N?`Hide`:`Show`)+` password`,onClick:()=>P(e=>!e),children:N?`Hide`:`Show`})]})})]}),(0,w.jsx)(`div`,{hidden:v,children:(0,w.jsx)(m,{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,w.jsx)(`div`,{children:R?(0,w.jsx)(`select`,{id:`c-database`,value:R.includes(F)?F:R[0]??``,onChange:e=>I(e.target.value),children:R.map(e=>(0,w.jsx)(`option`,{value:e,children:e},e))}):(0,w.jsx)(`input`,{id:`c-database`,placeholder:`SQLite, DuckDB and H2 take a file path`,autoComplete:`off`,spellCheck:!1,value:F,onChange:e=>I(e.target.value)})})})}),(0,w.jsxs)(`div`,{className:`test-row`,children:[(0,w.jsx)(`button`,{type:`button`,className:`btn sm ghost`,hidden:!be,disabled:$.isPending,onClick:ve,children:$.isPending&&$.variables===`databases`?`Loading…`:`Load databases…`}),(0,w.jsx)(`span`,{className:`test-result`,children:z&&(0,w.jsx)(`span`,{className:z.ok?`test-ok`:`test-fail`,children:z.text})})]}),Z&&(0,w.jsxs)(`fieldset`,{className:`duck-files`,children:[(0,w.jsx)(`legend`,{children:`Files it may read`}),(0,w.jsx)(m,{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,w.jsx)(`textarea`,{id:`c-paths`,rows:3,spellCheck:!1,placeholder:`s3://sales/2026/
|
|
4
|
+
https://data.example.com/prices.parquet`,value:W,onChange:e=>oe(e.target.value)})}),(0,w.jsxs)(`div`,{className:`grid2`,children:[(0,w.jsx)(m,{id:`c-storage`,label:`Object storage`,children:(0,w.jsxs)(`select`,{id:`c-storage`,value:G,onChange:e=>se(e.target.value),children:[(0,w.jsx)(`option`,{value:`s3`,children:`S3 or S3-compatible`}),(0,w.jsx)(`option`,{value:`gcs`,children:`Google Cloud Storage`}),(0,w.jsx)(`option`,{value:`r2`,children:`Cloudflare R2`})]})}),(0,w.jsx)(m,{id:`c-region`,label:`Region`,children:(0,w.jsx)(`input`,{id:`c-region`,placeholder:`us-east-1`,spellCheck:!1,value:K,onChange:e=>ce(e.target.value)})})]}),(0,w.jsxs)(`div`,{className:`grid2`,children:[(0,w.jsx)(m,{id:`c-endpoint`,label:`Endpoint`,hint:`Only for an S3-compatible service, e.g. minio.internal:9000.`,children:(0,w.jsx)(`input`,{id:`c-endpoint`,spellCheck:!1,value:q,onChange:e=>le(e.target.value)})}),(0,w.jsx)(m,{id:`c-url-style`,label:`URL style`,children:(0,w.jsxs)(`select`,{id:`c-url-style`,value:J,onChange:e=>ue(e.target.value),children:[(0,w.jsx)(`option`,{value:``,children:`Default`}),(0,w.jsx)(`option`,{value:`vhost`,children:`Virtual-hosted (bucket.host)`}),(0,w.jsx)(`option`,{value:`path`,children:`Path (host/bucket)`})]})})]}),(0,w.jsxs)(`label`,{className:`switch`,children:[(0,w.jsx)(`input`,{id:`c-auto-views`,type:`checkbox`,checked:he,onChange:e=>me(e.target.checked)}),`A view for each file and folder`,(0,w.jsx)(`span`,{className:`hint`,children:`— orders.parquet becomes orders; a folder of files, one view over all of them`})]}),(0,w.jsxs)(`label`,{className:`switch`,children:[(0,w.jsx)(`input`,{id:`c-ssl`,type:`checkbox`,checked:Y,onChange:e=>de(e.target.checked)}),`Use HTTPS for object storage`]}),(0,w.jsx)(m,{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,w.jsx)(`textarea`,{id:`c-views`,rows:3,spellCheck:!1,value:X,onChange:e=>fe(e.target.value)})})]}),(0,w.jsxs)(`label`,{className:`switch`,children:[(0,w.jsx)(`input`,{id:`c-active`,type:`checkbox`,checked:L,onChange:e=>ne(e.target.checked)}),`Active`,(0,w.jsx)(`span`,{className:`hint`,children:`— inactive connections refuse queries`})]}),(0,w.jsxs)(`div`,{className:`test-row`,children:[(0,w.jsx)(`button`,{type:`button`,className:`btn`,disabled:$.isPending,onClick:ye,children:$.isPending&&$.variables===`test`?`Testing…`:`Test connection`}),(0,w.jsx)(`span`,{className:`test-result`,children:V&&(0,w.jsx)(`span`,{className:V.ok?`test-ok`:`test-fail`,children:V.text})})]}),i&&t?.created_at&&(0,w.jsx)(`div`,{className:`hint`,children:`Created `+t.created_at+(t.updated_at&&t.updated_at!==t.created_at?` · last edited `+t.updated_at:``)}),(0,w.jsx)(f,{onCancel:c,children:(0,w.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:xe.isPending,children:i?`Save`:`Create`})})]})}function I({name:e}){let t=n(),{closeDrawer:i,showError:o,toast:s}=h(),[c,l]=(0,C.useState)(``),[d,p]=(0,C.useState)(``),g=u({mutationFn:async()=>r(await a.DELETE(`/api/v1/connections/{name}`,{params:{path:{name:e}},body:{reason:c.trim()}})),onSuccess:()=>{i(),s(`Deleted `+e),t.invalidateQueries({queryKey:[`connections`]})},onError:e=>te(o,e)}),_=!!c.trim()&&d===e;return(0,w.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{e.preventDefault(),_&&g.mutate()},children:[(0,w.jsxs)(`p`,{className:`d-desc`,children:[`This deletes `,(0,w.jsx)(`span`,{className:`name`,children:e}),` permanently. Every saved query that uses it will stop working. It moves to the Deleted tab, with the reason and who deleted it.`]}),(0,w.jsx)(m,{id:`del-reason`,label:`Reason`,hint:`Recorded in the audit log - required.`,children:(0,w.jsx)(`textarea`,{id:`del-reason`,placeholder:`Why is this connection being deleted?`,autoFocus:!0,value:c,onChange:e=>l(e.target.value)})}),(0,w.jsx)(m,{id:`del-confirm`,label:`Type "${e}" to confirm`,children:(0,w.jsx)(`input`,{id:`del-confirm`,type:`text`,autoComplete:`off`,spellCheck:!1,placeholder:e,value:d,onChange:e=>p(e.target.value)})}),(0,w.jsx)(f,{onCancel:i,children:(0,w.jsx)(`button`,{type:`submit`,className:`btn danger`,disabled:!_||g.isPending,children:`Delete`})})]})}export{P as ConnectionForm,N as ConnectionsPage};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{B as e,H as t,g as n,n as r,o as i,s as a,v as o,z as s}from"./client-ZHNiMG8H.js";import{
|
|
1
|
+
import{B as e,H as t,g as n,n as r,o as i,s as a,v as o,z as s}from"./client-ZHNiMG8H.js";import{E as c,M as l,T as u,d,i as f,j as p,k as m,n as h,r as g,s as _,u as v}from"./index--QHzN3S_.js";import{a as ee,i as y}from"./CodeBox-Cwh5KHLp.js";import{a as b}from"./api-Dc-2iLvt.js";import{n as te,t as x}from"./maskRules-CvKQIPgN.js";import{n as ne,t as S}from"./SchemaBrowser-CAIraoYm.js";import{n as re,r as C,t as ie}from"./Results-3iK2cKFL.js";import{Impact as w,t as T}from"./forms--qfP4qjO.js";var E=t(e(),1),D=()=>f().format,O=null;function ae(){return O??={type:``,host:``,connection:``,database:``,format:D(),sql:``,params:``,page:1,pageSize:g(),timeout:``,mask:[]},O}function oe(e){O=e}var k=`queryapigate-run-history`,A=20;function j(){try{return JSON.parse(sessionStorage.getItem(k)||`[]`)}catch{return[]}}function M(e){try{sessionStorage.setItem(k,JSON.stringify(e))}catch{}}function se(e,t,n){let r=e.trim();if(!r)return j();let i=j().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,A);return M(a),a}function ce(e,t,n){let r=j();return r.length?(r[0]={...r[0],status:e,rows:t,duration_ms:n},M(r),r):r}var N=/([A-Za-z_]\w*)\s*(=|!=|<>|<=|>=|<|>)\s*('(?:[^']|'')*'|(?:-?\d+(?:\.\d+)?|NULL|TRUE|FALSE)\b)/dgi;function le(e,t,n){let r=e.lastIndexOf(`
|
|
2
2
|
`,t-1)+1,i=e.indexOf(`
|
|
3
|
-
`,n);i===-1&&(i=e.length);let a=e.slice(r,i),o=t-r,s=n-r;N.lastIndex=0;for(let e=N.exec(a);e;e=N.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 ue(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 de(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 P=s(),fe=[`mysql`,`postgres`,`clickhouse`,`mongo`],F=`queryapigate-ui-run-side-tab`,pe=`queryapigate-ui-runner-side-w`,I=220,L=560,R=320;function z(e){return e.host?e.host+(e.port?`:`+e.port:``):`(local file)`}function me(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 he(e){try{return localStorage.getItem(e)}catch{return null}}function B(e,t){try{localStorage.setItem(e,t)}catch{}}function V(){let e=
|
|
4
|
-
`));try{let{response:t,elapsed:n}=await i(p,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(m)}),r=await re(t,{page:l,size:u,format:g.format,elapsed:n,filename:e.explain?`explain`:`query`});r.error&&s(r.error,{status:r.status}),we(r);let a=r.rows?r.rows.length:null;Ee({status:r.status,rows:a,elapsed:n,format:g.format,widest:r.rows?C(r.rows):null}),e.explain||Oe(ce(r.status,a,n))}catch(e){s(`Network error: `+e.message)}finally{Te(!1)}}let Me=t.state,J=Me?.preview,Y=Me?.select,Ne=(0,E.useRef)(null);(0,E.useEffect)(()=>{if(!Y||Ne.current===t.key||!f[Y])return;Ne.current=t.key;let n=f[Y];_(e=>({...e,type:n.db,host:z(n),connection:n.name,database:``})),e(t.pathname,{replace:!0,state:null})},[Y,t.key,t.pathname,f,e]);let X=(0,E.useRef)(null);(0,E.useEffect)(()=>{if(!J||X.current===t.key||!f[J.connection])return;X.current=t.key;let n=f[J.connection],r=n.db===`mongo`?JSON.stringify({collection:J.table,filter:{}},null,2):`SELECT * FROM `+J.table;_(e=>({...e,type:n.db,host:z(n),connection:n.name,database:``,sql:r,page:1})),q({sql:r,connection:n.name}),e(t.pathname,{replace:!0,state:null})},[J,t.key,t.pathname,f,q,e]);function Pe(e){let t=f[e.connection];v({sql:e.sql,format:e.format||g.format,...t?{type:t.db,host:z(t),connection:t.name,database:``}:{}})}let[Fe,Z]=(0,E.useState)(null);function Ie(e){let t=b.current?.selection();if(!t||t.from===t.to)return;let n=le(t.doc,t.from,t.to);n&&Z({x:e.clientX/h(),y:e.clientY/h(),candidate:n})}function Le(e){Z(null),b.current?.replaceRange(e.absStart,e.absEnd,`:`+e.name);let t;try{t=g.params.trim()?JSON.parse(g.params):{}}catch{t={}}t[e.name]=ue(e.valueText),v({params:JSON.stringify(t,null,2)})}let[Re,Q]=(0,E.useState)(!1);function ze(){if(!M){s(`Pick a connection first — add one on the Connections tab.`);return}if(!g.sql.trim()){s(`Write a query first.`,{errors:{sql:`This field is required`}});return}Q(!0)}let $=F?de(g.sql):ne(g.sql),Be=me(N?.usage);return(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(`div`,{className:`page-head`,children:(0,P.jsxs)(`div`,{className:`titles`,children:[(0,P.jsx)(`h1`,{children:`API Designer`}),(0,P.jsx)(`span`,{className:`sub`,children:`Ad-hoc statements against any active connection. Nothing here is saved.`})]})}),(0,P.jsxs)(`form`,{id:`run-form`,className:`panel runner`,noValidate:!0,onSubmit:e=>{e.preventDefault(),q()},style:{"--runner-side-w":void 0},ref:e=>{let t=Number(he(pe));e&&t&&!e.style.getPropertyValue(`--runner-side-w`)&&e.style.setProperty(`--runner-side-w`,Math.max(I,Math.min(L,t))+`px`)},children:[(0,P.jsxs)(`div`,{className:`runner-main`,children:[(0,P.jsxs)(`div`,{className:`runner-bar`,children:[(0,P.jsx)(`span`,{className:`lbl`,children:`Type`}),(0,P.jsxs)(`select`,{id:`run-conn-type`,"aria-label":`Database type`,value:D,onChange:e=>v({type:e.target.value}),children:[(0,P.jsx)(`option`,{value:``,children:`All types`}),w.map(e=>(0,P.jsx)(`option`,{value:e,children:e},e))]}),(0,P.jsx)(`span`,{className:`lbl`,children:`Host`}),(0,P.jsxs)(`select`,{id:`run-conn-host`,"aria-label":`Host`,value:k,onChange:e=>v({host:e.target.value}),children:[(0,P.jsx)(`option`,{value:``,children:`All hosts`}),O.map(e=>(0,P.jsx)(`option`,{value:e,children:e},e))]}),(0,P.jsx)(`span`,{className:`lbl`,children:`Connection`}),(0,P.jsx)(`select`,{id:`run-connection`,required:!0,"aria-label":`Connection`,value:M,onChange:e=>v({connection:e.target.value,database:``}),children:A.map(e=>(0,P.jsx)(`option`,{value:e,children:`${e} · ${f[e]?.database||`?`}${f[e]?.active?``:` (inactive)`}`},e))}),(0,P.jsx)(`span`,{className:`lbl`,children:`Database`}),(0,P.jsx)(`select`,{id:`run-database`,"aria-label":`Database`,disabled:!R||!B.isSuccess,value:R&&B.isSuccess?H:``,onChange:e=>v({database:e.target.value}),children:N?R?B.isPending?(0,P.jsx)(`option`,{value:``,children:`Loading…`}):B.isError?(0,P.jsx)(`option`,{value:``,children:`Unavailable`}):V.map(e=>(0,P.jsx)(`option`,{value:e,children:e},e)):(0,P.jsx)(`option`,{value:``,children:N.database||`(default)`}):(0,P.jsx)(`option`,{value:``,children:`—`})}),(0,P.jsx)(`span`,{className:`lbl`,children:`Format`}),(0,P.jsx)(`select`,{id:`run-format`,"aria-label":`Format`,value:g.format,onChange:e=>v({format:e.target.value}),children:[`json`,`ndjson`,`csv`,`tsv`,`xml`,`yaml`,`xlsx`,`parquet`].map(e=>(0,P.jsx)(`option`,{value:e,children:e},e))}),(0,P.jsx)(`span`,{className:`spacer`}),(0,P.jsxs)(`span`,{className:`hint hide-sm`,children:[(0,P.jsx)(`kbd`,{children:`Ctrl`}),` `,(0,P.jsx)(`kbd`,{children:`Enter`})]}),(0,P.jsx)(`button`,{type:`button`,className:`btn ghost md`,id:`run-save-as-api-button`,title:`Save this query as a new API`,onClick:ze,children:`Save as New API`}),!F&&(0,P.jsx)(`button`,{type:`button`,className:`btn ghost md`,id:`run-explain-button`,title:`Run EXPLAIN on this query`,disabled:!!W,onClick:()=>void q({explain:!0}),children:`Explain`}),(0,P.jsx)(`button`,{type:`submit`,className:`btn primary md`,id:`run-button`,style:{padding:`0 16px`},disabled:!!W,children:`Run`})]}),(0,P.jsx)(`div`,{className:`runner-conn-context`,id:`run-conn-context`,children:N&&(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(`span`,{className:`dot ${N.active?`ok`:`bad`}`,title:N.active?`Active`:`Inactive`}),(0,P.jsx)(`span`,{children:N.name}),(0,P.jsx)(`span`,{className:`dim`,children:N.host?`${z(N)} · ${N.database||`?`}`:N.database||`(local file)`}),(0,P.jsx)(`span`,{className:`dim`,children:Be||`No activity yet`})]})}),(0,P.jsx)(T,{handle:b,id:`run-sql`,boxed:!1,value:g.sql,onChange:e=>v({sql:e}),dialect:N?.db,schema:Se,label:F?`Mongo find query (JSON)`:`SQL`,placeholder:F?`{"collection": "orders", "filter": {"status": ":status"}}`:`SELECT * FROM t WHERE id = :id`,onRun:()=>void q(),onDoubleClick:Ie})]}),(0,P.jsx)(_e,{}),(0,P.jsx)(`div`,{className:`runner-side`,children:(0,P.jsx)(ge,{recent:(0,P.jsx)(ve,{entries:De,onPick:Pe}),settings:(0,P.jsxs)(P.Fragment,{children:[(0,P.jsxs)(`div`,{className:`field`,children:[(0,P.jsxs)(`label`,{htmlFor:`run-params`,children:[`Bound parameters `,(0,P.jsx)(`span`,{className:`type`,children:`JSON`})]}),(0,P.jsx)(`textarea`,{id:`run-params`,spellCheck:!1,placeholder:`{"id": 1}`,value:g.params,onChange:e=>v({params:e.target.value}),onKeyDown:e=>{e.key===`Enter`&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),q())}}),(0,P.jsxs)(`div`,{className:`refs`,id:`run-refs`,children:[!F&&$.length>0&&(0,P.jsx)(`span`,{className:`hint`,children:`In SQL:`}),!F&&$.map(e=>(0,P.jsxs)(`span`,{className:`tag`,children:[`:`,e]},e))]})]}),(0,P.jsxs)(`div`,{className:`grid2`,children:[(0,P.jsxs)(`div`,{className:`field`,children:[(0,P.jsx)(`label`,{htmlFor:`run-page`,children:`Page`}),(0,P.jsx)(`input`,{id:`run-page`,type:`number`,min:`1`,value:g.page,onChange:e=>v({page:Number(e.target.value)||1})})]}),(0,P.jsxs)(`div`,{className:`field`,children:[(0,P.jsx)(`label`,{htmlFor:`run-page-size`,children:`Page size`}),(0,P.jsx)(`input`,{id:`run-page-size`,type:`number`,min:`1`,value:g.pageSize,onChange:e=>v({pageSize:Number(e.target.value)||10})})]})]}),(0,P.jsxs)(`div`,{className:`field`,children:[(0,P.jsxs)(`label`,{htmlFor:`run-timeout`,children:[`Timeout `,(0,P.jsx)(`span`,{className:`type`,children:`seconds, optional`})]}),(0,P.jsx)(`input`,{id:`run-timeout`,type:`number`,min:`0`,step:`any`,placeholder:`server default`,value:g.timeout,onChange:e=>v({timeout:e.target.value})})]}),!F&&(0,P.jsx)(y,{id:`run-mask`,rules:g.mask,onChange:e=>v({mask:e}),who:`this preview`})]}),schema:(0,P.jsx)(`div`,{id:`run-schema-slot`,children:(0,P.jsx)(te,{connection:M,dialect:N?.db,database:U,editor:b,isMongo:F,onPreview:e=>{let t=F?JSON.stringify({collection:e,filter:{}},null,2):`SELECT * FROM `+e;v({sql:t,page:1}),q({sql:t})},showDdl:!0,showUsage:!0})})})})]}),(0,P.jsx)(`div`,{id:`run-quick-stats`,children:G?.widest&&(0,P.jsx)(`span`,{className:`dim`,children:`Widest column: ${G.widest.column} (~${G.widest.maxLen} chars)`})}),(0,P.jsx)(`div`,{className:`panel`,id:`run-save-panel`,hidden:!Re,style:{marginTop:12},children:Re&&(0,P.jsx)(be,{connection:M,sql:g.sql,isMongo:F,onClose:()=>Q(!1),onSaved:t=>{Q(!1),e(`/queries/${encodeURIComponent(t)}`)}})}),(0,P.jsx)(ie,{result:Ce,running:!!W,filename:K?`explain`:`query`,curl:ke,panelProps:{id:`run-results-panel`,className:`panel results`},onPage:e=>void q({explain:K,page:e}),onPageSize:e=>{v({pageSize:e}),q({explain:K,page:1,pageSize:e})}}),Fe&&(0,P.jsx)(ye,{...Fe,onApply:Le,onClose:()=>Z(null)})]})}function ge({recent:e,settings:t,schema:n}){let[r,i]=(0,E.useState)(()=>{let e=he(F);return e===`recent`||e===`settings`?e:`schema`}),a=e=>{i(e),B(F,e)},o=(e,t)=>(0,P.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,P.jsxs)(P.Fragment,{children:[(0,P.jsxs)(`div`,{className:`side-tabs`,role:`tablist`,children:[o(`recent`,`Recent Queries`),o(`settings`,`Settings`),o(`schema`,`Schema`)]}),(0,P.jsxs)(`div`,{className:`side-panels`,children:[(0,P.jsx)(`div`,{className:`side-tab-panel`,"data-side-panel":`recent`,hidden:r!==`recent`,children:(0,P.jsx)(`div`,{id:`run-history-slot`,children:e})}),(0,P.jsx)(`div`,{className:`side-tab-panel`,"data-side-panel":`settings`,hidden:r!==`settings`,children:t}),(0,P.jsx)(`div`,{className:`side-tab-panel`,"data-side-panel":`schema`,hidden:r!==`schema`,children:n})]})]})}function _e(){let e=(0,E.useRef)(null),t=(0,E.useRef)(null),n=()=>e.current?.closest(`.runner`)??null,r=()=>(n()?.querySelector(`.runner-side`)?.getBoundingClientRect().width??280*h())/h(),i=e=>n()?.style.setProperty(`--runner-side-w`,e+`px`),a=()=>{let e=parseFloat(getComputedStyle(n()).getPropertyValue(`--runner-side-w`));e&&B(pe,String(e))};return(0,P.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)/h(),a=Math.min(L,(n()?.getBoundingClientRect().width??0)/h()-R-7);i(Math.max(I,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(L,r()+t));else if(e.key===`ArrowRight`)i(Math.max(I,r()-t));else return;e.preventDefault(),a()}})}function ve({entries:e,onPick:t}){return e.length?(0,P.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,P.jsxs)(`button`,{type:`button`,className:`history-item`,title:e.sql,onClick:()=>t(e),children:[(0,P.jsx)(`span`,{className:`history-sql`,children:e.sql.length>64?e.sql.slice(0,64)+`…`:e.sql}),n.length>0&&(0,P.jsx)(`span`,{className:`history-meta`,children:n.join(` · `)})]},`${e.connection}:${e.sql}`)})}):(0,P.jsx)(`div`,{className:`hint`,children:`Queries you run will show up here.`})}function ye({x:e,y:t,candidate:n,onApply:r,onClose:i}){let a=(0,E.useRef)(null),[o,s]=(0,E.useState)(e);return(0,E.useEffect)(()=>{let t=a.current?.offsetWidth??260;s(Math.max(8,Math.min(e,window.innerWidth/h()-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,P.jsxs)(`div`,{ref:a,id:`paramize-popover`,className:`paramize-popover`,style:{left:o,top:t+12},children:[(0,P.jsxs)(`div`,{className:`preview`,children:[`${n.name} = ${n.valueText} → ${n.name} = `,(0,P.jsxs)(`b`,{children:[`:`,n.name]})]}),(0,P.jsx)(`button`,{type:`button`,onClick:()=>r(n),children:`Parameterize`})]})}function be({connection:e,sql:t,isMongo:n,onClose:r,onSaved:i}){let{showError:a,toast:o}=m(),s=_(),c=v(),d=b(),[f,p]=(0,E.useState)(``),[h,g]=(0,E.useState)(``),[y,ee]=(0,E.useState)(``),[x,te]=(0,E.useState)(``),[S,re]=(0,E.useState)(``),[C,ie]=(0,E.useState)(()=>{let e=n?de(t):ne(t);return e.length?JSON.stringify(Object.fromEntries(e.map(e=>[e,{}])),null,2):``});function T(r){let c={};if(C.trim())try{c=JSON.parse(C)}catch(e){a(`query_parameters is not valid JSON: `+e.message,{errors:{query_parameters:e.message}});return}let l=f.trim(),u={author:h,description:y,tags:S.split(`,`).map(e=>e.trim()).filter(Boolean),connection_name:e||void 0,parameters:c,publish:r};if(n){let e;try{e=t.trim()?JSON.parse(t):{}}catch(e){a(`The query must be valid JSON: `+e.message,{errors:{sql_query:e.message}});return}if(!e||typeof e!=`object`||Array.isArray(e)||!e.collection){a(`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(u,{query_type:`mongo`,mongo_collection:e.collection,mongo_filter:e.filter??{}}),e.projection&&(u.mongo_projection=e.projection),e.sort&&(u.mongo_sort=e.sort)}else u.sql=t;let p=s.data?.find(e=>e.name===l),m=x.trim();if(m&&p&&(p.collection??``)!==m){a(`“${l}” already exists${p.collection?` in collection `+p.collection:` with no collection`}. Save it without a collection here, then use “Move…” — that shows which keys gain or lose access first.`);return}let g={...u,name:l,...m?{collection:m}:{}};d.mutate(p?{kind:`version`,name:l,etag:null,body:u}:{kind:`create`,body:g},{onSuccess:e=>{a(``),o(`Saved ${e.name} v${e.latest_version}${r?``:` as a draft`}`),i(e.name)},onError:e=>a(e.message)})}return(0,P.jsxs)(P.Fragment,{children:[(0,P.jsxs)(`div`,{className:`panel-head`,children:[(0,P.jsx)(`h2`,{children:`Save as New API`}),(0,P.jsx)(`span`,{className:`spacer`}),(0,P.jsx)(`button`,{type:`button`,className:`btn sm ghost`,title:`Close`,onClick:r,children:`✕`})]}),(0,P.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{e.preventDefault(),T(!0)},children:[(0,P.jsx)(u,{id:`rs-filename`,label:`Filename`,hint:`Letters, digits, spaces, “.”, “_” and “-”. Saving an existing name adds a version.`,children:(0,P.jsx)(`input`,{id:`rs-filename`,placeholder:`films_by_rating`,required:!0,autoComplete:`off`,spellCheck:!1,autoFocus:!0,value:f,onChange:e=>p(e.target.value)})}),(0,P.jsxs)(`div`,{className:`grid2`,children:[(0,P.jsx)(u,{id:`rs-author`,label:`Author`,children:(0,P.jsx)(`input`,{id:`rs-author`,required:!0,autoComplete:`off`,spellCheck:!1,value:h,onChange:e=>g(e.target.value)})}),(0,P.jsx)(u,{id:`rs-description`,label:`Description`,children:(0,P.jsx)(`input`,{id:`rs-description`,required:!0,autoComplete:`off`,spellCheck:!1,value:y,onChange:e=>ee(e.target.value)})})]}),(0,P.jsxs)(`div`,{className:`field`,children:[(0,P.jsx)(`label`,{htmlFor:`rs-collection`,children:`Collection`}),(0,P.jsx)(`input`,{id:`rs-collection`,list:`rs-collection-list`,placeholder:`optional — e.g. reporting`,autoComplete:`off`,spellCheck:!1,value:x,onChange:e=>te(e.target.value)}),(0,P.jsx)(`datalist`,{id:`rs-collection-list`,children:Object.keys(c.data?.collections??{}).sort().map(e=>(0,P.jsx)(`option`,{value:e},e))}),(0,P.jsx)(`div`,{className:`hint`,children:`Optional. Lowercase letters, digits, “.”, “_” and “-”. Later, use “Move…” to change it.`}),x.trim()&&(0,P.jsx)(w,{moves:[[null,x.trim()]],info:c.data})]}),(0,P.jsx)(u,{id:`rs-tags`,label:`Tags`,children:(0,P.jsx)(`input`,{id:`rs-tags`,placeholder:`comma-separated`,autoComplete:`off`,spellCheck:!1,value:S,onChange:e=>re(e.target.value)})}),(0,P.jsx)(u,{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,P.jsx)(`textarea`,{id:`rs-params`,spellCheck:!1,placeholder:`{"id": {"type": "int", "min": 1}}`,value:C,onChange:e=>ie(e.target.value)})}),(0,P.jsxs)(l,{onCancel:r,children:[(0,P.jsx)(`button`,{type:`button`,className:`btn`,disabled:d.isPending,title:`Save without publishing`,onClick:()=>T(!1),children:`Save as draft`}),(0,P.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:d.isPending,children:`Save`})]})]})]})}export{V as DesignerPage};
|
|
3
|
+
`,n);i===-1&&(i=e.length);let a=e.slice(r,i),o=t-r,s=n-r;N.lastIndex=0;for(let e=N.exec(a);e;e=N.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 ue(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 de(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 P=s(),fe=[`mysql`,`postgres`,`clickhouse`,`mongo`,`trino`],F=`queryapigate-ui-run-side-tab`,pe=`queryapigate-ui-runner-side-w`,I=220,L=560,R=320;function z(e){return e.host?e.host+(e.port?`:`+e.port:``):`(local file)`}function me(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 he(e){try{return localStorage.getItem(e)}catch{return null}}function B(e,t){try{localStorage.setItem(e,t)}catch{}}function V(){let e=l(),t=p(),{showError:s}=m(),c=d(),u=(0,E.useMemo)(()=>c.data??[],[c.data]),f=(0,E.useMemo)(()=>Object.fromEntries(u.map(e=>[e.name,e])),[u]),[g,_]=(0,E.useState)(ae);(0,E.useEffect)(()=>oe(g),[g]);let v=e=>_(t=>({...t,...e})),b=(0,E.useRef)(null),w=[...new Set(u.map(e=>e.db))].sort(),D=w.includes(g.type)?g.type:``,O=[...new Set(u.filter(e=>!D||e.db===D).map(z))].sort(),k=O.includes(g.host)?g.host:``,A=u.filter(e=>(!D||e.db===D)&&(!k||z(e)===k)).map(e=>e.name).sort(),M=A.includes(g.connection)?g.connection:A.find(e=>f[e]?.active)??``,N=f[M],F=N?.db===`mongo`,R=!!(N&&fe.includes(N.db)),B=o({queryKey:[`connections`,M,`databases`],queryFn:async()=>a(await r.POST(`/api/v1/connections/databases`,{body:{name:M}})),enabled:R,retry:!1,staleTime:3e5}),V=B.data?.databases??[],H=V.includes(g.database)?g.database:N?.database??``,U=N&&H&&H!==N.database?H:null,xe=ne(M||void 0,U),Se=(0,E.useMemo)(()=>Object.fromEntries((xe.data?.tables??[]).map(e=>[e.name,e.columns.map(e=>e.name)])),[xe.data]),[Ce,we]=(0,E.useState)(null),[W,Te]=(0,E.useState)(!1),[G,Ee]=(0,E.useState)(null),[De,Oe]=(0,E.useState)(j),[ke,Ae]=(0,E.useState)(),[K,je]=(0,E.useState)(!1);async function q(e={}){let t=e.connection??M,r=f[t];if(!t||!r){s(`Pick a connection first — add one on the Connections tab.`);return}let a=r.db===`mongo`,o=e.sql??g.sql;if(e.explain&&a){s(`EXPLAIN is not supported for Mongo connections yet.`);return}if(!o.trim()){s(a?`Write a query first.`:`Write some SQL to run.`,{errors:{sql:`This field is required`}});return}let c={};if(g.params.trim())try{c=JSON.parse(g.params)}catch(e){s(`Bound parameters must be valid JSON: `+e.message,{errors:{params:e.message}});return}let l=e.page??1,u=e.pageSize??(g.pageSize||10),d=`format=${encodeURIComponent(g.format)}&page=${l}&page_size=${u}${g.timeout?`&timeout=`+encodeURIComponent(g.timeout):``}`,p,m;if(a){let e;try{e=JSON.parse(o)}catch(e){s(`The query must be valid JSON: `+e.message,{errors:{sql:e.message}});return}if(!e||typeof e!=`object`||Array.isArray(e)||!e.collection){s(`The query must be a JSON object with a "collection" field, e.g. {"collection": "users", "filter": {}}.`,{errors:{sql:`collection is required`}});return}p=`/execute_mongo?${d}`,m={collection:e.collection,filter:e.filter??{},connection_name:t,params:c},e.projection&&(m.projection=e.projection),e.sort&&(m.sort=e.sort)}else{p=`/execute_sql?${d}`,m={sql:e.explain?`EXPLAIN `+o:o,connection_name:t,params:c},t===M&&U&&(m.database=U);let n=x(g.mask,void 0);n&&!e.explain&&(m.mask=n)}je(!!e.explain),v({page:l}),s(``),!e.explain&&l===1&&e.page===void 0&&Oe(se(o,t,g.format)),Te(e.explain?`explain`:`run`),Ae([`curl -X POST ${y(new URL(p,globalThis.location?.href??`http://localhost`).href)}`,` -H 'Content-Type: application/json'`,...n()?[` -H 'X-API-Key: YOUR_KEY_HERE' # replace with your own key`]:[],` -d ${y(JSON.stringify(m))}`].join(` \\
|
|
4
|
+
`));try{let{response:t,elapsed:n}=await i(p,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(m)}),r=await re(t,{page:l,size:u,format:g.format,elapsed:n,filename:e.explain?`explain`:`query`});r.error&&s(r.error,{status:r.status}),we(r);let a=r.rows?r.rows.length:null;Ee({status:r.status,rows:a,elapsed:n,format:g.format,widest:r.rows?C(r.rows):null}),e.explain||Oe(ce(r.status,a,n))}catch(e){s(`Network error: `+e.message)}finally{Te(!1)}}let Me=t.state,J=Me?.preview,Y=Me?.select,Ne=(0,E.useRef)(null);(0,E.useEffect)(()=>{if(!Y||Ne.current===t.key||!f[Y])return;Ne.current=t.key;let n=f[Y];_(e=>({...e,type:n.db,host:z(n),connection:n.name,database:``})),e(t.pathname,{replace:!0,state:null})},[Y,t.key,t.pathname,f,e]);let X=(0,E.useRef)(null);(0,E.useEffect)(()=>{if(!J||X.current===t.key||!f[J.connection])return;X.current=t.key;let n=f[J.connection],r=n.db===`mongo`?JSON.stringify({collection:J.table,filter:{}},null,2):`SELECT * FROM `+J.table;_(e=>({...e,type:n.db,host:z(n),connection:n.name,database:``,sql:r,page:1})),q({sql:r,connection:n.name}),e(t.pathname,{replace:!0,state:null})},[J,t.key,t.pathname,f,q,e]);function Pe(e){let t=f[e.connection];v({sql:e.sql,format:e.format||g.format,...t?{type:t.db,host:z(t),connection:t.name,database:``}:{}})}let[Fe,Z]=(0,E.useState)(null);function Ie(e){let t=b.current?.selection();if(!t||t.from===t.to)return;let n=le(t.doc,t.from,t.to);n&&Z({x:e.clientX/h(),y:e.clientY/h(),candidate:n})}function Le(e){Z(null),b.current?.replaceRange(e.absStart,e.absEnd,`:`+e.name);let t;try{t=g.params.trim()?JSON.parse(g.params):{}}catch{t={}}t[e.name]=ue(e.valueText),v({params:JSON.stringify(t,null,2)})}let[Re,Q]=(0,E.useState)(!1);function ze(){if(!M){s(`Pick a connection first — add one on the Connections tab.`);return}if(!g.sql.trim()){s(`Write a query first.`,{errors:{sql:`This field is required`}});return}Q(!0)}let $=F?de(g.sql):ee(g.sql),Be=me(N?.usage);return(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(`div`,{className:`page-head`,children:(0,P.jsxs)(`div`,{className:`titles`,children:[(0,P.jsx)(`h1`,{children:`API Designer`}),(0,P.jsx)(`span`,{className:`sub`,children:`Ad-hoc statements against any active connection. Nothing here is saved.`})]})}),(0,P.jsxs)(`form`,{id:`run-form`,className:`panel runner`,noValidate:!0,onSubmit:e=>{e.preventDefault(),q()},style:{"--runner-side-w":void 0},ref:e=>{let t=Number(he(pe));e&&t&&!e.style.getPropertyValue(`--runner-side-w`)&&e.style.setProperty(`--runner-side-w`,Math.max(I,Math.min(L,t))+`px`)},children:[(0,P.jsxs)(`div`,{className:`runner-main`,children:[(0,P.jsxs)(`div`,{className:`runner-bar`,children:[(0,P.jsx)(`span`,{className:`lbl`,children:`Type`}),(0,P.jsxs)(`select`,{id:`run-conn-type`,"aria-label":`Database type`,value:D,onChange:e=>v({type:e.target.value}),children:[(0,P.jsx)(`option`,{value:``,children:`All types`}),w.map(e=>(0,P.jsx)(`option`,{value:e,children:e},e))]}),(0,P.jsx)(`span`,{className:`lbl`,children:`Host`}),(0,P.jsxs)(`select`,{id:`run-conn-host`,"aria-label":`Host`,value:k,onChange:e=>v({host:e.target.value}),children:[(0,P.jsx)(`option`,{value:``,children:`All hosts`}),O.map(e=>(0,P.jsx)(`option`,{value:e,children:e},e))]}),(0,P.jsx)(`span`,{className:`lbl`,children:`Connection`}),(0,P.jsx)(`select`,{id:`run-connection`,required:!0,"aria-label":`Connection`,value:M,onChange:e=>v({connection:e.target.value,database:``}),children:A.map(e=>(0,P.jsx)(`option`,{value:e,children:`${e} · ${f[e]?.database||`?`}${f[e]?.active?``:` (inactive)`}`},e))}),(0,P.jsx)(`span`,{className:`lbl`,children:`Database`}),(0,P.jsx)(`select`,{id:`run-database`,"aria-label":`Database`,disabled:!R||!B.isSuccess,value:R&&B.isSuccess?H:``,onChange:e=>v({database:e.target.value}),children:N?R?B.isPending?(0,P.jsx)(`option`,{value:``,children:`Loading…`}):B.isError?(0,P.jsx)(`option`,{value:``,children:`Unavailable`}):V.map(e=>(0,P.jsx)(`option`,{value:e,children:e},e)):(0,P.jsx)(`option`,{value:``,children:N.database||`(default)`}):(0,P.jsx)(`option`,{value:``,children:`—`})}),(0,P.jsx)(`span`,{className:`lbl`,children:`Format`}),(0,P.jsx)(`select`,{id:`run-format`,"aria-label":`Format`,value:g.format,onChange:e=>v({format:e.target.value}),children:[`json`,`ndjson`,`csv`,`tsv`,`xml`,`yaml`,`xlsx`,`parquet`].map(e=>(0,P.jsx)(`option`,{value:e,children:e},e))}),(0,P.jsx)(`span`,{className:`spacer`}),(0,P.jsxs)(`span`,{className:`hint hide-sm`,children:[(0,P.jsx)(`kbd`,{children:`Ctrl`}),` `,(0,P.jsx)(`kbd`,{children:`Enter`})]}),(0,P.jsx)(`button`,{type:`button`,className:`btn ghost md`,id:`run-save-as-api-button`,title:`Save this query as a new API`,onClick:ze,children:`Save as New API`}),!F&&(0,P.jsx)(`button`,{type:`button`,className:`btn ghost md`,id:`run-explain-button`,title:`Run EXPLAIN on this query`,disabled:!!W,onClick:()=>void q({explain:!0}),children:`Explain`}),(0,P.jsx)(`button`,{type:`submit`,className:`btn primary md`,id:`run-button`,style:{padding:`0 16px`},disabled:!!W,children:`Run`})]}),(0,P.jsx)(`div`,{className:`runner-conn-context`,id:`run-conn-context`,children:N&&(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(`span`,{className:`dot ${N.active?`ok`:`bad`}`,title:N.active?`Active`:`Inactive`}),(0,P.jsx)(`span`,{children:N.name}),(0,P.jsx)(`span`,{className:`dim`,children:N.host?`${z(N)} · ${N.database||`?`}`:N.database||`(local file)`}),(0,P.jsx)(`span`,{className:`dim`,children:Be||`No activity yet`})]})}),(0,P.jsx)(T,{handle:b,id:`run-sql`,boxed:!1,value:g.sql,onChange:e=>v({sql:e}),dialect:N?.db,schema:Se,label:F?`Mongo find query (JSON)`:`SQL`,placeholder:F?`{"collection": "orders", "filter": {"status": ":status"}}`:`SELECT * FROM t WHERE id = :id`,onRun:()=>void q(),onDoubleClick:Ie})]}),(0,P.jsx)(_e,{}),(0,P.jsx)(`div`,{className:`runner-side`,children:(0,P.jsx)(ge,{recent:(0,P.jsx)(ve,{entries:De,onPick:Pe}),settings:(0,P.jsxs)(P.Fragment,{children:[(0,P.jsxs)(`div`,{className:`field`,children:[(0,P.jsxs)(`label`,{htmlFor:`run-params`,children:[`Bound parameters `,(0,P.jsx)(`span`,{className:`type`,children:`JSON`})]}),(0,P.jsx)(`textarea`,{id:`run-params`,spellCheck:!1,placeholder:`{"id": 1}`,value:g.params,onChange:e=>v({params:e.target.value}),onKeyDown:e=>{e.key===`Enter`&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),q())}}),(0,P.jsxs)(`div`,{className:`refs`,id:`run-refs`,children:[!F&&$.length>0&&(0,P.jsx)(`span`,{className:`hint`,children:`In SQL:`}),!F&&$.map(e=>(0,P.jsxs)(`span`,{className:`tag`,children:[`:`,e]},e))]})]}),(0,P.jsxs)(`div`,{className:`grid2`,children:[(0,P.jsxs)(`div`,{className:`field`,children:[(0,P.jsx)(`label`,{htmlFor:`run-page`,children:`Page`}),(0,P.jsx)(`input`,{id:`run-page`,type:`number`,min:`1`,value:g.page,onChange:e=>v({page:Number(e.target.value)||1})})]}),(0,P.jsxs)(`div`,{className:`field`,children:[(0,P.jsx)(`label`,{htmlFor:`run-page-size`,children:`Page size`}),(0,P.jsx)(`input`,{id:`run-page-size`,type:`number`,min:`1`,value:g.pageSize,onChange:e=>v({pageSize:Number(e.target.value)||10})})]})]}),(0,P.jsxs)(`div`,{className:`field`,children:[(0,P.jsxs)(`label`,{htmlFor:`run-timeout`,children:[`Timeout `,(0,P.jsx)(`span`,{className:`type`,children:`seconds, optional`})]}),(0,P.jsx)(`input`,{id:`run-timeout`,type:`number`,min:`0`,step:`any`,placeholder:`server default`,value:g.timeout,onChange:e=>v({timeout:e.target.value})})]}),!F&&(0,P.jsx)(te,{id:`run-mask`,rules:g.mask,onChange:e=>v({mask:e}),who:`this preview`})]}),schema:(0,P.jsx)(`div`,{id:`run-schema-slot`,children:(0,P.jsx)(S,{connection:M,dialect:N?.db,database:U,editor:b,isMongo:F,onPreview:e=>{let t=F?JSON.stringify({collection:e,filter:{}},null,2):`SELECT * FROM `+e;v({sql:t,page:1}),q({sql:t})},showDdl:!0,showUsage:!0})})})})]}),(0,P.jsx)(`div`,{id:`run-quick-stats`,children:G?.widest&&(0,P.jsx)(`span`,{className:`dim`,children:`Widest column: ${G.widest.column} (~${G.widest.maxLen} chars)`})}),(0,P.jsx)(`div`,{className:`panel`,id:`run-save-panel`,hidden:!Re,style:{marginTop:12},children:Re&&(0,P.jsx)(be,{connection:M,sql:g.sql,isMongo:F,onClose:()=>Q(!1),onSaved:t=>{Q(!1),e(`/queries/${encodeURIComponent(t)}`)}})}),(0,P.jsx)(ie,{result:Ce,running:!!W,filename:K?`explain`:`query`,curl:ke,panelProps:{id:`run-results-panel`,className:`panel results`},onPage:e=>void q({explain:K,page:e}),onPageSize:e=>{v({pageSize:e}),q({explain:K,page:1,pageSize:e})}}),Fe&&(0,P.jsx)(ye,{...Fe,onApply:Le,onClose:()=>Z(null)})]})}function ge({recent:e,settings:t,schema:n}){let[r,i]=(0,E.useState)(()=>{let e=he(F);return e===`recent`||e===`settings`?e:`schema`}),a=e=>{i(e),B(F,e)},o=(e,t)=>(0,P.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,P.jsxs)(P.Fragment,{children:[(0,P.jsxs)(`div`,{className:`side-tabs`,role:`tablist`,children:[o(`recent`,`Recent Queries`),o(`settings`,`Settings`),o(`schema`,`Schema`)]}),(0,P.jsxs)(`div`,{className:`side-panels`,children:[(0,P.jsx)(`div`,{className:`side-tab-panel`,"data-side-panel":`recent`,hidden:r!==`recent`,children:(0,P.jsx)(`div`,{id:`run-history-slot`,children:e})}),(0,P.jsx)(`div`,{className:`side-tab-panel`,"data-side-panel":`settings`,hidden:r!==`settings`,children:t}),(0,P.jsx)(`div`,{className:`side-tab-panel`,"data-side-panel":`schema`,hidden:r!==`schema`,children:n})]})]})}function _e(){let e=(0,E.useRef)(null),t=(0,E.useRef)(null),n=()=>e.current?.closest(`.runner`)??null,r=()=>(n()?.querySelector(`.runner-side`)?.getBoundingClientRect().width??280*h())/h(),i=e=>n()?.style.setProperty(`--runner-side-w`,e+`px`),a=()=>{let e=parseFloat(getComputedStyle(n()).getPropertyValue(`--runner-side-w`));e&&B(pe,String(e))};return(0,P.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)/h(),a=Math.min(L,(n()?.getBoundingClientRect().width??0)/h()-R-7);i(Math.max(I,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(L,r()+t));else if(e.key===`ArrowRight`)i(Math.max(I,r()-t));else return;e.preventDefault(),a()}})}function ve({entries:e,onPick:t}){return e.length?(0,P.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,P.jsxs)(`button`,{type:`button`,className:`history-item`,title:e.sql,onClick:()=>t(e),children:[(0,P.jsx)(`span`,{className:`history-sql`,children:e.sql.length>64?e.sql.slice(0,64)+`…`:e.sql}),n.length>0&&(0,P.jsx)(`span`,{className:`history-meta`,children:n.join(` · `)})]},`${e.connection}:${e.sql}`)})}):(0,P.jsx)(`div`,{className:`hint`,children:`Queries you run will show up here.`})}function ye({x:e,y:t,candidate:n,onApply:r,onClose:i}){let a=(0,E.useRef)(null),[o,s]=(0,E.useState)(e);return(0,E.useEffect)(()=>{let t=a.current?.offsetWidth??260;s(Math.max(8,Math.min(e,window.innerWidth/h()-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,P.jsxs)(`div`,{ref:a,id:`paramize-popover`,className:`paramize-popover`,style:{left:o,top:t+12},children:[(0,P.jsxs)(`div`,{className:`preview`,children:[`${n.name} = ${n.valueText} → ${n.name} = `,(0,P.jsxs)(`b`,{children:[`:`,n.name]})]}),(0,P.jsx)(`button`,{type:`button`,onClick:()=>r(n),children:`Parameterize`})]})}function be({connection:e,sql:t,isMongo:n,onClose:r,onSaved:i}){let{showError:a,toast:o}=m(),s=_(),l=v(),d=b(),[f,p]=(0,E.useState)(``),[h,g]=(0,E.useState)(``),[y,te]=(0,E.useState)(``),[x,ne]=(0,E.useState)(``),[S,re]=(0,E.useState)(``),[C,ie]=(0,E.useState)(()=>{let e=n?de(t):ee(t);return e.length?JSON.stringify(Object.fromEntries(e.map(e=>[e,{}])),null,2):``});function T(r){let c={};if(C.trim())try{c=JSON.parse(C)}catch(e){a(`query_parameters is not valid JSON: `+e.message,{errors:{query_parameters:e.message}});return}let l=f.trim(),u={author:h,description:y,tags:S.split(`,`).map(e=>e.trim()).filter(Boolean),connection_name:e||void 0,parameters:c,publish:r};if(n){let e;try{e=t.trim()?JSON.parse(t):{}}catch(e){a(`The query must be valid JSON: `+e.message,{errors:{sql_query:e.message}});return}if(!e||typeof e!=`object`||Array.isArray(e)||!e.collection){a(`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(u,{query_type:`mongo`,mongo_collection:e.collection,mongo_filter:e.filter??{}}),e.projection&&(u.mongo_projection=e.projection),e.sort&&(u.mongo_sort=e.sort)}else u.sql=t;let p=s.data?.find(e=>e.name===l),m=x.trim();if(m&&p&&(p.collection??``)!==m){a(`“${l}” already exists${p.collection?` in collection `+p.collection:` with no collection`}. Save it without a collection here, then use “Move…” — that shows which keys gain or lose access first.`);return}let g={...u,name:l,...m?{collection:m}:{}};d.mutate(p?{kind:`version`,name:l,etag:null,body:u}:{kind:`create`,body:g},{onSuccess:e=>{a(``),o(`Saved ${e.name} v${e.latest_version}${r?``:` as a draft`}`),i(e.name)},onError:e=>a(e.message)})}return(0,P.jsxs)(P.Fragment,{children:[(0,P.jsxs)(`div`,{className:`panel-head`,children:[(0,P.jsx)(`h2`,{children:`Save as New API`}),(0,P.jsx)(`span`,{className:`spacer`}),(0,P.jsx)(`button`,{type:`button`,className:`btn sm ghost`,title:`Close`,onClick:r,children:`✕`})]}),(0,P.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{e.preventDefault(),T(!0)},children:[(0,P.jsx)(u,{id:`rs-filename`,label:`Filename`,hint:`Letters, digits, spaces, “.”, “_” and “-”. Saving an existing name adds a version.`,children:(0,P.jsx)(`input`,{id:`rs-filename`,placeholder:`films_by_rating`,required:!0,autoComplete:`off`,spellCheck:!1,autoFocus:!0,value:f,onChange:e=>p(e.target.value)})}),(0,P.jsxs)(`div`,{className:`grid2`,children:[(0,P.jsx)(u,{id:`rs-author`,label:`Author`,children:(0,P.jsx)(`input`,{id:`rs-author`,required:!0,autoComplete:`off`,spellCheck:!1,value:h,onChange:e=>g(e.target.value)})}),(0,P.jsx)(u,{id:`rs-description`,label:`Description`,children:(0,P.jsx)(`input`,{id:`rs-description`,required:!0,autoComplete:`off`,spellCheck:!1,value:y,onChange:e=>te(e.target.value)})})]}),(0,P.jsxs)(`div`,{className:`field`,children:[(0,P.jsx)(`label`,{htmlFor:`rs-collection`,children:`Collection`}),(0,P.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,P.jsx)(`datalist`,{id:`rs-collection-list`,children:Object.keys(l.data?.collections??{}).sort().map(e=>(0,P.jsx)(`option`,{value:e},e))}),(0,P.jsx)(`div`,{className:`hint`,children:`Optional. Lowercase letters, digits, “.”, “_” and “-”. Later, use “Move…” to change it.`}),x.trim()&&(0,P.jsx)(w,{moves:[[null,x.trim()]],info:l.data})]}),(0,P.jsx)(u,{id:`rs-tags`,label:`Tags`,children:(0,P.jsx)(`input`,{id:`rs-tags`,placeholder:`comma-separated`,autoComplete:`off`,spellCheck:!1,value:S,onChange:e=>re(e.target.value)})}),(0,P.jsx)(u,{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,P.jsx)(`textarea`,{id:`rs-params`,spellCheck:!1,placeholder:`{"id": {"type": "int", "min": 1}}`,value:C,onChange:e=>ie(e.target.value)})}),(0,P.jsxs)(c,{onCancel:r,children:[(0,P.jsx)(`button`,{type:`button`,className:`btn`,disabled:d.isPending,title:`Save without publishing`,onClick:()=>T(!1),children:`Save as draft`}),(0,P.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:d.isPending,children:`Save`})]})]})]})}export{V as DesignerPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{B as e,H as t,R as n,c as r,l as i,n as a,s as o,t as s,v as c,z as l}from"./client-ZHNiMG8H.js";import{t as u}from"./useMutation-
|
|
1
|
+
import{B as e,H as t,R as n,c as r,l as i,n as a,s as o,t as s,v as c,z as l}from"./client-ZHNiMG8H.js";import{t as u}from"./useMutation-DQTq-cNw.js";import{D as d,E as f,T as p,k as m,l as h}from"./index--QHzN3S_.js";import{n as g,t as _}from"./icons-CFXE8hAz.js";import{t as v}from"./Managed-BwU65gDK.js";import{r as y,t as b}from"./shared-CnZhEfLp.js";var x=t(e(),1),S=l();function C(){let e=b(),t=h(),{openDrawer:i,toast:s}=m(),c=y(),l=n(),u=t(`destinations.write`),f=()=>i({title:`New destination`,content:(0,S.jsx)(w,{})}),p=async e=>{try{let t=o(await a.POST(`/api/v1/destinations/{name}/test`,{params:{path:{name:e}}}));s(`✓ Wrote ${t.object} (${t.elapsed_ms} ms)`)}catch(e){c(e)}},x=async e=>{if(window.confirm(`Delete destination '${e}'? Its credentials are removed; files already written stay.`))try{r(await a.DELETE(`/api/v1/destinations/{name}`,{params:{path:{name:e}}})),s(`Deleted `+e),l.invalidateQueries({queryKey:[`destinations`]})}catch(e){c(e)}},C;return C=e.isPending?(0,S.jsx)(d,{text:`Loading destinations…`}):e.isError?(0,S.jsx)(`div`,{className:`hint`,children:e.error.message}):e.data.length?(0,S.jsx)(`div`,{style:{overflowX:`auto`},children:(0,S.jsxs)(`table`,{className:`grid`,children:[(0,S.jsx)(`thead`,{children:(0,S.jsx)(`tr`,{children:[`Name`,`Writes under`,`Storage`,`Credentials`,``].map((e,t)=>(0,S.jsx)(`th`,{children:e},t))})}),(0,S.jsx)(`tbody`,{children:e.data.map(e=>(0,S.jsxs)(`tr`,{"data-name":e.name,children:[(0,S.jsxs)(`td`,{children:[(0,S.jsx)(`span`,{className:`name`,children:e.name}),` `,(0,S.jsx)(v,{kind:`destinations`,name:e.name})]}),(0,S.jsx)(`td`,{className:`mono`,children:e.url}),(0,S.jsx)(`td`,{className:`dim`,children:[e.storage??`local folder`,e.region,e.endpoint].filter(Boolean).join(` · `)}),(0,S.jsx)(`td`,{className:`dim`,children:e.user?`${e.user} · ${e.password??`no secret`}`:`none`}),(0,S.jsx)(`td`,{children:(0,S.jsxs)(`div`,{className:`actions`,hidden:!u,children:[(0,S.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>void p(e.name),children:`Test`}),(0,S.jsx)(`button`,{type:`button`,className:`btn ghost sm icon`,title:`Edit ${e.name}`,"aria-label":`Edit`,onClick:()=>i({title:`Edit destination`,kicker:e.name,content:(0,S.jsx)(w,{name:e.name})}),children:(0,S.jsx)(_,{})}),(0,S.jsx)(`button`,{type:`button`,className:`btn ghost sm icon danger`,title:`Delete ${e.name}`,"aria-label":`Delete`,onClick:()=>void x(e.name),children:(0,S.jsx)(g,{})})]})})]},e.name))})]})}):(0,S.jsxs)(`div`,{className:`empty`,children:[(0,S.jsx)(`strong`,{children:`No destinations yet`}),(0,S.jsx)(`span`,{children:`A destination is where exports may write - an S3, Google Cloud Storage or R2 bucket prefix, or a folder - and the credentials to write there. Nothing is ever written outside it.`}),u&&(0,S.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:f,children:`New destination`})]}),(0,S.jsxs)(S.Fragment,{children:[(0,S.jsxs)(`div`,{className:`page-head`,children:[(0,S.jsxs)(`div`,{className:`titles`,children:[(0,S.jsx)(`h1`,{children:`Destinations`}),(0,S.jsx)(`span`,{className:`sub`,children:`Buckets and folders exports may write to, and the credentials to write there.`})]}),(0,S.jsx)(`span`,{className:`spacer`}),u&&(0,S.jsx)(`button`,{id:`new-destination`,type:`button`,className:`btn primary`,onClick:f,children:`New destination`})]}),(0,S.jsx)(`div`,{id:`destinations-table`,className:`panel`,children:C})]})}function w({name:e}){let t=c({queryKey:[`destinations`,`detail`,e],queryFn:async()=>i(await a.GET(`/api/v1/destinations/{name}`,{params:{path:{name:e}}})),enabled:!!e,retry:!1,gcTime:0});return e&&t.isPending?(0,S.jsx)(d,{}):e&&t.isError?(0,S.jsx)(`div`,{className:`hint`,children:t.error.message}):(0,S.jsx)(T,{name:e,existing:t.data?.data,etag:t.data?.etag??null},e??``)}function T({name:e,existing:t,etag:r}){let i=!!e,{closeDrawer:c,showError:l,toast:d}=m(),h=y(),g=n(),[_,v]=(0,x.useState)(e??``),[b,C]=(0,x.useState)(t?.url??``),[w,T]=(0,x.useState)(t?.region??``),[E,D]=(0,x.useState)(t?.endpoint??``),[O,k]=(0,x.useState)(t?.url_style??``),[A,j]=(0,x.useState)(t?.use_ssl!==!1),[M,N]=(0,x.useState)(t?.account_id??``),[P,F]=(0,x.useState)(t?.user??``),[I,L]=(0,x.useState)(t?.password??``),[R,z]=(0,x.useState)(null),B=/^(s3|gs|gcs|r2):\/\//.test(b),V=e=>({url:b.trim(),region:B&&w.trim()||e,endpoint:B&&E.trim()||e,url_style:B&&O||e,use_ssl:B&&!A?!1:e,account_id:B&&M.trim()||e,user:B&&P.trim()||e,password:B&&I||e}),H=u({mutationFn:async()=>o(i?await a.PATCH(`/api/v1/destinations/{name}`,{params:{path:{name:e},header:r?{"If-Match":r}:{}},body:V(null)}):await a.POST(`/api/v1/destinations`,{body:{name:_.trim(),...V(void 0)}})),onSuccess:e=>{l(``),g.invalidateQueries({queryKey:[`destinations`]}),c(),d((i?`Updated `:`Created `)+e.name)},onError:h}),U=u({mutationFn:async()=>i&&I===t?.password?o(await a.POST(`/api/v1/destinations/{name}/test`,{params:{path:{name:e}}})):o(await a.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 s,e.message)})});return(0,S.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{if(e.preventDefault(),!i&&!_.trim()){l(`A name is required.`,{errors:{name:`This field is required`}});return}H.mutate()},children:[(0,S.jsx)(p,{id:`d-name`,label:`Name`,hint:i?void 0:`How exports refer to it, e.g. partner-acme.`,children:(0,S.jsx)(`input`,{id:`d-name`,value:_,placeholder:`partner-acme`,autoComplete:`off`,spellCheck:!1,disabled:i,autoFocus:!i,onChange:e=>v(e.target.value)})}),(0,S.jsx)(p,{id:`d-url`,label:`Writes under`,hint:`An s3://, gs:// or r2:// prefix, or a folder (/exports/ or file:///exports/), ending in /. Exports write here and nowhere else.`,children:(0,S.jsx)(`input`,{id:`d-url`,value:b,placeholder:`s3://acme-exchange/from-us/`,autoComplete:`off`,spellCheck:!1,onChange:e=>C(e.target.value)})}),B&&(0,S.jsxs)(`fieldset`,{className:`duck-files`,children:[(0,S.jsx)(`legend`,{children:`Object storage`}),(0,S.jsxs)(`div`,{className:`grid2`,children:[(0,S.jsx)(p,{id:`d-user`,label:`Access key ID`,children:(0,S.jsx)(`input`,{id:`d-user`,value:P,autoComplete:`off`,spellCheck:!1,onChange:e=>F(e.target.value)})}),(0,S.jsx)(p,{id:`d-password`,label:`Secret access key`,hint:i?`Leave the mask to keep the stored secret.`:"${ENV_VAR} references are resolved on the server.",children:(0,S.jsx)(`input`,{id:`d-password`,type:`password`,value:I,autoComplete:`off`,onChange:e=>L(e.target.value)})})]}),(0,S.jsxs)(`div`,{className:`grid2`,children:[(0,S.jsx)(p,{id:`d-region`,label:`Region`,children:(0,S.jsx)(`input`,{id:`d-region`,value:w,placeholder:`eu-west-1`,onChange:e=>T(e.target.value)})}),(0,S.jsx)(p,{id:`d-endpoint`,label:`Endpoint`,hint:`Only for an S3-compatible service, e.g. minio.internal:9000.`,children:(0,S.jsx)(`input`,{id:`d-endpoint`,value:E,onChange:e=>D(e.target.value)})})]}),(0,S.jsxs)(`div`,{className:`grid2`,children:[(0,S.jsx)(p,{id:`d-url-style`,label:`URL style`,children:(0,S.jsxs)(`select`,{id:`d-url-style`,value:O,onChange:e=>k(e.target.value),children:[(0,S.jsx)(`option`,{value:``,children:`Default`}),(0,S.jsx)(`option`,{value:`vhost`,children:`Virtual-hosted (bucket.host)`}),(0,S.jsx)(`option`,{value:`path`,children:`Path (host/bucket)`})]})}),(0,S.jsx)(p,{id:`d-account`,label:`Account ID`,hint:`Cloudflare R2 only.`,children:(0,S.jsx)(`input`,{id:`d-account`,value:M,onChange:e=>N(e.target.value)})})]}),(0,S.jsxs)(`label`,{className:`switch`,children:[(0,S.jsx)(`input`,{id:`d-ssl`,type:`checkbox`,checked:A,onChange:e=>j(e.target.checked)}),`Use HTTPS`]})]}),(0,S.jsxs)(`div`,{className:`test-row`,children:[(0,S.jsx)(`button`,{type:`button`,className:`btn`,disabled:U.isPending||!b.trim(),onClick:()=>U.mutate(),children:U.isPending?`Testing…`:`Test`}),(0,S.jsx)(`span`,{className:`test-result`,children:R&&(0,S.jsx)(`span`,{className:R.ok?`test-ok`:`test-fail`,children:R.text})})]}),(0,S.jsxs)(`div`,{className:`hint`,children:[`Test writes one small file, `,`_queryapigate_probe.csv`,`, under the prefix - overwritten each time.`]}),(0,S.jsx)(f,{onCancel:c,children:(0,S.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:H.isPending,children:i?`Save`:`Create`})})]})}export{C as DestinationsPage};
|