queryapigate 0.45.0__tar.gz → 0.47.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.
Files changed (246) hide show
  1. {queryapigate-0.45.0/queryapigate.egg-info → queryapigate-0.47.0}/PKG-INFO +1 -1
  2. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/__init__.py +1 -1
  3. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/app.py +23 -17
  4. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/config.py +9 -0
  5. queryapigate-0.45.0/queryapigate/console_dist/assets/AccessMapPage-CNBzMwLN.js → queryapigate-0.47.0/queryapigate/console_dist/assets/AccessMapPage-CvmP9Sfw.js +2 -2
  6. queryapigate-0.45.0/queryapigate/console_dist/assets/AdministratorsPage-CLfRjojL.js → queryapigate-0.47.0/queryapigate/console_dist/assets/AdministratorsPage-B1G7Dgxv.js +1 -1
  7. queryapigate-0.45.0/queryapigate/console_dist/assets/AgentsPage-DlmXHHcH.js → queryapigate-0.47.0/queryapigate/console_dist/assets/AgentsPage-ex_lhxAd.js +1 -1
  8. queryapigate-0.45.0/queryapigate/console_dist/assets/AlertsPage-Dt4Uwz6s.js → queryapigate-0.47.0/queryapigate/console_dist/assets/AlertsPage-CkFfLPBu.js +1 -1
  9. queryapigate-0.45.0/queryapigate/console_dist/assets/ApiKeyDetailPage-D7Ycd4O4.js → queryapigate-0.47.0/queryapigate/console_dist/assets/ApiKeyDetailPage-DluS3aag.js +1 -1
  10. queryapigate-0.45.0/queryapigate/console_dist/assets/ApiKeysPage-BGgDoVCd.js → queryapigate-0.47.0/queryapigate/console_dist/assets/ApiKeysPage-D3xeaQlS.js +1 -1
  11. queryapigate-0.45.0/queryapigate/console_dist/assets/ApprovalsPage-CKF0I-Kz.js → queryapigate-0.47.0/queryapigate/console_dist/assets/ApprovalsPage-BYL5hXGT.js +1 -1
  12. queryapigate-0.45.0/queryapigate/console_dist/assets/AuditLogPage-C5YiXeK-.js → queryapigate-0.47.0/queryapigate/console_dist/assets/AuditLogPage-TAMz5LIn.js +1 -1
  13. queryapigate-0.45.0/queryapigate/console_dist/assets/CachingPage-Da1rD-zo.js → queryapigate-0.47.0/queryapigate/console_dist/assets/CachingPage-8OhOUgxW.js +1 -1
  14. queryapigate-0.45.0/queryapigate/console_dist/assets/ConnectionsPage-BajPP7ix.js → queryapigate-0.47.0/queryapigate/console_dist/assets/ConnectionsPage-mHkn5I8c.js +1 -1
  15. queryapigate-0.45.0/queryapigate/console_dist/assets/DesignerPage-CIzg9Apa.js → queryapigate-0.47.0/queryapigate/console_dist/assets/DesignerPage-CPZneqCQ.js +1 -1
  16. queryapigate-0.45.0/queryapigate/console_dist/assets/DestinationsPage-BMGKQgUe.js → queryapigate-0.47.0/queryapigate/console_dist/assets/DestinationsPage-Dy6NYnv3.js +1 -1
  17. queryapigate-0.45.0/queryapigate/console_dist/assets/ExportsPage-C9w4up5N.js → queryapigate-0.47.0/queryapigate/console_dist/assets/ExportsPage-BzDOR2fh.js +1 -1
  18. queryapigate-0.47.0/queryapigate/console_dist/assets/HelpPage-BV_YH8GV.js +69 -0
  19. queryapigate-0.45.0/queryapigate/console_dist/assets/HomePage-Cywjwlpb.js → queryapigate-0.47.0/queryapigate/console_dist/assets/HomePage-BShpQIil.js +2 -2
  20. queryapigate-0.45.0/queryapigate/console_dist/assets/KeyActivity-D673KcWq.js → queryapigate-0.47.0/queryapigate/console_dist/assets/KeyActivity-DuTyhSGT.js +1 -1
  21. queryapigate-0.45.0/queryapigate/console_dist/assets/Managed-VPfeN8Yu.js → queryapigate-0.47.0/queryapigate/console_dist/assets/Managed-CQ0VXNNZ.js +1 -1
  22. queryapigate-0.45.0/queryapigate/console_dist/assets/McpPage-aH1mkjzt.js → queryapigate-0.47.0/queryapigate/console_dist/assets/McpPage-JMpMLJIz.js +1 -1
  23. queryapigate-0.45.0/queryapigate/console_dist/assets/MetricsPage-BqIkrlP1.js → queryapigate-0.47.0/queryapigate/console_dist/assets/MetricsPage-BOUZqgao.js +1 -1
  24. queryapigate-0.45.0/queryapigate/console_dist/assets/QueryInfo-Bv-0Zmc5.js → queryapigate-0.47.0/queryapigate/console_dist/assets/QueryInfo-n4U1MkL1.js +1 -1
  25. queryapigate-0.45.0/queryapigate/console_dist/assets/RepositoryPage-lzLrJRXS.js → queryapigate-0.47.0/queryapigate/console_dist/assets/RepositoryPage-Cj_ZwMQj.js +1 -1
  26. queryapigate-0.45.0/queryapigate/console_dist/assets/Results-XnOh21dp.js → queryapigate-0.47.0/queryapigate/console_dist/assets/Results-6QW3JK2B.js +1 -1
  27. queryapigate-0.45.0/queryapigate/console_dist/assets/RolesPage-Cv5S5X7g.js → queryapigate-0.47.0/queryapigate/console_dist/assets/RolesPage-Dq83u-am.js +1 -1
  28. queryapigate-0.45.0/queryapigate/console_dist/assets/SchemaBrowser-8tmsr1__.js → queryapigate-0.47.0/queryapigate/console_dist/assets/SchemaBrowser-BzZ6DZjO.js +1 -1
  29. queryapigate-0.45.0/queryapigate/console_dist/assets/SettingsPage-pOVvmb4i.js → queryapigate-0.47.0/queryapigate/console_dist/assets/SettingsPage-CpjtKQ1P.js +1 -1
  30. queryapigate-0.45.0/queryapigate/console_dist/assets/Time-s5E5qK3s.js → queryapigate-0.47.0/queryapigate/console_dist/assets/Time-DurHvFXj.js +1 -1
  31. queryapigate-0.45.0/queryapigate/console_dist/assets/WorkspacesPage-B1lg3P5d.js → queryapigate-0.47.0/queryapigate/console_dist/assets/WorkspacesPage-Bfe_wYYe.js +1 -1
  32. queryapigate-0.45.0/queryapigate/console_dist/assets/api-DIm7t7Bb.js → queryapigate-0.47.0/queryapigate/console_dist/assets/api-CgyYL3VS.js +1 -1
  33. queryapigate-0.45.0/queryapigate/console_dist/assets/forms-B7OrYKBb.js → queryapigate-0.47.0/queryapigate/console_dist/assets/forms-DR_JyNks.js +1 -1
  34. queryapigate-0.45.0/queryapigate/console_dist/assets/forms-CTAVu1q5.js → queryapigate-0.47.0/queryapigate/console_dist/assets/forms-DdFqq646.js +1 -1
  35. queryapigate-0.45.0/queryapigate/console_dist/assets/index-Cl93xBjn.js → queryapigate-0.47.0/queryapigate/console_dist/assets/index-0v-rImZs.js +2 -2
  36. queryapigate-0.45.0/queryapigate/console_dist/assets/shared-epEBEbJD.js → queryapigate-0.47.0/queryapigate/console_dist/assets/shared-D8E1i8vU.js +1 -1
  37. queryapigate-0.45.0/queryapigate/console_dist/assets/useMutation-BIoTFTZ8.js → queryapigate-0.47.0/queryapigate/console_dist/assets/useMutation-D4M562eQ.js +1 -1
  38. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/console_dist/index.html +1 -1
  39. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/dataplane.py +23 -1
  40. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/events.py +29 -6
  41. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/experimental.py +3 -3
  42. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/governance.py +22 -2
  43. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/gunicorn_conf.py +15 -1
  44. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/history.py +9 -0
  45. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/mcp_server.py +20 -3
  46. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/runstore.py +140 -26
  47. {queryapigate-0.45.0 → queryapigate-0.47.0/queryapigate.egg-info}/PKG-INFO +1 -1
  48. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate.egg-info/SOURCES.txt +33 -33
  49. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_dataplane.py +52 -6
  50. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_helm.py +57 -1
  51. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_integration.py +2 -2
  52. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_mcp.py +76 -0
  53. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_queryapigate.py +33 -0
  54. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_runstore.py +169 -1
  55. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_settings.py +1 -1
  56. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_supply_chain.py +4 -2
  57. queryapigate-0.45.0/queryapigate/console_dist/assets/HelpPage-NSJp_qwt.js +0 -69
  58. {queryapigate-0.45.0 → queryapigate-0.47.0}/LICENSE +0 -0
  59. {queryapigate-0.45.0 → queryapigate-0.47.0}/NOTICE +0 -0
  60. {queryapigate-0.45.0 → queryapigate-0.47.0}/README.md +0 -0
  61. {queryapigate-0.45.0 → queryapigate-0.47.0}/pyproject.toml +0 -0
  62. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/__main__.py +0 -0
  63. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/accesslog.py +0 -0
  64. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/adminroles.py +0 -0
  65. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/admins.py +0 -0
  66. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/agentwrites.py +0 -0
  67. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/alerts.py +0 -0
  68. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/apikeys.py +0 -0
  69. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/auditchain.py +0 -0
  70. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/auditstream.py +0 -0
  71. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/aws.py +0 -0
  72. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/broadcast.py +0 -0
  73. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/bundle.py +0 -0
  74. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/cache.py +0 -0
  75. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/cli.py +0 -0
  76. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/collection_admin.py +0 -0
  77. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/configcode.py +0 -0
  78. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/console.py +0 -0
  79. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/console_dist/assets/CodeBox-Cwh5KHLp.js +0 -0
  80. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/console_dist/assets/Experimental-B3dnaeV7.js +0 -0
  81. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/console_dist/assets/StatTile-Cft4kJPH.js +0 -0
  82. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/console_dist/assets/client-ZHNiMG8H.js +0 -0
  83. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/console_dist/assets/icons-CFXE8hAz.js +0 -0
  84. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/console_dist/assets/index-Cv64XKNs.css +0 -0
  85. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/console_dist/assets/maskRules-CvKQIPgN.js +0 -0
  86. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/console_dist/assets/reach-BAOLd75Y.js +0 -0
  87. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/console_dist/assets/state-O13cl7cO.js +0 -0
  88. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/console_dist/favicon.svg +0 -0
  89. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/cors.py +0 -0
  90. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/databases.py +0 -0
  91. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/db.py +0 -0
  92. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/definitions.py +0 -0
  93. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/deprecations.py +0 -0
  94. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/descriptions.py +0 -0
  95. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/destinations.py +0 -0
  96. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/duckfiles.py +0 -0
  97. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/embeddings.py +0 -0
  98. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/engine.py +0 -0
  99. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/errors.py +0 -0
  100. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/examples.py +0 -0
  101. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/exporting.py +0 -0
  102. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/exports.py +0 -0
  103. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/formats.py +0 -0
  104. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/instances.py +0 -0
  105. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/jwtauth.py +0 -0
  106. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/lib/h2-2.2.224.jar +0 -0
  107. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/limits.py +0 -0
  108. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/logging_setup.py +0 -0
  109. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/masking.py +0 -0
  110. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/metacache.py +0 -0
  111. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/metrics.py +0 -0
  112. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/mongodocs.py +0 -0
  113. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/mongotools.py +0 -0
  114. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/oidc.py +0 -0
  115. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/openapi.py +0 -0
  116. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/params.py +0 -0
  117. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/parquet.py +0 -0
  118. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/pool.py +0 -0
  119. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/postman.py +0 -0
  120. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/quotas.py +0 -0
  121. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/ratelimit.py +0 -0
  122. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/rediscache.py +0 -0
  123. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/rekey.py +0 -0
  124. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/runners.py +0 -0
  125. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/schema.py +0 -0
  126. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/scope.py +0 -0
  127. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/secheaders.py +0 -0
  128. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/secretrefs.py +0 -0
  129. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/services/__init__.py +0 -0
  130. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/services/access.py +0 -0
  131. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/services/administrators.py +0 -0
  132. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/services/audit.py +0 -0
  133. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/services/collections.py +0 -0
  134. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/services/connections.py +0 -0
  135. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/services/descriptions.py +0 -0
  136. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/services/destinations.py +0 -0
  137. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/services/exports.py +0 -0
  138. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/services/mcp.py +0 -0
  139. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/services/queries.py +0 -0
  140. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/services/workspaces.py +0 -0
  141. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/sqlflow.py +0 -0
  142. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/sqltools.py +0 -0
  143. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/sso.py +0 -0
  144. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/store.py +0 -0
  145. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/tableguard.py +0 -0
  146. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/tracing.py +0 -0
  147. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/usage.py +0 -0
  148. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/v1.py +0 -0
  149. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/v1_spec.py +0 -0
  150. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/vault.py +0 -0
  151. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate/workspaces.py +0 -0
  152. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate.egg-info/dependency_links.txt +0 -0
  153. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate.egg-info/entry_points.txt +0 -0
  154. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate.egg-info/requires.txt +0 -0
  155. {queryapigate-0.45.0 → queryapigate-0.47.0}/queryapigate.egg-info/top_level.txt +0 -0
  156. {queryapigate-0.45.0 → queryapigate-0.47.0}/setup.cfg +0 -0
  157. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_adhoc_history.py +0 -0
  158. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_adminroles.py +0 -0
  159. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_admins.py +0 -0
  160. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_agent_writes.py +0 -0
  161. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_alerts.py +0 -0
  162. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_api_administrators.py +0 -0
  163. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_api_keys.py +0 -0
  164. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_api_v1.py +0 -0
  165. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_audit_chain.py +0 -0
  166. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_audit_log.py +0 -0
  167. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_audit_siem.py +0 -0
  168. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_audit_stream.py +0 -0
  169. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_aws.py +0 -0
  170. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_backup_restore.py +0 -0
  171. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_broadcast.py +0 -0
  172. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_cli.py +0 -0
  173. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_collection_bundle.py +0 -0
  174. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_collection_postman.py +0 -0
  175. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_collections.py +0 -0
  176. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_configcode.py +0 -0
  177. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_connection_encryption.py +0 -0
  178. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_console.py +0 -0
  179. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_cors_and_rate_limit.py +0 -0
  180. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_data_protection.py +0 -0
  181. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_databases.py +0 -0
  182. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_db.py +0 -0
  183. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_dependency_floors.py +0 -0
  184. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_deprecations.py +0 -0
  185. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_descriptions.py +0 -0
  186. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_destinations.py +0 -0
  187. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_docs_site.py +0 -0
  188. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_duckdb.py +0 -0
  189. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_duckdb_files.py +0 -0
  190. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_embeddings.py +0 -0
  191. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_errors.py +0 -0
  192. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_events.py +0 -0
  193. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_events_server.py +0 -0
  194. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_examples.py +0 -0
  195. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_experimental.py +0 -0
  196. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_export_security.py +0 -0
  197. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_exporting.py +0 -0
  198. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_exports.py +0 -0
  199. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_fips.py +0 -0
  200. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_history.py +0 -0
  201. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_howto.py +0 -0
  202. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_instances.py +0 -0
  203. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_jdbc.py +0 -0
  204. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_jwt.py +0 -0
  205. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_key_rotation.py +0 -0
  206. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_limits.py +0 -0
  207. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_masking.py +0 -0
  208. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_masking_requests.py +0 -0
  209. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_mcp_keycloak.py +0 -0
  210. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_mcp_oauth.py +0 -0
  211. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_metacache.py +0 -0
  212. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_mongo_streaming.py +0 -0
  213. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_mongotools.py +0 -0
  214. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_multinode.py +0 -0
  215. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_observability.py +0 -0
  216. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_oidc.py +0 -0
  217. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_params.py +0 -0
  218. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_parquet.py +0 -0
  219. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_pool.py +0 -0
  220. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_publishing.py +0 -0
  221. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_query_window.py +0 -0
  222. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_quotas.py +0 -0
  223. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_rekey.py +0 -0
  224. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_release_check.py +0 -0
  225. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_response_cache.py +0 -0
  226. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_roles.py +0 -0
  227. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_rolling_upgrade.py +0 -0
  228. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_runstore_differential.py +0 -0
  229. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_secretrefs.py +0 -0
  230. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_security_headers.py +0 -0
  231. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_shared_rate_limit.py +0 -0
  232. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_sql_guard_fuzz.py +0 -0
  233. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_sql_guard_writes.py +0 -0
  234. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_sqlflow.py +0 -0
  235. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_sso.py +0 -0
  236. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_sso_keycloak.py +0 -0
  237. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_tableguard.py +0 -0
  238. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_tracing.py +0 -0
  239. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_upgrades.py +0 -0
  240. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_usage.py +0 -0
  241. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_vault.py +0 -0
  242. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_workspace_config_cli.py +0 -0
  243. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_workspace_isolation.py +0 -0
  244. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_workspace_observability.py +0 -0
  245. {queryapigate-0.45.0 → queryapigate-0.47.0}/tests/test_workspace_store.py +0 -0
  246. {queryapigate-0.45.0 → queryapigate-0.47.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.45.0
3
+ Version: 0.47.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
@@ -1,5 +1,5 @@
1
1
  """QueryAPIGate - expose SQL databases as a REST API."""
2
- __version__ = "0.45.0"
2
+ __version__ = "0.47.0"
3
3
 
4
4
  from .app import create_app # noqa: E402 (app imports __version__)
5
5
 
@@ -1,5 +1,4 @@
1
1
  """The Flask application: HTTP routes on top of the store, engine and formatters."""
2
- import ipaddress
3
2
  import json
4
3
  import logging
5
4
  import math
@@ -178,8 +177,9 @@ class _CapturePeer:
178
177
  Behind a data plane (dataplane.py, ADR 0028) every request comes from it, over loopback: one that carries its
179
178
  secret names the client's own socket address in X-QAG-Peer, and that address is taken as the real one - so this
180
179
  server sees the request as if the client had connected directly, ProxyFix and QUERYAPIGATE_TRUST_PROXY included.
181
- A peer that isn't an IP address counts as remote, as a Unix socket does. Both headers are removed here, so
182
- nothing after this layer reads, logs or passes on the secret."""
180
+ A peer that isn't an IP address counts as remote, as a Unix socket does. A data plane from another release
181
+ (X-QAG-Dataplane-Version) is still the data plane, but is given no plan (dataplane.paired()). Its headers are
182
+ removed here, so nothing after this layer reads, logs or passes on the secret."""
183
183
 
184
184
  def __init__(self, app):
185
185
  self.app = app
@@ -188,21 +188,20 @@ class _CapturePeer:
188
188
  supplied = environ.pop(dataplane.SECRET_ENVIRON, None)
189
189
  peer = environ.pop(dataplane.PEER_ENVIRON, None)
190
190
  wanted = environ.pop(dataplane.WANTED_ENVIRON, None)
191
+ version = environ.pop(dataplane.VERSION_ENVIRON, None)
191
192
  if supplied is not None and dataplane.matches(supplied):
192
- environ['queryapigate.dataplane'] = 'plan' if wanted == '1' else True
193
+ is_paired = dataplane.paired(version)
194
+ environ['queryapigate.dataplane'] = 'plan' if wanted == '1' and is_paired else True
195
+ environ['queryapigate.dataplane_paired'] = is_paired
193
196
  environ['REMOTE_ADDR'] = peer if dataplane.is_address(peer) else ''
194
197
  environ['queryapigate.peer_addr'] = environ.get('REMOTE_ADDR')
195
198
  return self.app(environ, start_response)
196
199
 
197
200
 
198
201
  def _peer_is_local():
199
- """Whether the request's real socket peer is a loopback address. Fails closed: an address that can't be parsed,
200
- or none at all (e.g. a Unix socket), counts as remote - in open-access mode the caller must then configure a key."""
201
- peer = request.environ.get('queryapigate.peer_addr') or ''
202
- try:
203
- return ipaddress.ip_address(peer).is_loopback
204
- except ValueError:
205
- return False
202
+ """Whether the request's real socket peer is a loopback address (governance.is_loopback(), the MCP server's rule
203
+ too). Fails closed: in open-access mode a caller it can't place must configure a key."""
204
+ return governance.is_loopback(request.environ.get('queryapigate.peer_addr'))
206
205
 
207
206
 
208
207
  def create_app():
@@ -362,8 +361,7 @@ def create_app():
362
361
  # could reach the port would otherwise be an unrestricted admin. The real socket peer decides, never a
363
362
  # forgeable header. The public endpoints (health, docs, the Console shell) are reachable either way.
364
363
  if g.permission is apikeys.OPEN and request.endpoint not in PUBLIC_ENDPOINTS and not _peer_is_local():
365
- raise ApiError('This server has no API key set, so it answers only local clients. Set '
366
- 'QUERYAPIGATE_API_KEY to allow access from other hosts.', 403, code='open_access_remote')
364
+ raise ApiError(governance.OPEN_ACCESS_REMOTE, 403, code='open_access_remote')
367
365
  if g.permission is not None:
368
366
  g.permission = settle_workspace(g.permission, named)
369
367
  limited = check_key_rate_limit()
@@ -1276,25 +1274,33 @@ def dataplane_runs():
1276
1274
  def dataplane_lease():
1277
1275
  """A data plane's block of the event ids it mints when it writes its runs to the run store itself (runstore.py,
1278
1276
  ADR 0029 §2), leased here so its store access stays read-only: `{"holder": ...}` leases one, `{"holder": ...,
1279
- "block": n}` renews it (409 once it has lapsed: lease again). With the layout of an id and the settle window, so
1280
- the two processes can't disagree on them. 404 without a run store."""
1277
+ "block": n}` renews it (409 once it has lapsed: lease again), and with `"release": true` gives it back, as it stops.
1278
+ With the layout of an id and the settle window, so the two processes can't disagree on them, and where the run
1279
+ store is (`location`, as the metadata store records it): a data plane configured with another one writes nothing
1280
+ there. 404 without a run store."""
1281
1281
  if not runstore.enabled():
1282
1282
  return jsonify(error_body('No run store is configured', 404)), 404
1283
+ if not request.environ.get('queryapigate.dataplane_paired'):
1284
+ return jsonify(error_body(dataplane.UNPAIRED, 409)), 409
1283
1285
  body = request.get_json(silent=True) or {}
1284
1286
  holder = body.get('holder')
1285
1287
  if not isinstance(holder, str) or not 0 < len(holder) <= 200:
1286
1288
  return jsonify(error_body('holder: a name for the data plane process', 400)), 400
1289
+ if body.get('release') is True and isinstance(body.get('block'), int):
1290
+ runstore.release(body['block'], holder)
1291
+ return '', 204
1287
1292
  if body.get('block') is not None:
1288
1293
  if not isinstance(body['block'], int) or not runstore.renew(body['block'], holder):
1289
1294
  return jsonify(error_body('That lease has lapsed - lease a block again', 409)), 409
1290
1295
  return jsonify({'block': body['block'], 'lease_seconds': runstore.LEASE_SECONDS}), 200
1291
1296
  try:
1292
1297
  block = runstore.lease(runstore.DATAPLANE_BLOCK, holder)
1293
- except runstore.LeaseError as error:
1294
- return jsonify(error_body(str(error), 503)), 503
1298
+ except runstore.LeaseError:
1299
+ return jsonify(error_body(runstore.LEASES_TAKEN, 503)), 503 # the only reason, and no exception's text
1295
1300
  return jsonify({'block': block, 'size': runstore.DATAPLANE_BLOCK, 'lease_seconds': runstore.LEASE_SECONDS,
1296
1301
  'epoch_ms': runstore.EPOCH_MS, 'per_ms': runstore.PER_MS,
1297
1302
  'settle_seconds': runstore.SETTLE_SECONDS, 'database': runstore.database(),
1303
+ 'location': runstore.location(),
1298
1304
  'sample_rate': config.history_sample_rate()}), 200
1299
1305
 
1300
1306
 
@@ -244,6 +244,13 @@ def threads():
244
244
  return int(raw) if raw.isdigit() and 1 <= int(raw) <= MAX_THREADS else DEFAULT_THREADS
245
245
 
246
246
 
247
+ def bind():
248
+ """QUERYAPIGATE_BIND: the address the Docker image's gunicorn listens on (gunicorn_conf.py) - the image sets
249
+ 0.0.0.0:5000, and behind the Rust data plane, which then takes that port, 127.0.0.1:5001 (ADR 0030). None when
250
+ unset: gunicorn's own default, or its --bind."""
251
+ return os.environ.get('QUERYAPIGATE_BIND', '').strip() or None
252
+
253
+
247
254
  def access_log():
248
255
  """QUERYAPIGATE_ACCESS_LOG: the image's gunicorn writes an access line per request to stdout (on by default). Off
249
256
  for a server whose proxy or load balancer already logs requests - what a line costs, with no one reading it."""
@@ -1500,6 +1507,8 @@ def describe_settings():
1500
1507
  'description': 'Requests served at once, and idle connections kept open per distinct connection.', 'rows': [
1501
1508
  row('Threads', "Requests one server process serves at once - the Docker image's gunicorn. Read at "
1502
1509
  'startup.', 'QUERYAPIGATE_THREADS', str(threads())),
1510
+ row('Listen address', "Where the Docker image's gunicorn listens: 127.0.0.1:5001 behind the data plane, "
1511
+ 'which takes the public port. Read at startup.', 'QUERYAPIGATE_BIND', bind() or "gunicorn's own"),
1503
1512
  row('Pool size', 'Idle connections kept per connection - by default, one per thread. 0 disables pooling.',
1504
1513
  'QUERYAPIGATE_POOL_SIZE', str(pool_size())),
1505
1514
  row('Idle timeout', 'How long an idle pooled connection is kept before it is closed.',
@@ -1,2 +1,2 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/forms-B7OrYKBb.js","assets/client-ZHNiMG8H.js","assets/index-Cl93xBjn.js","assets/index-Cv64XKNs.css","assets/CodeBox-Cwh5KHLp.js","assets/api-DIm7t7Bb.js","assets/useMutation-BIoTFTZ8.js","assets/maskRules-CvKQIPgN.js","assets/SchemaBrowser-8tmsr1__.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-Cl93xBjn.js";import{t as d}from"./Time-s5E5qK3s.js";import{t as f}from"./QueryInfo-Bv-0Zmc5.js";import{a as p}from"./reach-BAOLd75Y.js";import{i as m,n as h,o as g,r as _}from"./SchemaBrowser-8tmsr1__.js";var v=t(e(),1),y=n(),b=(0,v.lazy)(()=>a(()=>import(`./forms-B7OrYKBb.js`).then(e=>({default:e.QueryForm})),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9]))),x={query:`Q`,collection:`C`,connection:`W`},S=[`Database`,`Created`,`Last modified`,`Last used`,`Version`],C=[`database`,`created`,`modified`,`lastUsed`,`version`],w=()=>new Date().toISOString().slice(0,10);function T(){let e=i(),{openDrawer:t}=l(),n=u(),a=o(),d=c(),x=s(),[S,C]=(0,v.useState)(m),w=e=>{let t={...S,...e};g(t),C(t)},T=x.data??[],D=e=>T.find(t=>t.name===e)?.db??null,O=h(S.conn||void 0),k=O.data?.tables??[],A=_(S.conn||void 0,D(S.conn),S.table&&!S.tableMatches?[S.table]:[]),j=S.table?S.tableMatches??(A.data?A.data[S.table]??[]:null):null,M=n.data??[],N=[...new Set(M.map(e=>D(e.connection_name)).filter(e=>!!e))].sort(),P=a.data??{},F=d.data??{},I=Object.keys(P).sort(),L=Object.keys(F).sort(),R,z=!0;S.conn?O.isPending?R=(0,y.jsx)(`option`,{value:``,children:`Loading tables…`}):O.isError?R=(0,y.jsx)(`option`,{value:``,children:`Schema unavailable`}):(z=!1,R=(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(`option`,{value:``,children:`All tables (${k.length})`}),k.map(e=>(0,y.jsx)(`option`,{value:e.name,children:`${e.name} · ${e.columns.length}${e.columns.length===1?` col`:` cols`}`},e.name))]})):R=(0,y.jsx)(`option`,{value:``,children:`Select a connection first`});let B=(e,t)=>p(e,t.name,t.collection,t.connection_name),V;if(n.isPending)V=(0,y.jsx)(r,{});else if(!M.length)V=(0,y.jsx)(`div`,{className:`empty`,children:(0,y.jsx)(`strong`,{children:`No saved queries yet`})});else if(!I.length&&!L.length)V=(0,y.jsxs)(`div`,{className:`empty`,children:[(0,y.jsx)(`strong`,{children:`No scoped API keys or roles yet`}),(0,y.jsx)(`span`,{children:`Every query is reachable by the admin key only until a scoped key is created.`})]});else{let n=S.q.trim().toLowerCase(),i=[...M].sort((e,t)=>(e.collection||`￿`)+e.name<(t.collection||`￿`)+t.name?-1:1),a=e=>e.toLowerCase().includes(n),o=n?i.filter(e=>a(e.name)||e.collection&&a(e.collection)):i,s=n?I.filter(a):I,c=n?L.filter(a):L,l=o.length||s.length||c.length,u=o.length?o:l?i:[],d=s.length?s:l?I:[],p=c.length?c:l?L:[];if(S.conn&&(u=u.filter(e=>e.connection_name===S.conn)),S.table&&j===null)V=(0,y.jsx)(r,{text:`Checking which queries reference ${S.table}…`});else if(j&&(u=u.filter(e=>j.includes(e.name))),u.length){let n=u.map(e=>({q:e,dbType:D(e.connection_name),reach:d.filter(t=>B(P[t],e).length).length}));S.db&&(n=n.filter(e=>e.dbType===S.db)),S.reach===`reachable`?n=n.filter(e=>e.reach>0):S.reach===`unreachable`&&(n=n.filter(e=>e.reach===0));let r=S.sort;if(r.field){let e=e=>{switch(r.field){case`query`:return e.q.name.toLowerCase();case`database`:return(e.dbType||``).toLowerCase();case`created`:return e.q.created_at||``;case`modified`:return e.q.updated_at||``;case`lastUsed`:return e.q.last_used_at||``;case`version`:return e.q.latest_version;case`connection`:return(e.q.connection_name||``).toLowerCase();case`reach`:return e.reach;case`col`:{let t=r.colType===`key`?P[r.colName]:F[r.colName];return t&&B(t,e.q).length?1:0}default:return``}};n=[...n].sort((t,n)=>{let i=e(t),a=e(n);return i<a?-r.dir:i>a?r.dir:0})}V=n.length?(0,y.jsx)(E,{rows:n,keyCols:d,roleCols:p,keyMap:P,roleMap:F,st:S,onSort:e=>w({sort:e}),onOpen:t=>e(`/queries/${encodeURIComponent(t)}`),onInfo:e=>t({title:e.q.name,kicker:`${e.q.collection||`uncollected`} · ${e.q.version_count}${e.q.version_count===1?` version`:` versions`}`,content:(0,y.jsx)(f,{name:e.q.name,dbType:e.dbType})})}):(0,y.jsx)(`div`,{className:`empty`,children:(0,y.jsx)(`span`,{children:`No queries match these filters.`})})}else{let i=n?`Nothing matches “${n}”.`:S.table?`“${S.table}” on ${S.conn} is not exposed by any saved query yet.`:S.conn?`No saved query uses “${S.conn}” yet.`:`Nothing matches these filters.`;V=(0,y.jsxs)(`div`,{className:`empty`,children:[(0,y.jsx)(`span`,{children:i}),!n&&S.conn?(0,y.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:()=>{let n=D(S.conn)===`mongo`,i=S.table?n?JSON.stringify({collection:S.table,filter:{}},null,2):`SELECT * FROM `+S.table:void 0;e(`/queries`),t({title:`New API`,kicker:`POST /api/v1/queries`,content:(0,y.jsx)(v.Suspense,{fallback:(0,y.jsx)(r,{}),children:(0,y.jsx)(b,{starter:{connection:S.conn,sql:i}})})})},children:`New API`+(S.table?` on `+S.table:``)}):null]})}}return(0,y.jsxs)(y.Fragment,{children:[(0,y.jsxs)(`div`,{className:`page-head`,children:[(0,y.jsxs)(`div`,{className:`titles`,children:[(0,y.jsx)(`h1`,{children:`Access map`}),(0,y.jsxs)(`span`,{className:`sub`,children:[`Which API keys can reach which saved queries - the whole point of collections and connection grants, in one place.`,` `,(0,y.jsxs)(`span`,{className:`legend`,children:[(0,y.jsx)(`span`,{className:`amap-dot`,children:`Q`}),` named query \xA0`,(0,y.jsx)(`span`,{className:`amap-dot`,children:`C`}),` `,`collection \xA0`,(0,y.jsx)(`span`,{className:`amap-dot conn`,children:`W`}),` whole connection`]})]})]}),(0,y.jsx)(`span`,{className:`spacer`}),(0,y.jsxs)(`select`,{id:`accessmap-conn-filter`,"aria-label":`Connection`,value:T.some(e=>e.name===S.conn)?S.conn:``,onChange:e=>w({conn:e.target.value,table:``,tableMatches:null}),children:[(0,y.jsx)(`option`,{value:``,children:`All connections`}),[...T].sort((e,t)=>e.name<t.name?-1:1).map(e=>(0,y.jsx)(`option`,{value:e.name,children:e.name},e.name))]}),(0,y.jsx)(`select`,{id:`accessmap-table-filter`,"aria-label":`Table`,disabled:z,value:k.some(e=>e.name===S.table)?S.table:``,onChange:e=>w({table:e.target.value,tableMatches:null}),children:R}),(0,y.jsxs)(`select`,{id:`accessmap-db-filter`,"aria-label":`Database`,value:N.includes(S.db)?S.db:``,onChange:e=>w({db:e.target.value}),children:[(0,y.jsx)(`option`,{value:``,children:`All databases`}),N.map(e=>(0,y.jsx)(`option`,{value:e,children:e},e))]}),(0,y.jsxs)(`select`,{id:`accessmap-reach-filter`,"aria-label":`Reach`,value:S.reach,onChange:e=>w({reach:e.target.value}),children:[(0,y.jsx)(`option`,{value:``,children:`Any reach`}),(0,y.jsx)(`option`,{value:`reachable`,children:`Reachable by a key`}),(0,y.jsx)(`option`,{value:`unreachable`,children:`Reachable by no key`})]}),(0,y.jsx)(`input`,{id:`accessmap-filter`,className:`search`,type:`search`,placeholder:`Filter by query or key…`,value:S.q,onChange:e=>w({q:e.target.value})})]}),(0,y.jsx)(`div`,{id:`accessmap-body`,className:`panel`,children:V})]})}function E({rows:e,keyCols:t,roleCols:n,keyMap:r,roleMap:i,st:a,onSort:o,onOpen:s,onInfo:c}){let l=a.sort,u=(e,t=null,n=null)=>l.field===e&&l.colType===t&&l.colName===n,f=(e,t=null,n=null)=>u(e,t,n)?l.dir===1?` ▲`:` ▼`:``,m=(e,t=null,n=null)=>{let r=u(e,t,n);o(r&&l.dir===1?{field:e,colType:t,colName:n,dir:-1}:r?{field:null,colType:null,colName:null,dir:1}:{field:e,colType:t,colName:n,dir:1})},h=(e,t,n)=>(0,y.jsx)(`th`,{className:t,onClick:()=>m(n),children:e+f(n)},n),g=(e,t,n,r)=>(0,y.jsxs)(`th`,{className:`amap-key-head amap-sortable`+(n?` revoked`:``),title:n?e+n:e,onClick:()=>m(`col`,r,e),children:[(0,y.jsxs)(`span`,{className:`amap-key-name`,children:[e,f(`col`,r,e),n?(0,y.jsx)(`span`,{className:`dim`,children:n}):null]}),(0,y.jsxs)(`span`,{className:`amap-key-sub`,children:[(t.allow_writes?`Read/write`:`Read-only`)+` · `+(t.rate_limit||`server default`),t.mask?.length?(0,y.jsx)(`span`,{className:`tag`,title:t.mask.map(e=>`${e.column}: ${e.action}`).join(`, `),children:`masked`}):null]})]},r+`:`+e),_=(e,t,n,r)=>{let i=p(e,t.name,t.collection,t.connection_name);if(!i.length)return(0,y.jsx)(`td`,{className:`amap-cell`},r);let a=[...new Set(i.map(e=>x[e.kind]))],o=a.length===1&&a[0]===`W`;return(0,y.jsx)(`td`,{className:`amap-cell`,title:i.map(e=>e.label).join(`, `),children:(0,y.jsx)(`span`,{className:`amap-dot`+(n?` role`:``)+(o?` conn`:``),children:a.join(`·`)})},r)},v=w();return(0,y.jsx)(`div`,{style:{overflow:`auto`},children:(0,y.jsxs)(`table`,{className:`amap`,children:[(0,y.jsxs)(`thead`,{children:[(0,y.jsxs)(`tr`,{children:[(0,y.jsx)(`th`,{colSpan:3+S.length}),t.length?(0,y.jsx)(`th`,{colSpan:t.length,className:`amap-group`,children:`API KEYS`}):null,n.length?(0,y.jsx)(`th`,{colSpan:n.length,className:`amap-group role`,title:`What a key created from each role would reach - a role grants nothing on its own`,children:`ROLES`}):null]}),(0,y.jsxs)(`tr`,{children:[h(`Query`,`amap-query amap-sortable`,`query`),C.map((e,t)=>h(S[t],`amap-sortable`,e)),h(`Connection`,`amap-sortable`,`connection`),h(`Reach`,`num amap-sortable`,`reach`),t.map(e=>{let t=r[e],n=!!(t.expires_at&&t.expires_at<v);return g(e,t,t.active?n?` (expired ${t.expires_at})`:``:` (revoked)`,`key`)}),n.map(e=>g(e,i[e],``,`role`))]})]}),(0,y.jsx)(`tbody`,{children:e.map(e=>{let a=e.q;return(0,y.jsxs)(`tr`,{children:[(0,y.jsxs)(`td`,{className:`amap-query`,children:[(0,y.jsxs)(`div`,{className:`amap-q-row`,children:[(0,y.jsx)(`button`,{type:`button`,className:`amap-q-name`,title:`Open ${a.name} in API Repository`,onClick:()=>s(a.name),children:a.name}),(0,y.jsx)(`button`,{type:`button`,className:`amap-info`,title:`Details for ${a.name}`,"aria-label":`Details for ${a.name}`,onClick:()=>c(e),children:`i`})]}),a.collection?(0,y.jsx)(`span`,{className:`amap-coll`,children:a.collection}):null]}),(0,y.jsx)(`td`,{children:e.dbType?(0,y.jsx)(`span`,{className:`tag`,children:e.dbType}):(0,y.jsx)(`span`,{className:`dim`,children:`—`})}),(0,y.jsx)(`td`,{className:`mono dim`,children:(0,y.jsx)(d,{value:a.created_at})}),(0,y.jsx)(`td`,{className:`mono dim`,children:(0,y.jsx)(d,{value:a.updated_at})}),(0,y.jsx)(`td`,{className:a.last_used_at?`mono`:`mono dim`,children:(0,y.jsx)(d,{value:a.last_used_at,fallback:`never`})}),(0,y.jsx)(`td`,{className:`mono`,children:`v`+a.latest_version}),(0,y.jsx)(`td`,{className:`mono dim`,children:a.connection_name||`—`}),(0,y.jsx)(`td`,{className:`num`,title:`Reachable by an actual API key (roles alone reach nothing)`,children:e.reach?String(e.reach):`—`}),t.map(e=>_(r[e],a,!1,`k:`+e)),n.map(e=>_(i[e],a,!0,`r:`+e))]},a.name)})}),(0,y.jsx)(`tfoot`,{children:(0,y.jsxs)(`tr`,{children:[(0,y.jsx)(`td`,{className:`amap-query`,children:`Reaches`}),S.map(e=>(0,y.jsx)(`td`,{},e)),(0,y.jsx)(`td`,{}),(0,y.jsx)(`td`,{}),t.map(t=>(0,y.jsx)(`td`,{className:`num`,children:`${e.filter(e=>p(r[t],e.q.name,e.q.collection,e.q.connection_name).length).length} / ${e.length}`},t)),n.map(t=>(0,y.jsx)(`td`,{className:`num`,children:`${e.filter(e=>p(i[t],e.q.name,e.q.collection,e.q.connection_name).length).length} / ${e.length}`},t))]})})]})})}export{T as AccessMapPage};
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/forms-DR_JyNks.js","assets/client-ZHNiMG8H.js","assets/index-0v-rImZs.js","assets/index-Cv64XKNs.css","assets/CodeBox-Cwh5KHLp.js","assets/api-CgyYL3VS.js","assets/useMutation-D4M562eQ.js","assets/maskRules-CvKQIPgN.js","assets/SchemaBrowser-BzZ6DZjO.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-0v-rImZs.js";import{t as d}from"./Time-DurHvFXj.js";import{t as f}from"./QueryInfo-n4U1MkL1.js";import{a as p}from"./reach-BAOLd75Y.js";import{i as m,n as h,o as g,r as _}from"./SchemaBrowser-BzZ6DZjO.js";var v=t(e(),1),y=n(),b=(0,v.lazy)(()=>a(()=>import(`./forms-DR_JyNks.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-BIoTFTZ8.js";import{D as f,E as p,T as m,f as h,h as g,k as _,l as v}from"./index-Cl93xBjn.js";import{t as y}from"./Time-s5E5qK3s.js";import{t as b}from"./forms-CTAVu1q5.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-D4M562eQ.js";import{D as f,E as p,T as m,f as h,h as g,k as _,l as v}from"./index-0v-rImZs.js";import{t as y}from"./Time-DurHvFXj.js";import{t as b}from"./forms-DdFqq646.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-Cl93xBjn.js";import{t as s}from"./Time-s5E5qK3s.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
+ 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-0v-rImZs.js";import{t as s}from"./Time-DurHvFXj.js";import{t as c}from"./CodeBox-Cwh5KHLp.js";var l=t(e(),1),u=a(),d=[{id:`1h`,label:`Last hour`,hours:1},{id:`24h`,label:`Last 24 hours`,hours:24},{id:`7d`,label:`Last 7 days`,hours:168},{id:`30d`,label:`Last 30 days`,hours:720}],f=e=>e.toLocaleString();function p({refusals:e}){let t=Object.entries(e);return t.length?(0,u.jsx)(`span`,{style:{display:`inline-flex`,flexWrap:`wrap`,gap:4},children:t.map(([e,t])=>(0,u.jsx)(`span`,{className:`tag`,title:`${t} call${t===1?``:`s`} failed with ${e}`,children:`${e} × ${t}`},e))}):(0,u.jsx)(`span`,{className:`dim`,children:`none`})}function m({writes:e}){let t=Object.entries(e??{}).filter(([,e])=>e).map(([e,t])=>`${t} ${e}`);return(0,u.jsx)(`div`,{className:`hint`,children:t.length?t.join(` · `):`None.`})}function h({agent:e}){let t=Object.entries(e.queries);return(0,u.jsxs)(`div`,{className:`agent-detail`,children:[(0,u.jsx)(`p`,{className:`sub-h`,children:`Writes asked for`}),(0,u.jsx)(m,{writes:e.writes}),(0,u.jsx)(`p`,{className:`sub-h`,style:{marginTop:12},children:`Saved queries`}),t.length?(0,u.jsx)(`div`,{className:`hint`,children:t.map(([e,t])=>`${e} × ${t}`).join(` · `)}):(0,u.jsx)(`div`,{className:`hint`,children:`None - only ad-hoc SQL.`}),(0,u.jsx)(`p`,{className:`sub-h`,style:{marginTop:12},children:`Most frequent ad-hoc statements`}),e.top_statements.length?e.top_statements.map((e,t)=>(0,u.jsxs)(`div`,{className:`agent-statement`,children:[(0,u.jsx)(`div`,{className:`hint`,children:`${e.count} call${e.count===1?``:`s`} · ${f(e.rows)} rows`+(e.errors?` · ${e.errors} failed`:``)}),e.sql?(0,u.jsx)(c,{sql:e.sql}):(0,u.jsx)(`span`,{className:`mono dim`,children:`sha256 ${e.sql_sha256}`})]},t)):(0,u.jsx)(`div`,{className:`hint`,children:e.adhoc_calls?`Run history keeps no SQL text here (QUERYAPIGATE_HISTORY_ADHOC_SQL is none).`:`No ad-hoc SQL.`})]})}function g(){let[e,t]=(0,l.useState)(`24h`),[a,c]=(0,l.useState)(null),m=d.find(t=>t.id===e).hours,g=i({queryKey:[`usage`,`agents`,e],queryFn:async()=>r(await n.GET(`/api/v1/usage/agents`,{params:{query:{hours:m}}})),retry:!1}),_=g.data?.items??[],v;v=g.isPending?(0,u.jsx)(o,{text:`Loading agents…`}):g.isError?(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`Couldn’t load agent usage`}),(0,u.jsx)(`span`,{children:g.error.message})]}):_.length?(0,u.jsx)(`div`,{style:{overflowX:`auto`},children:(0,u.jsxs)(`table`,{className:`grid`,children:[(0,u.jsx)(`thead`,{children:(0,u.jsx)(`tr`,{children:[`Agent`,`Calls`,`Rows`,`Failed`,`Masked`,`Refused or failed with`,`Last seen`].map(e=>(0,u.jsx)(`th`,{children:e},e))})}),(0,u.jsx)(`tbody`,{children:_.map(e=>(0,u.jsxs)(l.Fragment,{children:[(0,u.jsxs)(`tr`,{children:[(0,u.jsxs)(`td`,{children:[(0,u.jsx)(`button`,{type:`button`,className:`agent-name`,"aria-expanded":a===e.agent,onClick:()=>c(a===e.agent?null:e.agent),children:e.agent}),(0,u.jsx)(`div`,{className:`dim`,style:{fontSize:12},children:e.connections.join(`, `)})]}),(0,u.jsx)(`td`,{className:`num`,children:f(e.calls)}),(0,u.jsx)(`td`,{className:`num`,children:f(e.rows)}),(0,u.jsx)(`td`,{className:`num`,children:f(e.errors)}),(0,u.jsx)(`td`,{className:`num`,children:f(e.masked_calls)}),(0,u.jsx)(`td`,{children:(0,u.jsx)(p,{refusals:e.refusals})}),(0,u.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,u.jsx)(s,{value:e.last_seen,fallback:``})})]}),a===e.agent&&(0,u.jsx)(`tr`,{children:(0,u.jsx)(`td`,{colSpan:7,children:(0,u.jsx)(h,{agent:e})})})]},e.agent))})]})}):(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`No agent called the MCP server in this window`}),(0,u.jsx)(`span`,{children:`Runs over MCP - saved-query tools and execute_sql - appear here, per API key or signed-in user.`})]});let y=[];return g.data&&g.data.sample_rate<1&&y.push(`Run history keeps ${Math.round(g.data.sample_rate*100)}% of successful runs, so successful calls and rows are a sample; failures are all here.`),g.data?.truncated&&y.push(`More runs than one summary reads (100,000) - narrow the window.`),(0,u.jsxs)(u.Fragment,{children:[(0,u.jsxs)(`div`,{className:`page-head`,children:[(0,u.jsxs)(`div`,{className:`titles`,children:[(0,u.jsx)(`h1`,{children:`Agents`}),(0,u.jsx)(`span`,{className:`sub`,children:`What each AI agent did over MCP - per API key or signed-in user, from run history.`})]}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>void g.refetch(),children:`Refresh`})]}),(0,u.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,u.jsxs)(`div`,{className:`panel-bar`,children:[(0,u.jsx)(`select`,{"aria-label":`Window`,value:e,onChange:e=>t(e.target.value),children:d.map(e=>(0,u.jsx)(`option`,{value:e.id,children:e.label},e.id))}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`span`,{className:`panel-count`,children:g.data?`${_.length} agent${_.length===1?``:`s`}`:``})]}),y.map(e=>(0,u.jsx)(`div`,{className:`hint`,style:{padding:`6px 12px`},children:e},e)),v]})]})}export{g as AgentsPage};
@@ -1 +1 @@
1
- import{B as e,H as t,z as n}from"./client-ZHNiMG8H.js";import{C as r,D as i,M as a,N as o,S as s,x as c,y as l}from"./index-Cl93xBjn.js";import{t as u}from"./Time-s5E5qK3s.js";import{n as d}from"./state-O13cl7cO.js";var f=t(e(),1),p=n(),m=[{id:`critical`,label:`Critical`},{id:`warning`,label:`Warning`},{id:`info`,label:`For your information`}],h={key:`Open API keys`,query:`Open query`,connection:`Open connections`,settings:`Open settings`,export:`Open exports`,agent:`Open agents`,approvals:`Open approvals`,audit:`Open audit log`};function g(){let e=a();return t=>{t.type===`query`?e(`/queries/${encodeURIComponent(t.name)}`):t.type===`key`?e(`/api-keys`):t.type===`connection`?e(`/connections`):t.type===`export`?e(`/exports`):t.type===`agent`?e(`/agents`):t.type===`approvals`?e(`/approvals`):t.type===`audit`?e(`/audit-log`):(d(t.name),e(`/settings`))}}function _({alert:e,dismissed:t}){let n=g();return(0,p.jsxs)(`div`,{className:`alert-row `+e.severity,"data-alert":e.id,children:[(0,p.jsx)(`span`,{className:`sev `+e.severity,children:e.severity}),(0,p.jsxs)(`div`,{className:`alert-what`,children:[(0,p.jsx)(`strong`,{children:e.title}),(0,p.jsx)(`span`,{className:`alert-detail`,children:e.detail}),e.since&&!e.detail.includes(e.since)&&(0,p.jsxs)(`span`,{className:`alert-since`,children:[e.kind===`key_expiring`?`Expires `:`Since `,(0,p.jsx)(u,{value:e.since})]})]}),(0,p.jsxs)(`div`,{className:`actions`,children:[e.target&&(0,p.jsx)(`button`,{type:`button`,className:`btn sm outlined`,onClick:()=>n(e.target),children:h[e.target.type]??`Open`}),t?(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>s(e.id),children:`Restore`}):(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,title:`Hide it in this browser until it clears`,onClick:()=>c(e.id),children:`Dismiss`})]})]})}function v({alerts:e}){return(0,p.jsx)(p.Fragment,{children:m.map(({id:t,label:n})=>{let r=e.filter(e=>e.severity===t);return r.length?(0,p.jsxs)(`div`,{className:`panel alerts-group`,id:`alerts-`+t,children:[(0,p.jsxs)(`h2`,{children:[n,` `,(0,p.jsx)(`span`,{className:`count`,children:r.length})]}),r.map(e=>(0,p.jsx)(_,{alert:e},e.id))]},t):null})})}function y(){let e=a(),t=o(),n=l.find(e=>e.id===t.tab),{query:s,active:c,dismissed:d}=r(),[m,h]=(0,f.useState)(!1),g=e=>!n||n.kinds.includes(e.kind),y=c.filter(g),b=d.filter(g),x=e=>c.filter(t=>!e||e.includes(t.kind)),S;S=s.isPending?(0,p.jsx)(i,{text:`Checking…`}):s.isError?(0,p.jsx)(`div`,{className:`panel`,children:(0,p.jsxs)(`div`,{className:`empty`,children:[(0,p.jsx)(`strong`,{children:`Couldn’t check for alerts`}),(0,p.jsx)(`span`,{children:s.error.message}),(0,p.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>void s.refetch(),children:`Retry`})]})}):(0,p.jsxs)(p.Fragment,{children:[n&&(0,p.jsx)(`p`,{className:`alerts-about`,children:n.about}),y.length?(0,p.jsx)(v,{alerts:y}):(0,p.jsx)(`div`,{className:`panel`,id:`alerts-clear`,children:(0,p.jsxs)(`div`,{className:`empty`,children:[(0,p.jsx)(`strong`,{children:`All clear`}),(0,p.jsx)(`span`,{children:n?`Nothing to report from this check right now.`:`Nothing needs your attention right now.`})]})}),b.length>0&&(0,p.jsxs)(`div`,{className:`alerts-dismissed`,children:[(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>h(e=>!e),children:`${b.length} dismissed in this browser · ${m?`Hide`:`Show`}`}),m&&(0,p.jsx)(`div`,{className:`panel alerts-group`,id:`alerts-dismissed`,children:b.map(e=>(0,p.jsx)(_,{alert:e,dismissed:!0},e.id))})]})]});let C=(t,r,i)=>(0,p.jsxs)(`button`,{type:`button`,role:`tab`,"aria-selected":(n?.id??`all`)===t,className:`minitab`+((n?.id??`all`)===t?` active`:``),onClick:()=>e(t===`all`?`/alerts`:`/alerts/`+t),children:[r,s.data?(0,p.jsx)(`span`,{className:`n`+(i.length?` on`:``)+(i.some(e=>e.severity===`critical`)?` critical`:``),children:i.length}):null]},t);return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)(`div`,{className:`page-head`,children:[(0,p.jsxs)(`div`,{className:`titles`,children:[(0,p.jsx)(`h1`,{children:`Alerts`}),(0,p.jsxs)(`span`,{className:`sub`,children:[`What needs attention now, checked every minute. Each clears by itself once its cause does.`,s.data?(0,p.jsxs)(p.Fragment,{children:[` Last checked `,(0,p.jsx)(u,{value:s.data.checked_at}),`.`]}):null]})]}),(0,p.jsx)(`span`,{className:`spacer`}),(0,p.jsx)(`button`,{id:`refresh-alerts`,type:`button`,className:`btn`,onClick:()=>void s.refetch(),children:`Check now`})]}),(0,p.jsxs)(`div`,{className:`minitabs alerts-tabs`,role:`tablist`,"aria-label":`Checks`,children:[C(`all`,`All`,x()),l.map(e=>C(e.id,e.label,x(e.kinds)))]}),S]})}export{y as AlertsPage};
1
+ 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-0v-rImZs.js";import{t as u}from"./Time-DurHvFXj.js";import{n as d}from"./state-O13cl7cO.js";var f=t(e(),1),p=n(),m=[{id:`critical`,label:`Critical`},{id:`warning`,label:`Warning`},{id:`info`,label:`For your information`}],h={key:`Open API keys`,query:`Open query`,connection:`Open connections`,settings:`Open settings`,export:`Open exports`,agent:`Open agents`,approvals:`Open approvals`,audit:`Open audit log`};function g(){let e=a();return t=>{t.type===`query`?e(`/queries/${encodeURIComponent(t.name)}`):t.type===`key`?e(`/api-keys`):t.type===`connection`?e(`/connections`):t.type===`export`?e(`/exports`):t.type===`agent`?e(`/agents`):t.type===`approvals`?e(`/approvals`):t.type===`audit`?e(`/audit-log`):(d(t.name),e(`/settings`))}}function _({alert:e,dismissed:t}){let n=g();return(0,p.jsxs)(`div`,{className:`alert-row `+e.severity,"data-alert":e.id,children:[(0,p.jsx)(`span`,{className:`sev `+e.severity,children:e.severity}),(0,p.jsxs)(`div`,{className:`alert-what`,children:[(0,p.jsx)(`strong`,{children:e.title}),(0,p.jsx)(`span`,{className:`alert-detail`,children:e.detail}),e.since&&!e.detail.includes(e.since)&&(0,p.jsxs)(`span`,{className:`alert-since`,children:[e.kind===`key_expiring`?`Expires `:`Since `,(0,p.jsx)(u,{value:e.since})]})]}),(0,p.jsxs)(`div`,{className:`actions`,children:[e.target&&(0,p.jsx)(`button`,{type:`button`,className:`btn sm outlined`,onClick:()=>n(e.target),children:h[e.target.type]??`Open`}),t?(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>s(e.id),children:`Restore`}):(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,title:`Hide it in this browser until it clears`,onClick:()=>c(e.id),children:`Dismiss`})]})]})}function v({alerts:e}){return(0,p.jsx)(p.Fragment,{children:m.map(({id:t,label:n})=>{let r=e.filter(e=>e.severity===t);return r.length?(0,p.jsxs)(`div`,{className:`panel alerts-group`,id:`alerts-`+t,children:[(0,p.jsxs)(`h2`,{children:[n,` `,(0,p.jsx)(`span`,{className:`count`,children:r.length})]}),r.map(e=>(0,p.jsx)(_,{alert:e},e.id))]},t):null})})}function y(){let e=a(),t=o(),n=l.find(e=>e.id===t.tab),{query:s,active:c,dismissed:d}=r(),[m,h]=(0,f.useState)(!1),g=e=>!n||n.kinds.includes(e.kind),y=c.filter(g),b=d.filter(g),x=e=>c.filter(t=>!e||e.includes(t.kind)),S;S=s.isPending?(0,p.jsx)(i,{text:`Checking…`}):s.isError?(0,p.jsx)(`div`,{className:`panel`,children:(0,p.jsxs)(`div`,{className:`empty`,children:[(0,p.jsx)(`strong`,{children:`Couldn’t check for alerts`}),(0,p.jsx)(`span`,{children:s.error.message}),(0,p.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>void s.refetch(),children:`Retry`})]})}):(0,p.jsxs)(p.Fragment,{children:[n&&(0,p.jsx)(`p`,{className:`alerts-about`,children:n.about}),y.length?(0,p.jsx)(v,{alerts:y}):(0,p.jsx)(`div`,{className:`panel`,id:`alerts-clear`,children:(0,p.jsxs)(`div`,{className:`empty`,children:[(0,p.jsx)(`strong`,{children:`All clear`}),(0,p.jsx)(`span`,{children:n?`Nothing to report from this check right now.`:`Nothing needs your attention right now.`})]})}),b.length>0&&(0,p.jsxs)(`div`,{className:`alerts-dismissed`,children:[(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>h(e=>!e),children:`${b.length} dismissed in this browser · ${m?`Hide`:`Show`}`}),m&&(0,p.jsx)(`div`,{className:`panel alerts-group`,id:`alerts-dismissed`,children:b.map(e=>(0,p.jsx)(_,{alert:e,dismissed:!0},e.id))})]})]});let C=(t,r,i)=>(0,p.jsxs)(`button`,{type:`button`,role:`tab`,"aria-selected":(n?.id??`all`)===t,className:`minitab`+((n?.id??`all`)===t?` active`:``),onClick:()=>e(t===`all`?`/alerts`:`/alerts/`+t),children:[r,s.data?(0,p.jsx)(`span`,{className:`n`+(i.length?` on`:``)+(i.some(e=>e.severity===`critical`)?` critical`:``),children:i.length}):null]},t);return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)(`div`,{className:`page-head`,children:[(0,p.jsxs)(`div`,{className:`titles`,children:[(0,p.jsx)(`h1`,{children:`Alerts`}),(0,p.jsxs)(`span`,{className:`sub`,children:[`What needs attention now, checked every minute. Each clears by itself once its cause does.`,s.data?(0,p.jsxs)(p.Fragment,{children:[` Last checked `,(0,p.jsx)(u,{value:s.data.checked_at}),`.`]}):null]})]}),(0,p.jsx)(`span`,{className:`spacer`}),(0,p.jsx)(`button`,{id:`refresh-alerts`,type:`button`,className:`btn`,onClick:()=>void s.refetch(),children:`Check now`})]}),(0,p.jsxs)(`div`,{className:`minitabs alerts-tabs`,role:`tablist`,"aria-label":`Checks`,children:[C(`all`,`All`,x()),l.map(e=>C(e.id,e.label,x(e.kinds)))]}),S]})}export{y as AlertsPage};
@@ -1 +1 @@
1
- import{n as e,s as t,v as n,z as r}from"./client-ZHNiMG8H.js";import{A as i,D as a,N as o,c as s,d as c,k as l,s as u}from"./index-Cl93xBjn.js";import{t as d}from"./Time-s5E5qK3s.js";import{t as f}from"./QueryInfo-Bv-0Zmc5.js";import{a as p}from"./reach-BAOLd75Y.js";import{i as m,t as h}from"./KeyActivity-D673KcWq.js";var g=r(),_={query:`named query`,collection:`collection`,connection:`connection`};function v({term:e,children:t}){return(0,g.jsxs)(`div`,{children:[(0,g.jsx)(`dt`,{children:e}),(0,g.jsx)(`dd`,{children:t})]})}function y({title:e,sub:t,children:n}){return(0,g.jsxs)(`section`,{className:`panel`,style:{marginTop:16},children:[(0,g.jsxs)(`div`,{className:`panel-bar`,children:[(0,g.jsx)(`strong`,{children:e}),t?(0,g.jsx)(`span`,{className:`dim`,children:t}):null]}),n]})}function b(){let{name:r=``}=o(),b=s(),x=u(),S=c(),C=m(r),{openDrawer:w}=l(),T=new Date().toISOString().slice(0,10),E=n({queryKey:[`apikey-history`,r],queryFn:async()=>t(await e.GET(`/api/v1/history`,{params:{query:{key:r,limit:50}}})).items,retry:!1});if(b.isPending)return(0,g.jsx)(a,{text:`Loading API key…`});let D=b.data?.[r];if(!D)return(0,g.jsxs)(g.Fragment,{children:[(0,g.jsx)(`div`,{className:`page-head`,children:(0,g.jsxs)(`div`,{className:`titles`,children:[(0,g.jsx)(i,{to:`/api-keys`,className:`sub`,children:`← API keys`}),(0,g.jsx)(`h1`,{children:r||`API key`})]})}),(0,g.jsx)(`div`,{className:`panel`,children:(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`No such API key`}),(0,g.jsx)(`span`,{children:`It may have been revoked and removed, or the name is wrong.`}),(0,g.jsx)(i,{to:`/api-keys`,className:`btn sm`,children:`Back to API keys`})]})})]});let O=!!(D.expires_at&&D.expires_at<T),k=D.active?O?`Expired`:`Active`:`Revoked`,A=(x.data??[]).map(e=>({q:e,via:p(D,e.name,e.collection,e.connection_name)})).filter(e=>e.via.length),j=(S.data??[]).map(e=>e.name),M=D.connections===`*`?j:D.connections??[],N=A.map(e=>e.q.connection_name).filter(e=>e!=null&&!M.includes(e)),P=[...new Set([...M,...N])].sort(),F=e=>(S.data??[]).find(t=>t.name===e)?.db??null,I=e=>w({title:e.name,kicker:`${e.collection||`uncollected`} · ${e.connection_name||`no connection`}`,content:(0,g.jsx)(f,{name:e.name,dbType:e.connection_name?F(e.connection_name):null})}),L=D.allowed_tables??[],R=D.allowed_ips??[],z=D.usage_this_period;return(0,g.jsxs)(g.Fragment,{children:[(0,g.jsxs)(`div`,{className:`page-head`,children:[(0,g.jsxs)(`div`,{className:`titles`,children:[(0,g.jsx)(i,{to:`/api-keys`,className:`sub`,children:`← API keys`}),(0,g.jsxs)(`h1`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,g.jsx)(`span`,{className:`dot ${D.active&&!O?`ok`:`off`}`,title:k}),r,D.example?(0,g.jsx)(`span`,{className:`tag`,children:`example`}):null]}),(0,g.jsxs)(`span`,{className:`sub`,children:[k,D.created_from_role?` · created from role ${D.created_from_role}`:``]})]}),(0,g.jsx)(`span`,{className:`spacer`}),(0,g.jsx)(i,{to:`/api-keys`,className:`btn sm`,children:`Back`})]}),(0,g.jsxs)(`dl`,{className:`meta`,children:[(0,g.jsx)(v,{term:`status`,children:k}),(0,g.jsx)(v,{term:`access`,children:D.allow_writes?`Read / write`:`Read-only`}),(0,g.jsx)(v,{term:`created`,children:(0,g.jsx)(d,{value:D.created_at})}),(0,g.jsx)(v,{term:`last used`,children:(0,g.jsx)(d,{value:D.last_used_at,fallback:`never`})}),(0,g.jsx)(v,{term:`expires`,children:D.expires_at?D.expires_at+(O?` (expired)`:``):`never`}),(0,g.jsx)(v,{term:`rate limit`,children:D.rate_limit||`server default`}),(0,g.jsx)(v,{term:`query timeout`,children:D.effective_limits?.query_timeout==null?`server default`:`${D.effective_limits.query_timeout}s`}),(0,g.jsx)(v,{term:`row limit (page)`,children:D.effective_limits?.max_rows?.page??`—`}),(0,g.jsx)(v,{term:`max concurrent`,children:D.effective_limits?.max_concurrent??`unlimited`}),(0,g.jsx)(v,{term:`allowed IPs`,children:R.length?R.join(`, `):`any`})]}),(0,g.jsx)(y,{title:`APIs it can reach`,sub:D.queries===`*`?`all saved queries`:`${A.length} saved ${A.length===1?`query`:`queries`}`,children:D.queries===`*`?(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`Every saved query`}),(0,g.jsx)(`span`,{children:`This key carries a wildcard query grant, so it can run any saved query.`})]}):A.length?(0,g.jsx)(`div`,{style:{overflowX:`auto`},children:(0,g.jsxs)(`table`,{className:`grid`,children:[(0,g.jsx)(`thead`,{children:(0,g.jsx)(`tr`,{children:[`API endpoint`,`Query`,`Connection`,`Collection`,`Granted via`,`Activity (30d)`].map(e=>(0,g.jsx)(`th`,{children:e},e))})}),(0,g.jsx)(`tbody`,{children:A.map(({q:e,via:t})=>(0,g.jsxs)(`tr`,{"data-query":e.name,children:[(0,g.jsx)(`td`,{className:`mono`,children:e.published_version?e.endpoint:(0,g.jsxs)(`span`,{className:`dim`,title:`No published version yet`,children:[e.endpoint,` (draft)`]})}),(0,g.jsx)(`td`,{children:(0,g.jsx)(`button`,{type:`button`,className:`as-link name`,title:`Show the SQL for ${e.name}`,onClick:()=>I(e),children:e.name})}),(0,g.jsx)(`td`,{className:`dim`,children:e.connection_name||`—`}),(0,g.jsx)(`td`,{className:`dim`,children:e.collection||`—`}),(0,g.jsx)(`td`,{children:t.map((e,t)=>(0,g.jsx)(`span`,{className:`tag`,style:{marginRight:4},children:e.kind===`query`?_.query:e.label},t))}),(0,g.jsx)(`td`,{className:`spark-cell`,children:(0,g.jsx)(h,{label:e.name,items:C.by[e.name],isPending:C.isPending,isError:C.isError})})]},e.name))})]})}):(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`No saved queries`}),(0,g.jsx)(`span`,{children:`This key reaches no saved query by name or collection. It may still run ad-hoc SQL on the connections below.`})]})}),(0,g.jsx)(y,{title:`Databases it can reach`,sub:D.connections===`*`?`all connections`:`${P.length} connection(s)`,children:P.length?(0,g.jsx)(`div`,{style:{overflowX:`auto`},children:(0,g.jsxs)(`table`,{className:`grid`,children:[(0,g.jsx)(`thead`,{children:(0,g.jsx)(`tr`,{children:[`Connection`,`Type`,`Reached by`].map(e=>(0,g.jsx)(`th`,{children:e},e))})}),(0,g.jsx)(`tbody`,{children:P.map(e=>(0,g.jsxs)(`tr`,{"data-conn":e,children:[(0,g.jsx)(`td`,{className:`name`,children:e}),(0,g.jsx)(`td`,{children:F(e)?(0,g.jsx)(`span`,{className:`tag`,children:F(e)}):(0,g.jsx)(`span`,{className:`dim`,children:`—`})}),(0,g.jsx)(`td`,{className:`dim`,children:D.connections===`*`?`all connections`:M.includes(e)?`direct grant (ad-hoc SQL)`:`saved query only`})]},e))})]})}):(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`No connections`}),(0,g.jsx)(`span`,{children:`This key cannot reach any connection directly or through a saved query.`})]})}),(0,g.jsxs)(y,{title:`Tables it can reach`,sub:L.length?`${L.length} allowed`:`all tables on reachable connections`,children:[L.length?(0,g.jsx)(`div`,{style:{padding:`12px 14px`,display:`flex`,flexWrap:`wrap`,gap:6},children:L.map(e=>(0,g.jsx)(`span`,{className:`tag mono`,children:e},e))}):(0,g.jsx)(`div`,{style:{padding:`12px 14px`},className:`dim`,children:`No table allow-list — this key may read every table on the connections it reaches.`}),D.allow_writes?(0,g.jsx)(`div`,{style:{padding:`0 14px 12px`},children:(0,g.jsxs)(`span`,{className:`tag`,children:[`writes: `,D.allowed_write_ops?.length?D.allowed_write_ops.join(`, `):`all operations`]})}):null,D.mask?.length?(0,g.jsx)(`div`,{style:{padding:`0 14px 12px`,display:`flex`,flexWrap:`wrap`,gap:6},children:D.mask.map((e,t)=>(0,g.jsxs)(`span`,{className:`tag`,title:`${e.column}: ${e.action}`,children:[`masked `,e.column]},t))}):null]}),(0,g.jsx)(y,{title:`Usage history`,sub:z?`${D.usage.queries} queries total · ${D.usage.rows.toLocaleString()} rows`:`${D.usage.queries} queries · ${D.usage.errors} failed · since this process started`,children:E.isPending?(0,g.jsx)(a,{text:`Loading usage…`}):E.isError?(0,g.jsx)(`div`,{style:{padding:`12px 14px`},className:`dim`,children:`Usage history isn’t available for this key (the run log may be off, or your role can’t read it).`}):E.data.length?(0,g.jsx)(`div`,{style:{overflowX:`auto`},children:(0,g.jsxs)(`table`,{className:`grid`,children:[(0,g.jsx)(`thead`,{children:(0,g.jsx)(`tr`,{children:[`When`,`What`,`Connection`,`Via`,`Rows`,`Duration`,`Result`].map(e=>(0,g.jsx)(`th`,{children:e},e))})}),(0,g.jsx)(`tbody`,{children:E.data.map((e,t)=>(0,g.jsxs)(`tr`,{children:[(0,g.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:(0,g.jsx)(d,{value:e.executed_at})}),(0,g.jsx)(`td`,{className:`name`,children:e.kind===`adhoc`?(0,g.jsx)(`span`,{className:`dim`,children:`ad-hoc SQL`}):e.query}),(0,g.jsx)(`td`,{className:`dim`,children:e.connection_name||`—`}),(0,g.jsx)(`td`,{className:`dim`,children:e.transport||`—`}),(0,g.jsx)(`td`,{className:`mono`,children:e.rows??`—`}),(0,g.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:e.duration_ms==null?`—`:`${e.duration_ms} ms`}),(0,g.jsx)(`td`,{style:e.status===`error`?{color:`var(--danger)`}:void 0,children:e.status===`error`?(0,g.jsx)(`span`,{title:e.error??void 0,children:e.code||`error`}):`ok`})]},e.request_id??t))})]})}):(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`No runs recorded`}),(0,g.jsx)(`span`,{children:`This key hasn’t run a query yet, or its runs have aged out of the log.`})]})})]})}export{b as ApiKeyDetailPage};
1
+ 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-0v-rImZs.js";import{t as d}from"./Time-DurHvFXj.js";import{t as f}from"./QueryInfo-n4U1MkL1.js";import{a as p}from"./reach-BAOLd75Y.js";import{i as m,t as h}from"./KeyActivity-DuTyhSGT.js";var g=r(),_={query:`named query`,collection:`collection`,connection:`connection`};function v({term:e,children:t}){return(0,g.jsxs)(`div`,{children:[(0,g.jsx)(`dt`,{children:e}),(0,g.jsx)(`dd`,{children:t})]})}function y({title:e,sub:t,children:n}){return(0,g.jsxs)(`section`,{className:`panel`,style:{marginTop:16},children:[(0,g.jsxs)(`div`,{className:`panel-bar`,children:[(0,g.jsx)(`strong`,{children:e}),t?(0,g.jsx)(`span`,{className:`dim`,children:t}):null]}),n]})}function b(){let{name:r=``}=o(),b=s(),x=u(),S=c(),C=m(r),{openDrawer:w}=l(),T=new Date().toISOString().slice(0,10),E=n({queryKey:[`apikey-history`,r],queryFn:async()=>t(await e.GET(`/api/v1/history`,{params:{query:{key:r,limit:50}}})).items,retry:!1});if(b.isPending)return(0,g.jsx)(a,{text:`Loading API key…`});let D=b.data?.[r];if(!D)return(0,g.jsxs)(g.Fragment,{children:[(0,g.jsx)(`div`,{className:`page-head`,children:(0,g.jsxs)(`div`,{className:`titles`,children:[(0,g.jsx)(i,{to:`/api-keys`,className:`sub`,children:`← API keys`}),(0,g.jsx)(`h1`,{children:r||`API key`})]})}),(0,g.jsx)(`div`,{className:`panel`,children:(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`No such API key`}),(0,g.jsx)(`span`,{children:`It may have been revoked and removed, or the name is wrong.`}),(0,g.jsx)(i,{to:`/api-keys`,className:`btn sm`,children:`Back to API keys`})]})})]});let O=!!(D.expires_at&&D.expires_at<T),k=D.active?O?`Expired`:`Active`:`Revoked`,A=(x.data??[]).map(e=>({q:e,via:p(D,e.name,e.collection,e.connection_name)})).filter(e=>e.via.length),j=(S.data??[]).map(e=>e.name),M=D.connections===`*`?j:D.connections??[],N=A.map(e=>e.q.connection_name).filter(e=>e!=null&&!M.includes(e)),P=[...new Set([...M,...N])].sort(),F=e=>(S.data??[]).find(t=>t.name===e)?.db??null,I=e=>w({title:e.name,kicker:`${e.collection||`uncollected`} · ${e.connection_name||`no connection`}`,content:(0,g.jsx)(f,{name:e.name,dbType:e.connection_name?F(e.connection_name):null})}),L=D.allowed_tables??[],R=D.allowed_ips??[],z=D.usage_this_period;return(0,g.jsxs)(g.Fragment,{children:[(0,g.jsxs)(`div`,{className:`page-head`,children:[(0,g.jsxs)(`div`,{className:`titles`,children:[(0,g.jsx)(i,{to:`/api-keys`,className:`sub`,children:`← API keys`}),(0,g.jsxs)(`h1`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,g.jsx)(`span`,{className:`dot ${D.active&&!O?`ok`:`off`}`,title:k}),r,D.example?(0,g.jsx)(`span`,{className:`tag`,children:`example`}):null]}),(0,g.jsxs)(`span`,{className:`sub`,children:[k,D.created_from_role?` · created from role ${D.created_from_role}`:``]})]}),(0,g.jsx)(`span`,{className:`spacer`}),(0,g.jsx)(i,{to:`/api-keys`,className:`btn sm`,children:`Back`})]}),(0,g.jsxs)(`dl`,{className:`meta`,children:[(0,g.jsx)(v,{term:`status`,children:k}),(0,g.jsx)(v,{term:`access`,children:D.allow_writes?`Read / write`:`Read-only`}),(0,g.jsx)(v,{term:`created`,children:(0,g.jsx)(d,{value:D.created_at})}),(0,g.jsx)(v,{term:`last used`,children:(0,g.jsx)(d,{value:D.last_used_at,fallback:`never`})}),(0,g.jsx)(v,{term:`expires`,children:D.expires_at?D.expires_at+(O?` (expired)`:``):`never`}),(0,g.jsx)(v,{term:`rate limit`,children:D.rate_limit||`server default`}),(0,g.jsx)(v,{term:`query timeout`,children:D.effective_limits?.query_timeout==null?`server default`:`${D.effective_limits.query_timeout}s`}),(0,g.jsx)(v,{term:`row limit (page)`,children:D.effective_limits?.max_rows?.page??`—`}),(0,g.jsx)(v,{term:`max concurrent`,children:D.effective_limits?.max_concurrent??`unlimited`}),(0,g.jsx)(v,{term:`allowed IPs`,children:R.length?R.join(`, `):`any`})]}),(0,g.jsx)(y,{title:`APIs it can reach`,sub:D.queries===`*`?`all saved queries`:`${A.length} saved ${A.length===1?`query`:`queries`}`,children:D.queries===`*`?(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`Every saved query`}),(0,g.jsx)(`span`,{children:`This key carries a wildcard query grant, so it can run any saved query.`})]}):A.length?(0,g.jsx)(`div`,{style:{overflowX:`auto`},children:(0,g.jsxs)(`table`,{className:`grid`,children:[(0,g.jsx)(`thead`,{children:(0,g.jsx)(`tr`,{children:[`API endpoint`,`Query`,`Connection`,`Collection`,`Granted via`,`Activity (30d)`].map(e=>(0,g.jsx)(`th`,{children:e},e))})}),(0,g.jsx)(`tbody`,{children:A.map(({q:e,via:t})=>(0,g.jsxs)(`tr`,{"data-query":e.name,children:[(0,g.jsx)(`td`,{className:`mono`,children:e.published_version?e.endpoint:(0,g.jsxs)(`span`,{className:`dim`,title:`No published version yet`,children:[e.endpoint,` (draft)`]})}),(0,g.jsx)(`td`,{children:(0,g.jsx)(`button`,{type:`button`,className:`as-link name`,title:`Show the SQL for ${e.name}`,onClick:()=>I(e),children:e.name})}),(0,g.jsx)(`td`,{className:`dim`,children:e.connection_name||`—`}),(0,g.jsx)(`td`,{className:`dim`,children:e.collection||`—`}),(0,g.jsx)(`td`,{children:t.map((e,t)=>(0,g.jsx)(`span`,{className:`tag`,style:{marginRight:4},children:e.kind===`query`?_.query:e.label},t))}),(0,g.jsx)(`td`,{className:`spark-cell`,children:(0,g.jsx)(h,{label:e.name,items:C.by[e.name],isPending:C.isPending,isError:C.isError})})]},e.name))})]})}):(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`No saved queries`}),(0,g.jsx)(`span`,{children:`This key reaches no saved query by name or collection. It may still run ad-hoc SQL on the connections below.`})]})}),(0,g.jsx)(y,{title:`Databases it can reach`,sub:D.connections===`*`?`all connections`:`${P.length} connection(s)`,children:P.length?(0,g.jsx)(`div`,{style:{overflowX:`auto`},children:(0,g.jsxs)(`table`,{className:`grid`,children:[(0,g.jsx)(`thead`,{children:(0,g.jsx)(`tr`,{children:[`Connection`,`Type`,`Reached by`].map(e=>(0,g.jsx)(`th`,{children:e},e))})}),(0,g.jsx)(`tbody`,{children:P.map(e=>(0,g.jsxs)(`tr`,{"data-conn":e,children:[(0,g.jsx)(`td`,{className:`name`,children:e}),(0,g.jsx)(`td`,{children:F(e)?(0,g.jsx)(`span`,{className:`tag`,children:F(e)}):(0,g.jsx)(`span`,{className:`dim`,children:`—`})}),(0,g.jsx)(`td`,{className:`dim`,children:D.connections===`*`?`all connections`:M.includes(e)?`direct grant (ad-hoc SQL)`:`saved query only`})]},e))})]})}):(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`No connections`}),(0,g.jsx)(`span`,{children:`This key cannot reach any connection directly or through a saved query.`})]})}),(0,g.jsxs)(y,{title:`Tables it can reach`,sub:L.length?`${L.length} allowed`:`all tables on reachable connections`,children:[L.length?(0,g.jsx)(`div`,{style:{padding:`12px 14px`,display:`flex`,flexWrap:`wrap`,gap:6},children:L.map(e=>(0,g.jsx)(`span`,{className:`tag mono`,children:e},e))}):(0,g.jsx)(`div`,{style:{padding:`12px 14px`},className:`dim`,children:`No table allow-list — this key may read every table on the connections it reaches.`}),D.allow_writes?(0,g.jsx)(`div`,{style:{padding:`0 14px 12px`},children:(0,g.jsxs)(`span`,{className:`tag`,children:[`writes: `,D.allowed_write_ops?.length?D.allowed_write_ops.join(`, `):`all operations`]})}):null,D.mask?.length?(0,g.jsx)(`div`,{style:{padding:`0 14px 12px`,display:`flex`,flexWrap:`wrap`,gap:6},children:D.mask.map((e,t)=>(0,g.jsxs)(`span`,{className:`tag`,title:`${e.column}: ${e.action}`,children:[`masked `,e.column]},t))}):null]}),(0,g.jsx)(y,{title:`Usage history`,sub:z?`${D.usage.queries} queries total · ${D.usage.rows.toLocaleString()} rows`:`${D.usage.queries} queries · ${D.usage.errors} failed · since this process started`,children:E.isPending?(0,g.jsx)(a,{text:`Loading usage…`}):E.isError?(0,g.jsx)(`div`,{style:{padding:`12px 14px`},className:`dim`,children:`Usage history isn’t available for this key (the run log may be off, or your role can’t read it).`}):E.data.length?(0,g.jsx)(`div`,{style:{overflowX:`auto`},children:(0,g.jsxs)(`table`,{className:`grid`,children:[(0,g.jsx)(`thead`,{children:(0,g.jsx)(`tr`,{children:[`When`,`What`,`Connection`,`Via`,`Rows`,`Duration`,`Result`].map(e=>(0,g.jsx)(`th`,{children:e},e))})}),(0,g.jsx)(`tbody`,{children:E.data.map((e,t)=>(0,g.jsxs)(`tr`,{children:[(0,g.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:(0,g.jsx)(d,{value:e.executed_at})}),(0,g.jsx)(`td`,{className:`name`,children:e.kind===`adhoc`?(0,g.jsx)(`span`,{className:`dim`,children:`ad-hoc SQL`}):e.query}),(0,g.jsx)(`td`,{className:`dim`,children:e.connection_name||`—`}),(0,g.jsx)(`td`,{className:`dim`,children:e.transport||`—`}),(0,g.jsx)(`td`,{className:`mono`,children:e.rows??`—`}),(0,g.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:e.duration_ms==null?`—`:`${e.duration_ms} ms`}),(0,g.jsx)(`td`,{style:e.status===`error`?{color:`var(--danger)`}:void 0,children:e.status===`error`?(0,g.jsx)(`span`,{title:e.error??void 0,children:e.code||`error`}):`ok`})]},e.request_id??t))})]})}):(0,g.jsxs)(`div`,{className:`empty`,children:[(0,g.jsx)(`strong`,{children:`No runs recorded`}),(0,g.jsx)(`span`,{children:`This key hasn’t run a query yet, or its runs have aged out of the log.`})]})})]})}export{b as ApiKeyDetailPage};
@@ -1 +1 @@
1
- import{z as e}from"./client-ZHNiMG8H.js";import{A as t,D as n,c as r,l as i,u as a}from"./index-Cl93xBjn.js";import{t as o}from"./Time-s5E5qK3s.js";import{t as s}from"./reach-BAOLd75Y.js";import{r as c,t as l}from"./KeyActivity-D673KcWq.js";import{i as u,n as d,o as f}from"./forms-CTAVu1q5.js";import{t as p}from"./icons-CFXE8hAz.js";import{t as m}from"./Managed-VPfeN8Yu.js";var h=e();function g(e,t){let n=new Date(Date.now()+6048e5).toISOString().slice(0,10);return!!(e&&e>=t&&e<=n)}function _({usage:e}){let t=[`calls`,`rows`].map(t=>({what:t,used:e[t],limit:e[`${t}_limit`]})).filter(e=>e.limit!=null),n=t.reduce((e,t)=>t.used/t.limit>e.used/e.limit?t:e,t[0]),r=Math.min(1,n.used/n.limit),i=e.per===`day`?`today`:`this month`;return(0,h.jsxs)(`div`,{className:`quota-usage`,title:t.map(e=>`${e.used.toLocaleString()} of ${e.limit.toLocaleString()} ${e.what}`).join(` · `)+` ${i} · resets ${e.resets_at}`,children:[(0,h.jsxs)(`span`,{className:`dim`,children:[n.used.toLocaleString(),` / `,n.limit.toLocaleString(),` `,n.what,` `,i]}),(0,h.jsx)(`div`,{className:`quota-bar${r>=1?` full`:r>=.8?` near`:``}`,children:(0,h.jsx)(`span`,{style:{width:`${Math.round(r*100)}%`}})})]})}function v(e){if(!e.queries)return null;let t=[e.queries+(e.queries===1?` query`:` queries`)];return e.errors&&t.push(e.errors+` failed`),t.join(` · `)}function y(){let e=i(),y=r(),b=a(),x=c(),S=d(),C=u(),w=Object.keys(y.data??{}).sort(),T=new Date().toISOString().slice(0,10),E;return E=y.isPending?(0,h.jsx)(n,{text:`Loading API keys…`}):y.isError?(0,h.jsxs)(`div`,{className:`empty`,children:[(0,h.jsx)(`strong`,{children:`Couldn’t load API keys`}),(0,h.jsx)(`span`,{children:y.error.message}),(0,h.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>y.refetch(),children:`Retry`})]}):w.length?(0,h.jsx)(`div`,{style:{overflowX:`auto`},children:(0,h.jsxs)(`table`,{className:`grid`,children:[(0,h.jsx)(`thead`,{children:(0,h.jsx)(`tr`,{children:[`Name`,`Scope`,`Access`,`Rate limit`,`IPs`,`Expires`,`Last used`,`Usage`,`Activity (30d)`,``].map((e,t)=>(0,h.jsx)(`th`,{children:e},t))})}),(0,h.jsx)(`tbody`,{children:w.map(n=>{let r=y.data[n],i=!!(r.expires_at&&r.expires_at<T),a=!i&&g(r.expires_at,T),c=r.allowed_ips??[],u=v(r.usage);return(0,h.jsxs)(`tr`,{"data-name":n,children:[(0,h.jsx)(`td`,{children:(0,h.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,h.jsx)(`span`,{className:`dot ${r.active&&!i?`ok`:`off`}`,title:r.active?i?`Expired`:`Active`:`Revoked`}),(0,h.jsx)(`span`,{className:`name`,title:r.created_at?`Created `+r.created_at+(r.created_from_role?` from role `+r.created_from_role:``):void 0,children:n}),(0,h.jsx)(m,{kind:`api_keys`,name:n}),r.previous_expires_at?(0,h.jsx)(`span`,{className:`tag`,title:`Rotated: the old secret works until then too`,children:`rotating`}):null,r.mask?.length?(0,h.jsx)(`span`,{className:`tag`,title:r.mask.map(e=>`${e.column}: ${e.action}`).join(`, `),children:`masked`}):null]})}),(0,h.jsx)(`td`,{children:(0,h.jsx)(f,{entry:r,collections:b.data?.collections})}),(0,h.jsx)(s,{entry:r}),(0,h.jsx)(`td`,{className:r.rate_limit?`mono`:`mono dim`,style:{whiteSpace:`nowrap`},children:r.rate_limit||`server default`}),(0,h.jsx)(`td`,{className:`dim`,children:c.length?(0,h.jsx)(`span`,{title:c.join(`, `),children:c.length+(c.length===1?` IP`:` IPs`)}):(0,h.jsx)(`span`,{className:`dim`,children:`any`})}),(0,h.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:r.expires_at?(0,h.jsx)(`span`,{style:i?{color:`var(--danger)`}:a?{color:`var(--warn)`}:void 0,children:r.expires_at+(i?` (expired)`:a?` (expires soon)`:``)}):(0,h.jsx)(`span`,{className:`dim`,children:`never`})}),(0,h.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,h.jsx)(o,{value:r.last_used_at,fallback:`never`})}),r.usage_this_period?(0,h.jsxs)(`td`,{style:{whiteSpace:`nowrap`},children:[u,(0,h.jsx)(_,{usage:r.usage_this_period})]}):u?(0,h.jsx)(`td`,{title:`${r.usage.rows}${r.usage.rows===1?` row`:` rows`} returned in total · since this process started`,style:{whiteSpace:`nowrap`},children:u}):(0,h.jsx)(`td`,{className:`dim`,style:{whiteSpace:`nowrap`},children:`No activity yet`}),(0,h.jsx)(`td`,{className:`spark-cell`,children:(0,h.jsx)(l,{label:n,items:x.by[n],isPending:x.isPending,isError:x.isError,to:`/api-keys/${encodeURIComponent(n)}`})}),(0,h.jsx)(`td`,{children:(0,h.jsxs)(`div`,{className:`actions`,children:[(0,h.jsx)(t,{to:`/api-keys/${encodeURIComponent(n)}`,className:`btn ghost sm`,title:`Full details of ${n}`,children:`Details`}),(0,h.jsx)(`button`,{hidden:!e(`access.write`),type:`button`,className:`btn ghost sm icon`,title:`Edit ${n}`,"aria-label":`Edit`,onClick:()=>S.editKey(n),children:(0,h.jsx)(p,{})}),(0,h.jsx)(`button`,{hidden:!e(`access.write`),type:`button`,className:`btn ghost sm danger`,onClick:()=>void C(n),children:`Revoke`})]})})]},n)})})]})}):(0,h.jsxs)(`div`,{className:`empty`,children:[(0,h.jsx)(`strong`,{children:`No scoped API keys yet`}),(0,h.jsx)(`span`,{children:`QUERYAPIGATE_API_KEY is a full-access admin key. Create a scoped one to limit a caller to specific connections, read-only.`}),(0,h.jsx)(`button`,{hidden:!e(`access.write`),type:`button`,className:`btn primary`,onClick:()=>S.newKey(),children:`New API key`})]}),(0,h.jsxs)(h.Fragment,{children:[(0,h.jsxs)(`div`,{className:`page-head`,children:[(0,h.jsxs)(`div`,{className:`titles`,children:[(0,h.jsx)(`h1`,{children:`API keys`}),(0,h.jsx)(`span`,{className:`sub`,id:`apikeys-sub`,children:(w.length?w.length+(w.length===1?` key. `:` keys. `):``)+`Scope each one to connections, collections or roles.`})]}),(0,h.jsx)(`span`,{className:`spacer`}),(0,h.jsx)(`button`,{hidden:!e(`access.write`),id:`new-apikey`,type:`button`,className:`btn primary`,onClick:()=>S.newKey(),children:`New API key`})]}),(0,h.jsx)(`div`,{id:`apikeys-table`,className:`panel`,children:E})]})}export{y as ApiKeysPage};
1
+ 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-0v-rImZs.js";import{t as o}from"./Time-DurHvFXj.js";import{t as s}from"./reach-BAOLd75Y.js";import{r as c,t as l}from"./KeyActivity-DuTyhSGT.js";import{i as u,n as d,o as f}from"./forms-DdFqq646.js";import{t as p}from"./icons-CFXE8hAz.js";import{t as m}from"./Managed-CQ0VXNNZ.js";var h=e();function g(e,t){let n=new Date(Date.now()+6048e5).toISOString().slice(0,10);return!!(e&&e>=t&&e<=n)}function _({usage:e}){let t=[`calls`,`rows`].map(t=>({what:t,used:e[t],limit:e[`${t}_limit`]})).filter(e=>e.limit!=null),n=t.reduce((e,t)=>t.used/t.limit>e.used/e.limit?t:e,t[0]),r=Math.min(1,n.used/n.limit),i=e.per===`day`?`today`:`this month`;return(0,h.jsxs)(`div`,{className:`quota-usage`,title:t.map(e=>`${e.used.toLocaleString()} of ${e.limit.toLocaleString()} ${e.what}`).join(` · `)+` ${i} · resets ${e.resets_at}`,children:[(0,h.jsxs)(`span`,{className:`dim`,children:[n.used.toLocaleString(),` / `,n.limit.toLocaleString(),` `,n.what,` `,i]}),(0,h.jsx)(`div`,{className:`quota-bar${r>=1?` full`:r>=.8?` near`:``}`,children:(0,h.jsx)(`span`,{style:{width:`${Math.round(r*100)}%`}})})]})}function v(e){if(!e.queries)return null;let t=[e.queries+(e.queries===1?` query`:` queries`)];return e.errors&&t.push(e.errors+` failed`),t.join(` · `)}function y(){let e=i(),y=r(),b=a(),x=c(),S=d(),C=u(),w=Object.keys(y.data??{}).sort(),T=new Date().toISOString().slice(0,10),E;return E=y.isPending?(0,h.jsx)(n,{text:`Loading API keys…`}):y.isError?(0,h.jsxs)(`div`,{className:`empty`,children:[(0,h.jsx)(`strong`,{children:`Couldn’t load API keys`}),(0,h.jsx)(`span`,{children:y.error.message}),(0,h.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>y.refetch(),children:`Retry`})]}):w.length?(0,h.jsx)(`div`,{style:{overflowX:`auto`},children:(0,h.jsxs)(`table`,{className:`grid`,children:[(0,h.jsx)(`thead`,{children:(0,h.jsx)(`tr`,{children:[`Name`,`Scope`,`Access`,`Rate limit`,`IPs`,`Expires`,`Last used`,`Usage`,`Activity (30d)`,``].map((e,t)=>(0,h.jsx)(`th`,{children:e},t))})}),(0,h.jsx)(`tbody`,{children:w.map(n=>{let r=y.data[n],i=!!(r.expires_at&&r.expires_at<T),a=!i&&g(r.expires_at,T),c=r.allowed_ips??[],u=v(r.usage);return(0,h.jsxs)(`tr`,{"data-name":n,children:[(0,h.jsx)(`td`,{children:(0,h.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,h.jsx)(`span`,{className:`dot ${r.active&&!i?`ok`:`off`}`,title:r.active?i?`Expired`:`Active`:`Revoked`}),(0,h.jsx)(`span`,{className:`name`,title:r.created_at?`Created `+r.created_at+(r.created_from_role?` from role `+r.created_from_role:``):void 0,children:n}),(0,h.jsx)(m,{kind:`api_keys`,name:n}),r.previous_expires_at?(0,h.jsx)(`span`,{className:`tag`,title:`Rotated: the old secret works until then too`,children:`rotating`}):null,r.mask?.length?(0,h.jsx)(`span`,{className:`tag`,title:r.mask.map(e=>`${e.column}: ${e.action}`).join(`, `),children:`masked`}):null]})}),(0,h.jsx)(`td`,{children:(0,h.jsx)(f,{entry:r,collections:b.data?.collections})}),(0,h.jsx)(s,{entry:r}),(0,h.jsx)(`td`,{className:r.rate_limit?`mono`:`mono dim`,style:{whiteSpace:`nowrap`},children:r.rate_limit||`server default`}),(0,h.jsx)(`td`,{className:`dim`,children:c.length?(0,h.jsx)(`span`,{title:c.join(`, `),children:c.length+(c.length===1?` IP`:` IPs`)}):(0,h.jsx)(`span`,{className:`dim`,children:`any`})}),(0,h.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:r.expires_at?(0,h.jsx)(`span`,{style:i?{color:`var(--danger)`}:a?{color:`var(--warn)`}:void 0,children:r.expires_at+(i?` (expired)`:a?` (expires soon)`:``)}):(0,h.jsx)(`span`,{className:`dim`,children:`never`})}),(0,h.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,h.jsx)(o,{value:r.last_used_at,fallback:`never`})}),r.usage_this_period?(0,h.jsxs)(`td`,{style:{whiteSpace:`nowrap`},children:[u,(0,h.jsx)(_,{usage:r.usage_this_period})]}):u?(0,h.jsx)(`td`,{title:`${r.usage.rows}${r.usage.rows===1?` row`:` rows`} returned in total · since this process started`,style:{whiteSpace:`nowrap`},children:u}):(0,h.jsx)(`td`,{className:`dim`,style:{whiteSpace:`nowrap`},children:`No activity yet`}),(0,h.jsx)(`td`,{className:`spark-cell`,children:(0,h.jsx)(l,{label:n,items:x.by[n],isPending:x.isPending,isError:x.isError,to:`/api-keys/${encodeURIComponent(n)}`})}),(0,h.jsx)(`td`,{children:(0,h.jsxs)(`div`,{className:`actions`,children:[(0,h.jsx)(t,{to:`/api-keys/${encodeURIComponent(n)}`,className:`btn ghost sm`,title:`Full details of ${n}`,children:`Details`}),(0,h.jsx)(`button`,{hidden:!e(`access.write`),type:`button`,className:`btn ghost sm icon`,title:`Edit ${n}`,"aria-label":`Edit`,onClick:()=>S.editKey(n),children:(0,h.jsx)(p,{})}),(0,h.jsx)(`button`,{hidden:!e(`access.write`),type:`button`,className:`btn ghost sm danger`,onClick:()=>void C(n),children:`Revoke`})]})})]},n)})})]})}):(0,h.jsxs)(`div`,{className:`empty`,children:[(0,h.jsx)(`strong`,{children:`No scoped API keys yet`}),(0,h.jsx)(`span`,{children:`QUERYAPIGATE_API_KEY is a full-access admin key. Create a scoped one to limit a caller to specific connections, read-only.`}),(0,h.jsx)(`button`,{hidden:!e(`access.write`),type:`button`,className:`btn primary`,onClick:()=>S.newKey(),children:`New API key`})]}),(0,h.jsxs)(h.Fragment,{children:[(0,h.jsxs)(`div`,{className:`page-head`,children:[(0,h.jsxs)(`div`,{className:`titles`,children:[(0,h.jsx)(`h1`,{children:`API keys`}),(0,h.jsx)(`span`,{className:`sub`,id:`apikeys-sub`,children:(w.length?w.length+(w.length===1?` key. `:` keys. `):``)+`Scope each one to connections, collections or roles.`})]}),(0,h.jsx)(`span`,{className:`spacer`}),(0,h.jsx)(`button`,{hidden:!e(`access.write`),id:`new-apikey`,type:`button`,className:`btn primary`,onClick:()=>S.newKey(),children:`New API key`})]}),(0,h.jsx)(`div`,{id:`apikeys-table`,className:`panel`,children:E})]})}export{y as ApiKeysPage};
@@ -1 +1 @@
1
- import{B as e,H as t,R as n,n as r,s as i,t as a,v as o,z as s}from"./client-ZHNiMG8H.js";import{t as c}from"./useMutation-BIoTFTZ8.js";import{D as l,M as u,N as d,T as f,f as p,k as m}from"./index-Cl93xBjn.js";import{t as h}from"./Time-s5E5qK3s.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
+ 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-D4M562eQ.js";import{D as l,M as u,N as d,T as f,f as p,k as m}from"./index-0v-rImZs.js";import{t as h}from"./Time-DurHvFXj.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-Cl93xBjn.js";import{t as c}from"./Time-s5E5qK3s.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)+`
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-0v-rImZs.js";import{t as c}from"./Time-DurHvFXj.js";var l=t(e(),1),u=a();function d(e){return/^(create|load|save)/.test(e)?`ok`:/^(delete|unload|revoke)/.test(e)?`bad`:``}function f(e){if(e==null||e===``)return`none`;let t=e=>e&&typeof e==`object`?JSON.stringify(e):String(e);return Array.isArray(e)?e.length?e.map(t).join(`, `):`none`:t(e)}var p=e=>e==null||e===``||Array.isArray(e)&&!e.length,m=e=>!!e&&typeof e==`object`&&!Array.isArray(e)&&(`from`in e||`to`in e);function h({changes:e}){let t=e&&typeof e==`object`?e:null,n=t?Object.keys(t).sort():[],r=n.filter(e=>m(t[e])||!p(t[e]));return r.length?(0,u.jsx)(`div`,{children:r.map(e=>{let n=t[e];return(0,u.jsx)(`div`,{className:`mono`,style:{fontSize:12,color:`var(--ink-2)`},children:m(n)?`${e}: ${f(n.from)} → ${f(n.to)}`:`${e}: ${f(n)}`},e)})}):(0,u.jsx)(`span`,{className:`dim`,children:n.length?`no other fields set`:`—`})}function g(e){let t=new Blob([JSON.stringify(e,null,2)+`
2
2
  `],{type:`application/json`}),n=document.createElement(`a`);n.href=URL.createObjectURL(t),n.download=`queryapigate-audit-log.json`,n.click(),setTimeout(()=>URL.revokeObjectURL(n.href),0)}function _(){let{toast:e}=s(),t=i({queryKey:[`audit`],queryFn:async()=>r(await n.GET(`/api/v1/audit`)),retry:!1}),[a,f]=(0,l.useState)(``),[p,m]=(0,l.useState)(``),_=t.data?.items??[],v=p.trim().toLowerCase(),y=_.filter(e=>(!a||e.action===a)&&(!v||[e.timestamp,e.actor,e.target].join(` `).toLowerCase().includes(v))),b=t.data?.actions??[],x,S=``;return t.isPending?x=(0,u.jsx)(o,{text:`Loading audit log…`}):t.isError?x=(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`Couldn’t load the audit log`}),(0,u.jsx)(`span`,{children:t.error.message}),(0,u.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>t.refetch(),children:`Retry`})]}):_.length?(S=y.length===_.length?_.length+(_.length===1?` entry`:` entries`):`${y.length} of ${_.length} entries`,x=y.length?(0,u.jsx)(`div`,{style:{overflowX:`auto`},children:(0,u.jsxs)(`table`,{className:`grid`,children:[(0,u.jsx)(`thead`,{children:(0,u.jsx)(`tr`,{children:[`Time`,`Actor`,`Action`,`Target`,`Changes`].map(e=>(0,u.jsx)(`th`,{children:e},e))})}),(0,u.jsx)(`tbody`,{children:y.map((e,t)=>(0,u.jsxs)(`tr`,{children:[(0,u.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,u.jsx)(c,{value:e.timestamp,fallback:``})}),(0,u.jsx)(`td`,{className:`mono`,children:e.actor||`-`}),(0,u.jsx)(`td`,{children:(0,u.jsx)(`span`,{className:`tag act `+d(e.action),children:e.action||``})}),(0,u.jsx)(`td`,{children:(0,u.jsx)(`span`,{className:`name`,children:e.target||``})}),(0,u.jsx)(`td`,{children:(0,u.jsx)(h,{changes:e.changes})})]},t))})]})}):(0,u.jsx)(`div`,{className:`empty`,children:(0,u.jsx)(`span`,{children:`No entries match this filter.`})})):x=(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`No administrative changes recorded yet`}),(0,u.jsx)(`span`,{children:`Creating or changing a connection, saved query or API key will appear here.`})]}),(0,u.jsxs)(u.Fragment,{children:[(0,u.jsxs)(`div`,{className:`page-head`,children:[(0,u.jsxs)(`div`,{className:`titles`,children:[(0,u.jsx)(`h1`,{children:`Audit log`}),(0,u.jsx)(`span`,{className:`sub`,id:`auditlog-sub`,children:`Administrative actions, newest first.`+(t.data?` Keeps the last ${t.data.retention} entries.`:``)})]}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`button`,{id:`refresh-auditlog`,type:`button`,className:`btn`,onClick:()=>void t.refetch(),children:`Refresh`}),(0,u.jsx)(`button`,{id:`export-auditlog`,type:`button`,className:`btn`,onClick:()=>{g(y),e(`Exported `+y.length+(y.length===1?` entry`:` entries`))},children:`Export`})]}),(0,u.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,u.jsxs)(`div`,{className:`panel-bar`,children:[(0,u.jsxs)(`select`,{id:`auditlog-action-filter`,"aria-label":`Action`,value:b.includes(a)?a:``,onChange:e=>f(e.target.value),children:[(0,u.jsx)(`option`,{value:``,children:`All actions`}),b.map(e=>(0,u.jsx)(`option`,{value:e,children:e},e))]}),(0,u.jsx)(`input`,{id:`auditlog-filter`,className:`search`,style:{width:260},type:`search`,placeholder:`Filter by actor, target, time…`,value:p,onChange:e=>m(e.target.value)}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`span`,{className:`panel-count`,id:`auditlog-count`,children:S})]}),(0,u.jsx)(`div`,{id:`auditlog-table`,children:x})]})]})}export{_ as AuditLogPage};
@@ -1 +1 @@
1
- import{B as e,H as t,R as n,c as r,n as i,r as a,s as o,v as s,z as c}from"./client-ZHNiMG8H.js";import{M as l,_ as u,k as d,s as f}from"./index-Cl93xBjn.js";import{a as p,t as m}from"./KeyActivity-D673KcWq.js";import{i as h,n as g,t as _}from"./StatTile-Cft4kJPH.js";import{n as v,t as y}from"./Results-XnOh21dp.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
+ 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-0v-rImZs.js";import{a as p,t as m}from"./KeyActivity-DuTyhSGT.js";import{i as h,n as g,t as _}from"./StatTile-Cft4kJPH.js";import{n as v,t as y}from"./Results-6QW3JK2B.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,4 +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-BIoTFTZ8.js";import{D as d,E as f,M as p,T as m,k as h,l as g,w as _}from"./index-Cl93xBjn.js";import{t as v}from"./Time-s5E5qK3s.js";import{n as y,t as b}from"./KeyActivity-D673KcWq.js";import{n as x,t as S}from"./icons-CFXE8hAz.js";import{t as ee}from"./Managed-VPfeN8Yu.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(`
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-D4M562eQ.js";import{D as d,E as f,M as p,T as m,k as h,l as g,w as _}from"./index-0v-rImZs.js";import{t as v}from"./Time-DurHvFXj.js";import{n as y,t as b}from"./KeyActivity-DuTyhSGT.js";import{n as x,t as S}from"./icons-CFXE8hAz.js";import{t as ee}from"./Managed-CQ0VXNNZ.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
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
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
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};