queryapigate 0.36.0__tar.gz → 0.37.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 (219) hide show
  1. {queryapigate-0.36.0/queryapigate.egg-info → queryapigate-0.37.0}/PKG-INFO +3 -3
  2. {queryapigate-0.36.0 → queryapigate-0.37.0}/README.md +2 -2
  3. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/__init__.py +1 -1
  4. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/app.py +46 -6
  5. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/cli.py +32 -13
  6. queryapigate-0.36.0/queryapigate/console_dist/assets/AccessMapPage-h4Txh6_X.js → queryapigate-0.37.0/queryapigate/console_dist/assets/AccessMapPage-T11BZlz_.js +3 -3
  7. queryapigate-0.36.0/queryapigate/console_dist/assets/AdministratorsPage-DivFXGAk.js → queryapigate-0.37.0/queryapigate/console_dist/assets/AdministratorsPage-CFW9efGs.js +1 -1
  8. queryapigate-0.36.0/queryapigate/console_dist/assets/AgentsPage-DgbGG-X6.js → queryapigate-0.37.0/queryapigate/console_dist/assets/AgentsPage-DBaMef7r.js +1 -1
  9. queryapigate-0.36.0/queryapigate/console_dist/assets/AlertsPage-JvVikVfR.js → queryapigate-0.37.0/queryapigate/console_dist/assets/AlertsPage-BGYrlj9z.js +1 -1
  10. queryapigate-0.36.0/queryapigate/console_dist/assets/ApiKeysPage-4l-cQnNJ.js → queryapigate-0.37.0/queryapigate/console_dist/assets/ApiKeysPage-bKlOf5C4.js +1 -1
  11. queryapigate-0.36.0/queryapigate/console_dist/assets/ApprovalsPage-BqQaxTK3.js → queryapigate-0.37.0/queryapigate/console_dist/assets/ApprovalsPage-XtZognjK.js +1 -1
  12. queryapigate-0.36.0/queryapigate/console_dist/assets/AuditLogPage-DZUuB0af.js → queryapigate-0.37.0/queryapigate/console_dist/assets/AuditLogPage-BTzPqM3n.js +1 -1
  13. queryapigate-0.36.0/queryapigate/console_dist/assets/CachingPage-BeW1bGwK.js → queryapigate-0.37.0/queryapigate/console_dist/assets/CachingPage-BGEN2JEZ.js +1 -1
  14. queryapigate-0.36.0/queryapigate/console_dist/assets/CodeBox-5YJrpfHh.js → queryapigate-0.37.0/queryapigate/console_dist/assets/CodeBox-B933gn9I.js +1 -1
  15. queryapigate-0.36.0/queryapigate/console_dist/assets/ConnectionsPage-onpVbonP.js → queryapigate-0.37.0/queryapigate/console_dist/assets/ConnectionsPage-u3wkJBCg.js +1 -1
  16. queryapigate-0.36.0/queryapigate/console_dist/assets/DesignerPage-Zo5YZZhb.js → queryapigate-0.37.0/queryapigate/console_dist/assets/DesignerPage-BolQIner.js +1 -1
  17. queryapigate-0.36.0/queryapigate/console_dist/assets/DestinationsPage-SBwbj6TB.js → queryapigate-0.37.0/queryapigate/console_dist/assets/DestinationsPage-DSRHT2Uu.js +1 -1
  18. queryapigate-0.36.0/queryapigate/console_dist/assets/ExportsPage-BIJILblW.js → queryapigate-0.37.0/queryapigate/console_dist/assets/ExportsPage-DSgWJQ1N.js +1 -1
  19. queryapigate-0.36.0/queryapigate/console_dist/assets/HelpPage-O7S6pfMI.js → queryapigate-0.37.0/queryapigate/console_dist/assets/HelpPage-DhEIKUsN.js +1 -1
  20. queryapigate-0.36.0/queryapigate/console_dist/assets/HomePage-DY1dpfuK.js → queryapigate-0.37.0/queryapigate/console_dist/assets/HomePage-DJfXglyP.js +2 -2
  21. queryapigate-0.36.0/queryapigate/console_dist/assets/Managed-DHCfC-9H.js → queryapigate-0.37.0/queryapigate/console_dist/assets/Managed-BS06VUT9.js +1 -1
  22. queryapigate-0.36.0/queryapigate/console_dist/assets/MetricsPage-D3fhsmN3.js → queryapigate-0.37.0/queryapigate/console_dist/assets/MetricsPage-CC4GNG3w.js +1 -1
  23. queryapigate-0.36.0/queryapigate/console_dist/assets/RepositoryPage-8se0M13M.js → queryapigate-0.37.0/queryapigate/console_dist/assets/RepositoryPage-CyVHHs55.js +1 -1
  24. queryapigate-0.36.0/queryapigate/console_dist/assets/Results-BVRjDSO0.js → queryapigate-0.37.0/queryapigate/console_dist/assets/Results-BPGA2RXL.js +1 -1
  25. queryapigate-0.36.0/queryapigate/console_dist/assets/RolesPage-BvYNPktM.js → queryapigate-0.37.0/queryapigate/console_dist/assets/RolesPage-CkzAvWZ2.js +1 -1
  26. queryapigate-0.36.0/queryapigate/console_dist/assets/SchemaBrowser-BBPaiMFY.js → queryapigate-0.37.0/queryapigate/console_dist/assets/SchemaBrowser-B2wMn0QD.js +1 -1
  27. queryapigate-0.36.0/queryapigate/console_dist/assets/SettingsPage-CxY2Niuc.js → queryapigate-0.37.0/queryapigate/console_dist/assets/SettingsPage-T46yqNyh.js +1 -1
  28. queryapigate-0.36.0/queryapigate/console_dist/assets/StatTile-BY-03DUD.js → queryapigate-0.37.0/queryapigate/console_dist/assets/StatTile-zVc4DpFR.js +1 -1
  29. queryapigate-0.36.0/queryapigate/console_dist/assets/Time-yK6sDFr-.js → queryapigate-0.37.0/queryapigate/console_dist/assets/Time-BMys0hNR.js +1 -1
  30. queryapigate-0.36.0/queryapigate/console_dist/assets/forms-uF5aXx1J.js → queryapigate-0.37.0/queryapigate/console_dist/assets/forms-C5rX5RT9.js +1 -1
  31. queryapigate-0.36.0/queryapigate/console_dist/assets/forms-6-RLlXrl.js → queryapigate-0.37.0/queryapigate/console_dist/assets/forms-DXnuEzsc.js +1 -1
  32. queryapigate-0.36.0/queryapigate/console_dist/assets/icons-BPJHnFc_.js → queryapigate-0.37.0/queryapigate/console_dist/assets/icons-BdwlYqAC.js +1 -1
  33. queryapigate-0.36.0/queryapigate/console_dist/assets/index-DMiPsI_5.js → queryapigate-0.37.0/queryapigate/console_dist/assets/index-Efe2sJ9A.js +2 -2
  34. queryapigate-0.36.0/queryapigate/console_dist/assets/maskRules-BmvHhs_t.js → queryapigate-0.37.0/queryapigate/console_dist/assets/maskRules-BE2FbImC.js +1 -1
  35. queryapigate-0.36.0/queryapigate/console_dist/assets/reach-Czf5bpIV.js → queryapigate-0.37.0/queryapigate/console_dist/assets/reach-Jb8SEj5E.js +1 -1
  36. queryapigate-0.36.0/queryapigate/console_dist/assets/shared-CmIynm8l.js → queryapigate-0.37.0/queryapigate/console_dist/assets/shared-BNjhvRVu.js +1 -1
  37. queryapigate-0.36.0/queryapigate/console_dist/assets/useMutation-DGd9BCI3.js → queryapigate-0.37.0/queryapigate/console_dist/assets/useMutation-ptto6u1J.js +1 -1
  38. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/console_dist/index.html +1 -1
  39. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/databases.py +6 -3
  40. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/engine.py +54 -0
  41. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/experimental.py +7 -6
  42. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/exports.py +30 -6
  43. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/formats.py +6 -1
  44. queryapigate-0.37.0/queryapigate/mongodocs.py +161 -0
  45. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/parquet.py +10 -1
  46. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/runners.py +45 -0
  47. {queryapigate-0.36.0 → queryapigate-0.37.0/queryapigate.egg-info}/PKG-INFO +3 -3
  48. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate.egg-info/SOURCES.txt +34 -32
  49. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_experimental.py +7 -3
  50. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_integration.py +58 -3
  51. queryapigate-0.37.0/tests/test_mongo_streaming.py +258 -0
  52. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_queryapigate.py +0 -7
  53. {queryapigate-0.36.0 → queryapigate-0.37.0}/LICENSE +0 -0
  54. {queryapigate-0.36.0 → queryapigate-0.37.0}/pyproject.toml +0 -0
  55. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/__main__.py +0 -0
  56. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/accesslog.py +0 -0
  57. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/adminroles.py +0 -0
  58. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/admins.py +0 -0
  59. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/agentwrites.py +0 -0
  60. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/alerts.py +0 -0
  61. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/apikeys.py +0 -0
  62. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/auditchain.py +0 -0
  63. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/auditstream.py +0 -0
  64. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/aws.py +0 -0
  65. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/broadcast.py +0 -0
  66. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/bundle.py +0 -0
  67. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/cache.py +0 -0
  68. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/collection_admin.py +0 -0
  69. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/config.py +0 -0
  70. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/configcode.py +0 -0
  71. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/console.py +0 -0
  72. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/console_dist/assets/index-BLd0t6vZ.css +0 -0
  73. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/console_dist/assets/state-O13cl7cO.js +0 -0
  74. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/console_dist/favicon.svg +0 -0
  75. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/cors.py +0 -0
  76. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/db.py +0 -0
  77. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/definitions.py +0 -0
  78. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/deprecations.py +0 -0
  79. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/descriptions.py +0 -0
  80. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/destinations.py +0 -0
  81. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/duckfiles.py +0 -0
  82. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/errors.py +0 -0
  83. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/events.py +0 -0
  84. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/examples.py +0 -0
  85. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/exporting.py +0 -0
  86. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/governance.py +0 -0
  87. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/gunicorn_conf.py +0 -0
  88. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/history.py +0 -0
  89. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/instances.py +0 -0
  90. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/jwtauth.py +0 -0
  91. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/lib/h2-2.2.224.jar +0 -0
  92. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/limits.py +0 -0
  93. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/logging_setup.py +0 -0
  94. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/masking.py +0 -0
  95. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/mcp_server.py +0 -0
  96. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/metrics.py +0 -0
  97. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/mongotools.py +0 -0
  98. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/oidc.py +0 -0
  99. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/openapi.py +0 -0
  100. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/params.py +0 -0
  101. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/pool.py +0 -0
  102. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/postman.py +0 -0
  103. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/quotas.py +0 -0
  104. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/ratelimit.py +0 -0
  105. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/rediscache.py +0 -0
  106. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/rekey.py +0 -0
  107. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/schema.py +0 -0
  108. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/secheaders.py +0 -0
  109. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/secretrefs.py +0 -0
  110. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/services/__init__.py +0 -0
  111. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/services/access.py +0 -0
  112. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/services/administrators.py +0 -0
  113. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/services/audit.py +0 -0
  114. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/services/collections.py +0 -0
  115. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/services/connections.py +0 -0
  116. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/services/descriptions.py +0 -0
  117. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/services/destinations.py +0 -0
  118. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/services/exports.py +0 -0
  119. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/services/mcp.py +0 -0
  120. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/services/queries.py +0 -0
  121. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/sqlflow.py +0 -0
  122. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/sqltools.py +0 -0
  123. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/sso.py +0 -0
  124. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/store.py +0 -0
  125. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/tableguard.py +0 -0
  126. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/tracing.py +0 -0
  127. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/usage.py +0 -0
  128. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/v1.py +0 -0
  129. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/v1_spec.py +0 -0
  130. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate/vault.py +0 -0
  131. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate.egg-info/dependency_links.txt +0 -0
  132. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate.egg-info/entry_points.txt +0 -0
  133. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate.egg-info/requires.txt +0 -0
  134. {queryapigate-0.36.0 → queryapigate-0.37.0}/queryapigate.egg-info/top_level.txt +0 -0
  135. {queryapigate-0.36.0 → queryapigate-0.37.0}/setup.cfg +0 -0
  136. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_adhoc_history.py +0 -0
  137. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_adminroles.py +0 -0
  138. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_admins.py +0 -0
  139. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_agent_writes.py +0 -0
  140. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_alerts.py +0 -0
  141. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_api_administrators.py +0 -0
  142. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_api_keys.py +0 -0
  143. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_api_v1.py +0 -0
  144. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_audit_chain.py +0 -0
  145. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_audit_log.py +0 -0
  146. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_audit_siem.py +0 -0
  147. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_audit_stream.py +0 -0
  148. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_aws.py +0 -0
  149. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_backup_restore.py +0 -0
  150. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_broadcast.py +0 -0
  151. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_cli.py +0 -0
  152. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_collection_bundle.py +0 -0
  153. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_collection_postman.py +0 -0
  154. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_collections.py +0 -0
  155. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_configcode.py +0 -0
  156. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_connection_encryption.py +0 -0
  157. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_console.py +0 -0
  158. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_cors_and_rate_limit.py +0 -0
  159. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_data_protection.py +0 -0
  160. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_databases.py +0 -0
  161. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_db.py +0 -0
  162. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_dependency_floors.py +0 -0
  163. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_deprecations.py +0 -0
  164. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_descriptions.py +0 -0
  165. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_destinations.py +0 -0
  166. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_docs_site.py +0 -0
  167. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_duckdb.py +0 -0
  168. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_duckdb_files.py +0 -0
  169. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_errors.py +0 -0
  170. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_events.py +0 -0
  171. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_events_server.py +0 -0
  172. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_examples.py +0 -0
  173. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_export_security.py +0 -0
  174. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_exporting.py +0 -0
  175. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_exports.py +0 -0
  176. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_fips.py +0 -0
  177. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_helm.py +0 -0
  178. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_history.py +0 -0
  179. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_howto.py +0 -0
  180. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_instances.py +0 -0
  181. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_jdbc.py +0 -0
  182. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_jwt.py +0 -0
  183. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_key_rotation.py +0 -0
  184. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_limits.py +0 -0
  185. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_masking.py +0 -0
  186. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_masking_requests.py +0 -0
  187. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_mcp.py +0 -0
  188. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_mcp_keycloak.py +0 -0
  189. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_mcp_oauth.py +0 -0
  190. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_mongotools.py +0 -0
  191. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_multinode.py +0 -0
  192. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_observability.py +0 -0
  193. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_oidc.py +0 -0
  194. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_params.py +0 -0
  195. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_parquet.py +0 -0
  196. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_pool.py +0 -0
  197. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_publishing.py +0 -0
  198. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_query_window.py +0 -0
  199. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_quotas.py +0 -0
  200. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_rekey.py +0 -0
  201. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_release_check.py +0 -0
  202. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_response_cache.py +0 -0
  203. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_roles.py +0 -0
  204. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_rolling_upgrade.py +0 -0
  205. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_secretrefs.py +0 -0
  206. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_security_headers.py +0 -0
  207. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_settings.py +0 -0
  208. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_shared_rate_limit.py +0 -0
  209. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_sql_guard_fuzz.py +0 -0
  210. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_sql_guard_writes.py +0 -0
  211. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_sqlflow.py +0 -0
  212. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_sso.py +0 -0
  213. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_sso_keycloak.py +0 -0
  214. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_supply_chain.py +0 -0
  215. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_tableguard.py +0 -0
  216. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_tracing.py +0 -0
  217. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_upgrades.py +0 -0
  218. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_usage.py +0 -0
  219. {queryapigate-0.36.0 → queryapigate-0.37.0}/tests/test_vault.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: queryapigate
3
- Version: 0.36.0
3
+ Version: 0.37.0
4
4
  Summary: Turn SQL into governed REST APIs over MySQL, PostgreSQL, ClickHouse, SQLite, DuckDB, H2, MongoDB, and Parquet/CSV/JSON files - JSON, CSV, Parquet or Excel out, with scoped keys, an admin Console and MCP for AI agents.
5
5
  Author-email: Anantha Raju C <arcswdev@gmail.com>
6
6
  License-Expression: FSL-1.1-MIT
@@ -102,7 +102,7 @@ Dynamic: license-file
102
102
  <p align="center">
103
103
  <a href="https://raw.githubusercontent.com/AnanthaRajuC/QueryAPIGate/main/documentation/screenshots/queryapigate-architecture.gif">
104
104
  <img src="https://raw.githubusercontent.com/AnanthaRajuC/QueryAPIGate/main/documentation/screenshots/queryapigate-architecture.gif"
105
- alt="One governed gate between your databases and everything that asks them for data: apps, partners, AI agents, people and pipelines come in through five front doors - REST endpoints, the MCP server, live events, exports and the CLI, the Management API - and every call passes the same governed core (identity, policy, execution) before it reaches PostgreSQL, MySQL, SQLite, DuckDB, ClickHouse, the experimental H2, JDBC and MongoDB, files through DuckDB, or deliveries to object storage" width="820">
105
+ alt="One governed gate between your databases and everything that asks them for data: apps, partners, AI agents, people and pipelines come in through five front doors - REST endpoints, the MCP server, live events, exports and the CLI, the Management API - and every call passes the same governed core (identity, policy, execution) before it reaches PostgreSQL, MySQL, SQLite, DuckDB, ClickHouse, MongoDB, the experimental H2 and JDBC, files through DuckDB, or deliveries to object storage" width="820">
106
106
  </a>
107
107
  <br>
108
108
  <sub>Every front door runs the same checks, so an AI agent's call is governed exactly like an app's. Write the SQL once; it's a REST endpoint, an MCP tool, a live event stream and a scheduled export.</sub>
@@ -183,7 +183,7 @@ safely". Use cases, how it compares to PostgREST, Hasura and others, and the thi
183
183
  - **[An MCP server for AI agents](https://github.com/AnanthaRajuC/QueryAPIGate/blob/main/documentation/MCP.md)** - saved queries as tools, OAuth sign-in, write tools that
184
184
  wait for a person's approval.
185
185
  - **[Eight database types, and files](https://github.com/AnanthaRajuC/QueryAPIGate/blob/main/documentation/DATABASE_CONNECTION_CONFIGURATION.md#support-matrix)** -
186
- PostgreSQL, MySQL, SQLite, DuckDB and ClickHouse; H2, JDBC and MongoDB; Parquet, CSV and JSON on S3, GCS, R2 or disk.
186
+ PostgreSQL, MySQL, SQLite, DuckDB, ClickHouse and MongoDB; H2 and JDBC; Parquet, CSV and JSON on S3, GCS, R2 or disk.
187
187
  - **[Eight output formats, and streaming](https://github.com/AnanthaRajuC/QueryAPIGate/blob/main/documentation/API.md#streaming-exports)** - JSON, NDJSON, XML, YAML, CSV,
188
188
  TSV, Excel and Parquet; whole results streamed from the database cursor.
189
189
  - **[Exports to object storage](https://github.com/AnanthaRajuC/QueryAPIGate/blob/main/documentation/API.md#exports-to-object-storage)** - scheduled, incremental, to
@@ -24,7 +24,7 @@
24
24
  <p align="center">
25
25
  <a href="https://raw.githubusercontent.com/AnanthaRajuC/QueryAPIGate/main/documentation/screenshots/queryapigate-architecture.gif">
26
26
  <img src="https://raw.githubusercontent.com/AnanthaRajuC/QueryAPIGate/main/documentation/screenshots/queryapigate-architecture.gif"
27
- alt="One governed gate between your databases and everything that asks them for data: apps, partners, AI agents, people and pipelines come in through five front doors - REST endpoints, the MCP server, live events, exports and the CLI, the Management API - and every call passes the same governed core (identity, policy, execution) before it reaches PostgreSQL, MySQL, SQLite, DuckDB, ClickHouse, the experimental H2, JDBC and MongoDB, files through DuckDB, or deliveries to object storage" width="820">
27
+ alt="One governed gate between your databases and everything that asks them for data: apps, partners, AI agents, people and pipelines come in through five front doors - REST endpoints, the MCP server, live events, exports and the CLI, the Management API - and every call passes the same governed core (identity, policy, execution) before it reaches PostgreSQL, MySQL, SQLite, DuckDB, ClickHouse, MongoDB, the experimental H2 and JDBC, files through DuckDB, or deliveries to object storage" width="820">
28
28
  </a>
29
29
  <br>
30
30
  <sub>Every front door runs the same checks, so an AI agent's call is governed exactly like an app's. Write the SQL once; it's a REST endpoint, an MCP tool, a live event stream and a scheduled export.</sub>
@@ -105,7 +105,7 @@ safely". Use cases, how it compares to PostgREST, Hasura and others, and the thi
105
105
  - **[An MCP server for AI agents](https://github.com/AnanthaRajuC/QueryAPIGate/blob/main/documentation/MCP.md)** - saved queries as tools, OAuth sign-in, write tools that
106
106
  wait for a person's approval.
107
107
  - **[Eight database types, and files](https://github.com/AnanthaRajuC/QueryAPIGate/blob/main/documentation/DATABASE_CONNECTION_CONFIGURATION.md#support-matrix)** -
108
- PostgreSQL, MySQL, SQLite, DuckDB and ClickHouse; H2, JDBC and MongoDB; Parquet, CSV and JSON on S3, GCS, R2 or disk.
108
+ PostgreSQL, MySQL, SQLite, DuckDB, ClickHouse and MongoDB; H2 and JDBC; Parquet, CSV and JSON on S3, GCS, R2 or disk.
109
109
  - **[Eight output formats, and streaming](https://github.com/AnanthaRajuC/QueryAPIGate/blob/main/documentation/API.md#streaming-exports)** - JSON, NDJSON, XML, YAML, CSV,
110
110
  TSV, Excel and Parquet; whole results streamed from the database cursor.
111
111
  - **[Exports to object storage](https://github.com/AnanthaRajuC/QueryAPIGate/blob/main/documentation/API.md#exports-to-object-storage)** - scheduled, incremental, to
@@ -1,5 +1,5 @@
1
1
  """QueryAPIGate - expose SQL databases as a REST API."""
2
- __version__ = "0.36.0"
2
+ __version__ = "0.37.0"
3
3
 
4
4
  from .app import create_app # noqa: E402 (app imports __version__)
5
5
 
@@ -39,6 +39,7 @@ from . import (
39
39
  logging_setup,
40
40
  masking,
41
41
  metrics,
42
+ mongodocs,
42
43
  mongotools,
43
44
  openapi,
44
45
  quotas,
@@ -52,7 +53,7 @@ from . import (
52
53
  )
53
54
  from . import params as param_rules
54
55
  from .errors import ApiError, code_for
55
- from .formats import FORMATTERS, STREAM_FORMATTERS, json_default, stream_response
56
+ from .formats import FORMATTERS, STREAM_FORMATTERS, chunked_response, json_default, stream_response
56
57
 
57
58
  log = logging.getLogger('queryapigate')
58
59
  bp = Blueprint('api', __name__)
@@ -589,6 +590,37 @@ def stream_sql_response(sql, connection_name, params, timeout, output_format, fi
589
590
  return response
590
591
 
591
592
 
593
+ def stream_mongo_response(collection, filter_doc, connection_name, params, timeout, output_format, filename,
594
+ projection=None, sort=None, saved=None, statement=None, entry=None):
595
+ """stream_sql_response() for a find() (ADR 0022): the same constraints and history, the documents shaped by
596
+ their format (mongodocs.chunks()). A CSV or TSV run that left fields out says how many in its history entry -
597
+ the response can't, its headers went out with the first row."""
598
+ if output_format not in STREAM_FORMATTERS:
599
+ raise ApiError(f"stream=true only supports these formats: {', '.join(sorted(STREAM_FORMATTERS))}",
600
+ code='invalid_stream')
601
+ if request.args.get('page') or request.args.get('page_size'):
602
+ raise ApiError('stream=true exports the whole result and does not accept page/page_size', code='invalid_stream')
603
+ docs = engine.stream_mongo(collection, filter_doc, connection_name, params, timeout, projection=projection,
604
+ sort=sort, key_name=metrics_key_name(), allowed_tables=g.permission.allowed_tables,
605
+ mask=g.permission.mask)
606
+ broadcaster = current_app.extensions['queryapigate_broadcaster'] # captured now: see stream_sql_response()
607
+ entry = {**run_entry(connection_name), **(entry or {})}
608
+ shaped = None
609
+
610
+ def record(finished):
611
+ finished = {**finished, **(shaped.report() if shaped is not None else {})}
612
+ if saved is not None:
613
+ _record_and_broadcast(*saved, finished, broadcaster)
614
+ else:
615
+ _record_adhoc_and_broadcast(statement, params, finished, broadcaster)
616
+
617
+ body, shaped = mongodocs.chunks(output_format, _record_stream_history(docs, entry, record, g.get('permission')),
618
+ projection)
619
+ response = chunked_response(output_format, body, filename)
620
+ response.call_on_close(docs.close) # as in stream_sql_response(): a client gone before the first chunk
621
+ return response
622
+
623
+
592
624
  def masked_entry(fired):
593
625
  """A run's history field for what masking did (ADR 0005): which result columns, which action, from which source
594
626
  - so "was national_id ever returned to this key?" is answered by history. Nothing when nothing was masked."""
@@ -711,9 +743,14 @@ def execute_mongo_endpoint():
711
743
  params = get_object(data.get('params'), 'params')
712
744
  output_format = get_output_format(data)
713
745
  timeout = get_timeout(data)
746
+ statement = json.dumps({'collection': data['collection'], 'filter': filter_doc}, default=str)
747
+ if get_stream_flag():
748
+ return stream_mongo_response(data['collection'], filter_doc, data['connection_name'], params, timeout,
749
+ output_format, filename=data['collection'], projection=data.get('projection'),
750
+ sort=data.get('sort'), statement=statement,
751
+ entry={'query_type': 'mongo', 'mongo_collection': data['collection']})
714
752
  limit, offset, page = get_pagination()
715
753
  entry = {**run_entry(data['connection_name']), 'query_type': 'mongo', 'mongo_collection': data['collection']}
716
- statement = json.dumps({'collection': data['collection'], 'filter': filter_doc}, default=str)
717
754
  try:
718
755
  result, elapsed_ms = engine.timed(engine.execute_mongo, data['collection'], filter_doc,
719
756
  data['connection_name'], limit, offset, params, timeout,
@@ -1009,13 +1046,16 @@ def _agent_write_answer(request, **extra):
1009
1046
 
1010
1047
  def run_saved_mongo(saved, path, number, ref, connection_name, raw, output_format, timeout):
1011
1048
  """The mongo query_type branch of run_saved(): a find() query instead of SQL. No caching (cache_ttl is
1012
- not yet supported for a mongo saved query) and no streaming (?stream=true) in this version - see
1013
- BACKLOG #36. Always read-only, same as the ad-hoc /execute_mongo."""
1014
- if get_stream_flag():
1015
- raise ApiError('Streaming is not supported for Mongo queries yet', 400, code='stream_unsupported')
1049
+ not yet supported for a mongo saved query). ?stream=true streams the whole result (ADR 0022). Always
1050
+ read-only, same as the ad-hoc /execute_mongo."""
1016
1051
  used = set(mongotools.placeholder_names(saved.get('mongo_filter') or {}))
1017
1052
  values = param_rules.resolve(saved.get('query_parameters'), raw, used=used)
1018
1053
  filter_doc = mongotools.fill_placeholders(saved.get('mongo_filter') or {}, values)
1054
+ if get_stream_flag():
1055
+ return stream_mongo_response(saved['mongo_collection'], filter_doc, connection_name, None, timeout,
1056
+ output_format, filename=ref,
1057
+ projection=saved.get('mongo_projection'), sort=saved.get('mongo_sort'),
1058
+ saved=(path, number))
1019
1059
  limit, offset, page = get_pagination()
1020
1060
  entry = run_entry(connection_name)
1021
1061
  try:
@@ -185,7 +185,8 @@ def _export_run(args):
185
185
  print(f'queryapigate export: {error}', file=sys.stderr)
186
186
  return 2
187
187
 
188
- from .engine import stream_sql
188
+ from . import mongodocs
189
+ from .engine import stream_mongo, stream_sql
189
190
  from .errors import ApiError
190
191
  from .formats import STREAM_FORMATTERS, iter_stream_chunks
191
192
 
@@ -212,13 +213,20 @@ def _export_run(args):
212
213
  started = time.monotonic()
213
214
  entry = {'executed_at': store.now(), 'connection_name': connection_name, 'request_id': None,
214
215
  'key_name': 'cli'}
216
+ find = sql if isinstance(sql, mongodocs.Find) else None
217
+ timeout = config.effective_timeout(args.timeout)
215
218
  try:
216
- columns, rows = stream_sql(sql, connection_name, values, config.effective_timeout(args.timeout))
219
+ if find is not None:
220
+ columns, rows = None, stream_mongo(find.collection, find.filter, connection_name, None, timeout,
221
+ projection=find.projection, sort=find.sort)
222
+ else:
223
+ columns, rows = stream_sql(sql, connection_name, values, timeout)
217
224
  except ApiError as error:
218
225
  store.record_execution(path, version,
219
226
  {**entry, 'status': 'error', 'error': error.message, 'code': error.code})
220
227
  raise
221
228
  row_count = 0
229
+ shaped = None
222
230
 
223
231
  def counted(row_iter):
224
232
  nonlocal row_count
@@ -226,18 +234,30 @@ def _export_run(args):
226
234
  row_count += 1
227
235
  yield row
228
236
 
229
- if args.format == 'parquet':
230
- from . import parquet
231
- parquet.write(columns, counted(rows), tmp_path)
232
- else:
233
- with open(tmp_path, 'w', newline='') as f:
234
- for chunk in iter_stream_chunks(args.format, columns, counted(rows)):
235
- f.write(chunk)
237
+ with contextlib.closing(rows): # a failed write must still end the query and give its connection back
238
+ if find is not None: # documents, shaped by the format (ADR 0022)
239
+ body, shaped = mongodocs.chunks(args.format, counted(rows), find.projection)
240
+ with open(tmp_path, 'wb' if args.format == 'parquet' else 'w',
241
+ **({} if args.format == 'parquet' else {'newline': ''})) as f:
242
+ for chunk in body:
243
+ f.write(chunk)
244
+ elif args.format == 'parquet':
245
+ from . import parquet
246
+ parquet.write(columns, counted(rows), tmp_path)
247
+ else:
248
+ with open(tmp_path, 'w', newline='') as f:
249
+ for chunk in iter_stream_chunks(args.format, columns, counted(rows)):
250
+ f.write(chunk)
236
251
  os.replace(tmp_path, out_path)
237
252
  # The CLI runs outside the API's grants (a local operator is already trusted), but what it exports is
238
253
  # still on the record: run history, with `cli` as the caller.
239
- store.record_execution(path, version, {**entry, 'status': 'success', 'rows': row_count,
254
+ report = shaped.report() if shaped is not None else {}
255
+ store.record_execution(path, version, {**entry, 'status': 'success', 'rows': row_count, **report,
240
256
  'duration_ms': round((time.monotonic() - started) * 1000, 1)})
257
+ if report:
258
+ print(f"queryapigate export: {report['fields_dropped']} field(s) had no column and were left out - a "
259
+ f'CSV takes its columns from the projection, or else the first {mongodocs.LOOKAHEAD} documents. '
260
+ 'Name the fields in the projection, or export NDJSON or Parquet, to keep them', file=sys.stderr)
241
261
  except ApiError as error:
242
262
  print(f'queryapigate export: {error.message}', file=sys.stderr)
243
263
  return 1
@@ -255,8 +275,7 @@ def _export_run(args):
255
275
  def _export_to_destination(args, path, name, version, connection_name, sql, values):
256
276
  """`queryapigate export QUERY --to DESTINATION --out PATH`: the same run, written by DuckDB under a destination's
257
277
  prefix (ADR 0004) - a bucket or a folder - instead of a local file. Raises ApiError; the caller reports it."""
258
- from . import destinations, exporting
259
- from .engine import stream_sql
278
+ from . import destinations, exporting, exports
260
279
  if args.format not in exporting.FORMATS:
261
280
  raise ApiError(f"--format must be one of {', '.join(exporting.FORMATS)} with --to")
262
281
  destination = destinations.get(args.to)
@@ -266,7 +285,7 @@ def _export_to_destination(args, path, name, version, connection_name, sql, valu
266
285
  entry = {'executed_at': store.now(), 'connection_name': connection_name, 'request_id': None, 'key_name': 'cli',
267
286
  'transport': 'export', 'destination': args.to, 'run_id': run_id}
268
287
  try:
269
- columns, rows = stream_sql(sql, connection_name, values, config.effective_timeout(args.timeout))
288
+ columns, rows = exports.open_stream(sql, connection_name, values, config.effective_timeout(args.timeout))
270
289
  with contextlib.closing(rows): # a failed write must still end the query and give its connection back
271
290
  result = exporting.deliver(destination, relative, columns, rows, args.format)
272
291
  except ApiError as error:
@@ -1,3 +1,3 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/forms-uF5aXx1J.js","assets/index-DMiPsI_5.js","assets/index-BLd0t6vZ.css","assets/SchemaBrowser-BBPaiMFY.js","assets/useMutation-DGd9BCI3.js","assets/reach-Czf5bpIV.js","assets/CodeBox-5YJrpfHh.js"])))=>i.map(i=>d[i]);
2
- import{$ as e,C as t,D as n,F as r,H as i,I as a,L as o,Q as s,V as c,Z as l,h as u,o as d,s as f,u as p,z as m}from"./index-DMiPsI_5.js";import{a as h}from"./reach-Czf5bpIV.js";import{t as g}from"./Time-yK6sDFr-.js";import{f as _,i as v,n as y,r as b,u as x}from"./SchemaBrowser-BBPaiMFY.js";import{t as S}from"./CodeBox-5YJrpfHh.js";var C=e(s(),1),w=l();function T({term:e,children:t}){return(0,w.jsxs)(`div`,{children:[(0,w.jsx)(`dt`,{children:e}),(0,w.jsx)(`dd`,{title:typeof t==`string`?t:void 0,children:t})]})}function E({name:e,dbType:s}){let c=m(),{closeDrawer:l,toast:u}=o(),d=i({queryKey:[`queries`,`detail`,e,`info`],queryFn:async()=>n(await t.GET(`/api/v1/queries/{name}`,{params:{path:{name:e}}})),retry:!1}),f=d.data,p=f?.versions[f.versions.length-1],h=p?.query_type===`mongo`?JSON.stringify(p.mongo??{},null,2):p?.sql??``,_=i({queryKey:[`queries`,`flow`,e,p?.version],queryFn:()=>v(e,p.version),enabled:!(!p||p.query_type!==`sql`||h.includes(`
3
- `)),staleTime:1/0});if(d.isPending)return(0,w.jsx)(r,{});if(!f||!p)return(0,w.jsx)(`div`,{className:`hint`,children:`Could not load the SQL.`});let y=_.data?.formatted||h,b=f.created_at,x=f.last_used_at;return(0,w.jsxs)(`div`,{className:`form`,children:[p.description?(0,w.jsx)(`p`,{className:`d-desc`,children:p.description}):null,(0,w.jsxs)(`dl`,{className:`meta`,children:[(0,w.jsx)(T,{term:`connection`,children:p.connection_name||`—`}),(0,w.jsx)(T,{term:`database`,children:s?(0,w.jsx)(`span`,{className:`tag`,children:s}):(0,w.jsx)(`span`,{className:`dim`,children:`—`})}),(0,w.jsx)(T,{term:`collection`,children:f.collection||`—`}),(0,w.jsx)(T,{term:`status`,children:p.status||`—`}),(0,w.jsx)(T,{term:`author`,children:p.author||`—`}),(0,w.jsx)(T,{term:`created`,children:(0,w.jsx)(g,{value:b})}),(0,w.jsx)(T,{term:`last modified`,children:(0,w.jsx)(g,{value:p.last_modified_at||p.created_at})}),(0,w.jsx)(T,{term:`last used`,children:(0,w.jsx)(g,{value:x,fallback:`never`})}),p.tags.length?(0,w.jsx)(T,{term:`tags`,children:p.tags.join(`, `)}):null]}),(0,w.jsx)(`p`,{className:`sub-h`,style:{marginTop:6},children:`SQL · v${p.version}${f.versions.length>1?` (latest)`:``}`}),(0,w.jsx)(`div`,{children:_.isFetching?(0,w.jsx)(r,{text:`Loading SQL…`}):(0,w.jsxs)(w.Fragment,{children:[(0,w.jsx)(S,{sql:y}),(0,w.jsx)(`div`,{style:{marginTop:6},children:(0,w.jsx)(`button`,{type:`button`,className:`btn sm ghost`,onClick:()=>a(y,u),children:`Copy SQL`})})]})}),(0,w.jsx)(`p`,{className:`sub-h`,style:{marginTop:6},children:`Versions`}),(0,w.jsx)(`div`,{className:`panel`,style:{overflow:`auto`},children:(0,w.jsxs)(`table`,{className:`grid qi-versions`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsx)(`tr`,{children:[`Version`,`Created`,`Last modified`,`Runs`].map(e=>(0,w.jsx)(`th`,{children:e},e))})}),(0,w.jsx)(`tbody`,{children:[...f.versions].reverse().map(e=>(0,w.jsxs)(`tr`,{children:[(0,w.jsx)(`td`,{className:`mono`,children:`v`+e.version}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(g,{value:e.created_at})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(g,{value:e.last_modified_at})}),(0,w.jsx)(`td`,{className:`mono`,style:{textAlign:`right`},children:String(e.run_count??0)})]},e.version))})]})}),(0,w.jsxs)(`div`,{className:`form-actions`,children:[(0,w.jsx)(`button`,{type:`button`,className:`btn`,onClick:l,children:`Close`}),(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:()=>{l(),c(`/queries/${encodeURIComponent(e)}`)},children:`Open in API Repository`})]})]})}var D=(0,C.lazy)(()=>c(()=>import(`./forms-uF5aXx1J.js`).then(e=>({default:e.QueryForm})),__vite__mapDeps([0,1,2,3,4,5,6]))),O={query:`Q`,collection:`C`,connection:`W`},k=[`Database`,`Created`,`Last modified`,`Last used`,`Version`],A=[`database`,`created`,`modified`,`lastUsed`,`version`],j=()=>new Date().toISOString().slice(0,10);function M(){let e=m(),{openDrawer:t}=o(),n=d(),i=f(),a=u(),s=p(),[c,l]=(0,C.useState)(x),g=e=>{let t={...c,...e};_(t),l(t)},v=s.data??[],S=e=>v.find(t=>t.name===e)?.db??null,T=y(c.conn||void 0),O=T.data?.tables??[],k=b(c.conn||void 0,S(c.conn),c.table&&!c.tableMatches?[c.table]:[]),A=c.table?c.tableMatches??(k.data?k.data[c.table]??[]:null):null,j=n.data??[],M=[...new Set(j.map(e=>S(e.connection_name)).filter(e=>!!e))].sort(),P=i.data??{},F=a.data??{},I=Object.keys(P).sort(),L=Object.keys(F).sort(),R,z=!0;c.conn?T.isPending?R=(0,w.jsx)(`option`,{value:``,children:`Loading tables…`}):T.isError?R=(0,w.jsx)(`option`,{value:``,children:`Schema unavailable`}):(z=!1,R=(0,w.jsxs)(w.Fragment,{children:[(0,w.jsx)(`option`,{value:``,children:`All tables (${O.length})`}),O.map(e=>(0,w.jsx)(`option`,{value:e.name,children:`${e.name} · ${e.columns.length}${e.columns.length===1?` col`:` cols`}`},e.name))]})):R=(0,w.jsx)(`option`,{value:``,children:`Select a connection first`});let B=(e,t)=>h(e,t.name,t.collection,t.connection_name),V;if(n.isPending)V=(0,w.jsx)(r,{});else if(!j.length)V=(0,w.jsx)(`div`,{className:`empty`,children:(0,w.jsx)(`strong`,{children:`No saved queries yet`})});else if(!I.length&&!L.length)V=(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`strong`,{children:`No scoped API keys or roles yet`}),(0,w.jsx)(`span`,{children:`Every query is reachable by the admin key only until a scoped key is created.`})]});else{let n=c.q.trim().toLowerCase(),i=[...j].sort((e,t)=>(e.collection||`￿`)+e.name<(t.collection||`￿`)+t.name?-1:1),a=e=>e.toLowerCase().includes(n),o=n?i.filter(e=>a(e.name)||e.collection&&a(e.collection)):i,s=n?I.filter(a):I,l=n?L.filter(a):L,u=o.length||s.length||l.length,d=o.length?o:u?i:[],f=s.length?s:u?I:[],p=l.length?l:u?L:[];if(c.conn&&(d=d.filter(e=>e.connection_name===c.conn)),c.table&&A===null)V=(0,w.jsx)(r,{text:`Checking which queries reference ${c.table}…`});else if(A&&(d=d.filter(e=>A.includes(e.name))),d.length){let n=d.map(e=>({q:e,dbType:S(e.connection_name),reach:f.filter(t=>B(P[t],e).length).length}));c.db&&(n=n.filter(e=>e.dbType===c.db)),c.reach===`reachable`?n=n.filter(e=>e.reach>0):c.reach===`unreachable`&&(n=n.filter(e=>e.reach===0));let r=c.sort;if(r.field){let e=e=>{switch(r.field){case`query`:return e.q.name.toLowerCase();case`database`:return(e.dbType||``).toLowerCase();case`created`:return e.q.created_at||``;case`modified`:return e.q.updated_at||``;case`lastUsed`:return e.q.last_used_at||``;case`version`:return e.q.latest_version;case`connection`:return(e.q.connection_name||``).toLowerCase();case`reach`:return e.reach;case`col`:{let t=r.colType===`key`?P[r.colName]:F[r.colName];return t&&B(t,e.q).length?1:0}default:return``}};n=[...n].sort((t,n)=>{let i=e(t),a=e(n);return i<a?-r.dir:i>a?r.dir:0})}V=n.length?(0,w.jsx)(N,{rows:n,keyCols:f,roleCols:p,keyMap:P,roleMap:F,st:c,onSort:e=>g({sort:e}),onOpen:t=>e(`/queries/${encodeURIComponent(t)}`),onInfo:e=>t({title:e.q.name,kicker:`${e.q.collection||`uncollected`} · ${e.q.version_count}${e.q.version_count===1?` version`:` versions`}`,content:(0,w.jsx)(E,{name:e.q.name,dbType:e.dbType})})}):(0,w.jsx)(`div`,{className:`empty`,children:(0,w.jsx)(`span`,{children:`No queries match these filters.`})})}else{let i=n?`Nothing matches “${n}”.`:c.table?`“${c.table}” on ${c.conn} is not exposed by any saved query yet.`:c.conn?`No saved query uses “${c.conn}” yet.`:`Nothing matches these filters.`;V=(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`span`,{children:i}),!n&&c.conn?(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:()=>{let n=S(c.conn)===`mongo`,i=c.table?n?JSON.stringify({collection:c.table,filter:{}},null,2):`SELECT * FROM `+c.table:void 0;e(`/queries`),t({title:`New API`,kicker:`POST /api/v1/queries`,content:(0,w.jsx)(C.Suspense,{fallback:(0,w.jsx)(r,{}),children:(0,w.jsx)(D,{starter:{connection:c.conn,sql:i}})})})},children:`New API`+(c.table?` on `+c.table:``)}):null]})}}return(0,w.jsxs)(w.Fragment,{children:[(0,w.jsxs)(`div`,{className:`page-head`,children:[(0,w.jsxs)(`div`,{className:`titles`,children:[(0,w.jsx)(`h1`,{children:`Access map`}),(0,w.jsxs)(`span`,{className:`sub`,children:[`Which API keys can reach which saved queries - the whole point of collections and connection grants, in one place.`,` `,(0,w.jsxs)(`span`,{className:`legend`,children:[(0,w.jsx)(`span`,{className:`amap-dot`,children:`Q`}),` named query \xA0`,(0,w.jsx)(`span`,{className:`amap-dot`,children:`C`}),` `,`collection \xA0`,(0,w.jsx)(`span`,{className:`amap-dot conn`,children:`W`}),` whole connection`]})]})]}),(0,w.jsx)(`span`,{className:`spacer`}),(0,w.jsxs)(`select`,{id:`accessmap-conn-filter`,"aria-label":`Connection`,value:v.some(e=>e.name===c.conn)?c.conn:``,onChange:e=>g({conn:e.target.value,table:``,tableMatches:null}),children:[(0,w.jsx)(`option`,{value:``,children:`All connections`}),[...v].sort((e,t)=>e.name<t.name?-1:1).map(e=>(0,w.jsx)(`option`,{value:e.name,children:e.name},e.name))]}),(0,w.jsx)(`select`,{id:`accessmap-table-filter`,"aria-label":`Table`,disabled:z,value:O.some(e=>e.name===c.table)?c.table:``,onChange:e=>g({table:e.target.value,tableMatches:null}),children:R}),(0,w.jsxs)(`select`,{id:`accessmap-db-filter`,"aria-label":`Database`,value:M.includes(c.db)?c.db:``,onChange:e=>g({db:e.target.value}),children:[(0,w.jsx)(`option`,{value:``,children:`All databases`}),M.map(e=>(0,w.jsx)(`option`,{value:e,children:e},e))]}),(0,w.jsxs)(`select`,{id:`accessmap-reach-filter`,"aria-label":`Reach`,value:c.reach,onChange:e=>g({reach:e.target.value}),children:[(0,w.jsx)(`option`,{value:``,children:`Any reach`}),(0,w.jsx)(`option`,{value:`reachable`,children:`Reachable by a key`}),(0,w.jsx)(`option`,{value:`unreachable`,children:`Reachable by no key`})]}),(0,w.jsx)(`input`,{id:`accessmap-filter`,className:`search`,type:`search`,placeholder:`Filter by query or key…`,value:c.q,onChange:e=>g({q:e.target.value})})]}),(0,w.jsx)(`div`,{id:`accessmap-body`,className:`panel`,children:V})]})}function N({rows:e,keyCols:t,roleCols:n,keyMap:r,roleMap:i,st:a,onSort:o,onOpen:s,onInfo:c}){let l=a.sort,u=(e,t=null,n=null)=>l.field===e&&l.colType===t&&l.colName===n,d=(e,t=null,n=null)=>u(e,t,n)?l.dir===1?` ▲`:` ▼`:``,f=(e,t=null,n=null)=>{let r=u(e,t,n);o(r&&l.dir===1?{field:e,colType:t,colName:n,dir:-1}:r?{field:null,colType:null,colName:null,dir:1}:{field:e,colType:t,colName:n,dir:1})},p=(e,t,n)=>(0,w.jsx)(`th`,{className:t,onClick:()=>f(n),children:e+d(n)},n),m=(e,t,n,r)=>(0,w.jsxs)(`th`,{className:`amap-key-head amap-sortable`+(n?` revoked`:``),title:n?e+n:e,onClick:()=>f(`col`,r,e),children:[(0,w.jsxs)(`span`,{className:`amap-key-name`,children:[e,d(`col`,r,e),n?(0,w.jsx)(`span`,{className:`dim`,children:n}):null]}),(0,w.jsxs)(`span`,{className:`amap-key-sub`,children:[(t.allow_writes?`Read/write`:`Read-only`)+` · `+(t.rate_limit||`server default`),t.mask?.length?(0,w.jsx)(`span`,{className:`tag`,title:t.mask.map(e=>`${e.column}: ${e.action}`).join(`, `),children:`masked`}):null]})]},r+`:`+e),_=(e,t,n,r)=>{let i=h(e,t.name,t.collection,t.connection_name);if(!i.length)return(0,w.jsx)(`td`,{className:`amap-cell`},r);let a=[...new Set(i.map(e=>O[e.kind]))],o=a.length===1&&a[0]===`W`;return(0,w.jsx)(`td`,{className:`amap-cell`,title:i.map(e=>e.label).join(`, `),children:(0,w.jsx)(`span`,{className:`amap-dot`+(n?` role`:``)+(o?` conn`:``),children:a.join(`·`)})},r)},v=j();return(0,w.jsx)(`div`,{style:{overflow:`auto`},children:(0,w.jsxs)(`table`,{className:`amap`,children:[(0,w.jsxs)(`thead`,{children:[(0,w.jsxs)(`tr`,{children:[(0,w.jsx)(`th`,{colSpan:3+k.length}),t.length?(0,w.jsx)(`th`,{colSpan:t.length,className:`amap-group`,children:`API KEYS`}):null,n.length?(0,w.jsx)(`th`,{colSpan:n.length,className:`amap-group role`,title:`What a key created from each role would reach - a role grants nothing on its own`,children:`ROLES`}):null]}),(0,w.jsxs)(`tr`,{children:[p(`Query`,`amap-query amap-sortable`,`query`),A.map((e,t)=>p(k[t],`amap-sortable`,e)),p(`Connection`,`amap-sortable`,`connection`),p(`Reach`,`num amap-sortable`,`reach`),t.map(e=>{let t=r[e],n=!!(t.expires_at&&t.expires_at<v);return m(e,t,t.active?n?` (expired ${t.expires_at})`:``:` (revoked)`,`key`)}),n.map(e=>m(e,i[e],``,`role`))]})]}),(0,w.jsx)(`tbody`,{children:e.map(e=>{let a=e.q;return(0,w.jsxs)(`tr`,{children:[(0,w.jsxs)(`td`,{className:`amap-query`,children:[(0,w.jsxs)(`div`,{className:`amap-q-row`,children:[(0,w.jsx)(`button`,{type:`button`,className:`amap-q-name`,title:`Open ${a.name} in API Repository`,onClick:()=>s(a.name),children:a.name}),(0,w.jsx)(`button`,{type:`button`,className:`amap-info`,title:`Details for ${a.name}`,"aria-label":`Details for ${a.name}`,onClick:()=>c(e),children:`i`})]}),a.collection?(0,w.jsx)(`span`,{className:`amap-coll`,children:a.collection}):null]}),(0,w.jsx)(`td`,{children:e.dbType?(0,w.jsx)(`span`,{className:`tag`,children:e.dbType}):(0,w.jsx)(`span`,{className:`dim`,children:`—`})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(g,{value:a.created_at})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(g,{value:a.updated_at})}),(0,w.jsx)(`td`,{className:a.last_used_at?`mono`:`mono dim`,children:(0,w.jsx)(g,{value:a.last_used_at,fallback:`never`})}),(0,w.jsx)(`td`,{className:`mono`,children:`v`+a.latest_version}),(0,w.jsx)(`td`,{className:`mono dim`,children:a.connection_name||`—`}),(0,w.jsx)(`td`,{className:`num`,title:`Reachable by an actual API key (roles alone reach nothing)`,children:e.reach?String(e.reach):`—`}),t.map(e=>_(r[e],a,!1,`k:`+e)),n.map(e=>_(i[e],a,!0,`r:`+e))]},a.name)})}),(0,w.jsx)(`tfoot`,{children:(0,w.jsxs)(`tr`,{children:[(0,w.jsx)(`td`,{className:`amap-query`,children:`Reaches`}),k.map(e=>(0,w.jsx)(`td`,{},e)),(0,w.jsx)(`td`,{}),(0,w.jsx)(`td`,{}),t.map(t=>(0,w.jsx)(`td`,{className:`num`,children:`${e.filter(e=>h(r[t],e.q.name,e.q.collection,e.q.connection_name).length).length} / ${e.length}`},t)),n.map(t=>(0,w.jsx)(`td`,{className:`num`,children:`${e.filter(e=>h(i[t],e.q.name,e.q.collection,e.q.connection_name).length).length} / ${e.length}`},t))]})})]})})}export{M as AccessMapPage};
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/forms-C5rX5RT9.js","assets/index-Efe2sJ9A.js","assets/index-BLd0t6vZ.css","assets/SchemaBrowser-B2wMn0QD.js","assets/useMutation-ptto6u1J.js","assets/reach-Jb8SEj5E.js","assets/CodeBox-B933gn9I.js"])))=>i.map(i=>d[i]);
2
+ import{$ as e,C as t,D as n,F as r,H as i,I as a,L as o,Q as s,V as c,Z as l,h as u,o as d,s as f,u as p,z as m}from"./index-Efe2sJ9A.js";import{a as h}from"./reach-Jb8SEj5E.js";import{t as g}from"./Time-BMys0hNR.js";import{f as _,i as v,n as y,r as b,u as x}from"./SchemaBrowser-B2wMn0QD.js";import{t as S}from"./CodeBox-B933gn9I.js";var C=e(s(),1),w=l();function T({term:e,children:t}){return(0,w.jsxs)(`div`,{children:[(0,w.jsx)(`dt`,{children:e}),(0,w.jsx)(`dd`,{title:typeof t==`string`?t:void 0,children:t})]})}function E({name:e,dbType:s}){let c=m(),{closeDrawer:l,toast:u}=o(),d=i({queryKey:[`queries`,`detail`,e,`info`],queryFn:async()=>n(await t.GET(`/api/v1/queries/{name}`,{params:{path:{name:e}}})),retry:!1}),f=d.data,p=f?.versions[f.versions.length-1],h=p?.query_type===`mongo`?JSON.stringify(p.mongo??{},null,2):p?.sql??``,_=i({queryKey:[`queries`,`flow`,e,p?.version],queryFn:()=>v(e,p.version),enabled:!(!p||p.query_type!==`sql`||h.includes(`
3
+ `)),staleTime:1/0});if(d.isPending)return(0,w.jsx)(r,{});if(!f||!p)return(0,w.jsx)(`div`,{className:`hint`,children:`Could not load the SQL.`});let y=_.data?.formatted||h,b=f.created_at,x=f.last_used_at;return(0,w.jsxs)(`div`,{className:`form`,children:[p.description?(0,w.jsx)(`p`,{className:`d-desc`,children:p.description}):null,(0,w.jsxs)(`dl`,{className:`meta`,children:[(0,w.jsx)(T,{term:`connection`,children:p.connection_name||`—`}),(0,w.jsx)(T,{term:`database`,children:s?(0,w.jsx)(`span`,{className:`tag`,children:s}):(0,w.jsx)(`span`,{className:`dim`,children:`—`})}),(0,w.jsx)(T,{term:`collection`,children:f.collection||`—`}),(0,w.jsx)(T,{term:`status`,children:p.status||`—`}),(0,w.jsx)(T,{term:`author`,children:p.author||`—`}),(0,w.jsx)(T,{term:`created`,children:(0,w.jsx)(g,{value:b})}),(0,w.jsx)(T,{term:`last modified`,children:(0,w.jsx)(g,{value:p.last_modified_at||p.created_at})}),(0,w.jsx)(T,{term:`last used`,children:(0,w.jsx)(g,{value:x,fallback:`never`})}),p.tags.length?(0,w.jsx)(T,{term:`tags`,children:p.tags.join(`, `)}):null]}),(0,w.jsx)(`p`,{className:`sub-h`,style:{marginTop:6},children:`SQL · v${p.version}${f.versions.length>1?` (latest)`:``}`}),(0,w.jsx)(`div`,{children:_.isFetching?(0,w.jsx)(r,{text:`Loading SQL…`}):(0,w.jsxs)(w.Fragment,{children:[(0,w.jsx)(S,{sql:y}),(0,w.jsx)(`div`,{style:{marginTop:6},children:(0,w.jsx)(`button`,{type:`button`,className:`btn sm ghost`,onClick:()=>a(y,u),children:`Copy SQL`})})]})}),(0,w.jsx)(`p`,{className:`sub-h`,style:{marginTop:6},children:`Versions`}),(0,w.jsx)(`div`,{className:`panel`,style:{overflow:`auto`},children:(0,w.jsxs)(`table`,{className:`grid qi-versions`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsx)(`tr`,{children:[`Version`,`Created`,`Last modified`,`Runs`].map(e=>(0,w.jsx)(`th`,{children:e},e))})}),(0,w.jsx)(`tbody`,{children:[...f.versions].reverse().map(e=>(0,w.jsxs)(`tr`,{children:[(0,w.jsx)(`td`,{className:`mono`,children:`v`+e.version}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(g,{value:e.created_at})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(g,{value:e.last_modified_at})}),(0,w.jsx)(`td`,{className:`mono`,style:{textAlign:`right`},children:String(e.run_count??0)})]},e.version))})]})}),(0,w.jsxs)(`div`,{className:`form-actions`,children:[(0,w.jsx)(`button`,{type:`button`,className:`btn`,onClick:l,children:`Close`}),(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:()=>{l(),c(`/queries/${encodeURIComponent(e)}`)},children:`Open in API Repository`})]})]})}var D=(0,C.lazy)(()=>c(()=>import(`./forms-C5rX5RT9.js`).then(e=>({default:e.QueryForm})),__vite__mapDeps([0,1,2,3,4,5,6]))),O={query:`Q`,collection:`C`,connection:`W`},k=[`Database`,`Created`,`Last modified`,`Last used`,`Version`],A=[`database`,`created`,`modified`,`lastUsed`,`version`],j=()=>new Date().toISOString().slice(0,10);function M(){let e=m(),{openDrawer:t}=o(),n=d(),i=f(),a=u(),s=p(),[c,l]=(0,C.useState)(x),g=e=>{let t={...c,...e};_(t),l(t)},v=s.data??[],S=e=>v.find(t=>t.name===e)?.db??null,T=y(c.conn||void 0),O=T.data?.tables??[],k=b(c.conn||void 0,S(c.conn),c.table&&!c.tableMatches?[c.table]:[]),A=c.table?c.tableMatches??(k.data?k.data[c.table]??[]:null):null,j=n.data??[],M=[...new Set(j.map(e=>S(e.connection_name)).filter(e=>!!e))].sort(),P=i.data??{},F=a.data??{},I=Object.keys(P).sort(),L=Object.keys(F).sort(),R,z=!0;c.conn?T.isPending?R=(0,w.jsx)(`option`,{value:``,children:`Loading tables…`}):T.isError?R=(0,w.jsx)(`option`,{value:``,children:`Schema unavailable`}):(z=!1,R=(0,w.jsxs)(w.Fragment,{children:[(0,w.jsx)(`option`,{value:``,children:`All tables (${O.length})`}),O.map(e=>(0,w.jsx)(`option`,{value:e.name,children:`${e.name} · ${e.columns.length}${e.columns.length===1?` col`:` cols`}`},e.name))]})):R=(0,w.jsx)(`option`,{value:``,children:`Select a connection first`});let B=(e,t)=>h(e,t.name,t.collection,t.connection_name),V;if(n.isPending)V=(0,w.jsx)(r,{});else if(!j.length)V=(0,w.jsx)(`div`,{className:`empty`,children:(0,w.jsx)(`strong`,{children:`No saved queries yet`})});else if(!I.length&&!L.length)V=(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`strong`,{children:`No scoped API keys or roles yet`}),(0,w.jsx)(`span`,{children:`Every query is reachable by the admin key only until a scoped key is created.`})]});else{let n=c.q.trim().toLowerCase(),i=[...j].sort((e,t)=>(e.collection||`￿`)+e.name<(t.collection||`￿`)+t.name?-1:1),a=e=>e.toLowerCase().includes(n),o=n?i.filter(e=>a(e.name)||e.collection&&a(e.collection)):i,s=n?I.filter(a):I,l=n?L.filter(a):L,u=o.length||s.length||l.length,d=o.length?o:u?i:[],f=s.length?s:u?I:[],p=l.length?l:u?L:[];if(c.conn&&(d=d.filter(e=>e.connection_name===c.conn)),c.table&&A===null)V=(0,w.jsx)(r,{text:`Checking which queries reference ${c.table}…`});else if(A&&(d=d.filter(e=>A.includes(e.name))),d.length){let n=d.map(e=>({q:e,dbType:S(e.connection_name),reach:f.filter(t=>B(P[t],e).length).length}));c.db&&(n=n.filter(e=>e.dbType===c.db)),c.reach===`reachable`?n=n.filter(e=>e.reach>0):c.reach===`unreachable`&&(n=n.filter(e=>e.reach===0));let r=c.sort;if(r.field){let e=e=>{switch(r.field){case`query`:return e.q.name.toLowerCase();case`database`:return(e.dbType||``).toLowerCase();case`created`:return e.q.created_at||``;case`modified`:return e.q.updated_at||``;case`lastUsed`:return e.q.last_used_at||``;case`version`:return e.q.latest_version;case`connection`:return(e.q.connection_name||``).toLowerCase();case`reach`:return e.reach;case`col`:{let t=r.colType===`key`?P[r.colName]:F[r.colName];return t&&B(t,e.q).length?1:0}default:return``}};n=[...n].sort((t,n)=>{let i=e(t),a=e(n);return i<a?-r.dir:i>a?r.dir:0})}V=n.length?(0,w.jsx)(N,{rows:n,keyCols:f,roleCols:p,keyMap:P,roleMap:F,st:c,onSort:e=>g({sort:e}),onOpen:t=>e(`/queries/${encodeURIComponent(t)}`),onInfo:e=>t({title:e.q.name,kicker:`${e.q.collection||`uncollected`} · ${e.q.version_count}${e.q.version_count===1?` version`:` versions`}`,content:(0,w.jsx)(E,{name:e.q.name,dbType:e.dbType})})}):(0,w.jsx)(`div`,{className:`empty`,children:(0,w.jsx)(`span`,{children:`No queries match these filters.`})})}else{let i=n?`Nothing matches “${n}”.`:c.table?`“${c.table}” on ${c.conn} is not exposed by any saved query yet.`:c.conn?`No saved query uses “${c.conn}” yet.`:`Nothing matches these filters.`;V=(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`span`,{children:i}),!n&&c.conn?(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:()=>{let n=S(c.conn)===`mongo`,i=c.table?n?JSON.stringify({collection:c.table,filter:{}},null,2):`SELECT * FROM `+c.table:void 0;e(`/queries`),t({title:`New API`,kicker:`POST /api/v1/queries`,content:(0,w.jsx)(C.Suspense,{fallback:(0,w.jsx)(r,{}),children:(0,w.jsx)(D,{starter:{connection:c.conn,sql:i}})})})},children:`New API`+(c.table?` on `+c.table:``)}):null]})}}return(0,w.jsxs)(w.Fragment,{children:[(0,w.jsxs)(`div`,{className:`page-head`,children:[(0,w.jsxs)(`div`,{className:`titles`,children:[(0,w.jsx)(`h1`,{children:`Access map`}),(0,w.jsxs)(`span`,{className:`sub`,children:[`Which API keys can reach which saved queries - the whole point of collections and connection grants, in one place.`,` `,(0,w.jsxs)(`span`,{className:`legend`,children:[(0,w.jsx)(`span`,{className:`amap-dot`,children:`Q`}),` named query \xA0`,(0,w.jsx)(`span`,{className:`amap-dot`,children:`C`}),` `,`collection \xA0`,(0,w.jsx)(`span`,{className:`amap-dot conn`,children:`W`}),` whole connection`]})]})]}),(0,w.jsx)(`span`,{className:`spacer`}),(0,w.jsxs)(`select`,{id:`accessmap-conn-filter`,"aria-label":`Connection`,value:v.some(e=>e.name===c.conn)?c.conn:``,onChange:e=>g({conn:e.target.value,table:``,tableMatches:null}),children:[(0,w.jsx)(`option`,{value:``,children:`All connections`}),[...v].sort((e,t)=>e.name<t.name?-1:1).map(e=>(0,w.jsx)(`option`,{value:e.name,children:e.name},e.name))]}),(0,w.jsx)(`select`,{id:`accessmap-table-filter`,"aria-label":`Table`,disabled:z,value:O.some(e=>e.name===c.table)?c.table:``,onChange:e=>g({table:e.target.value,tableMatches:null}),children:R}),(0,w.jsxs)(`select`,{id:`accessmap-db-filter`,"aria-label":`Database`,value:M.includes(c.db)?c.db:``,onChange:e=>g({db:e.target.value}),children:[(0,w.jsx)(`option`,{value:``,children:`All databases`}),M.map(e=>(0,w.jsx)(`option`,{value:e,children:e},e))]}),(0,w.jsxs)(`select`,{id:`accessmap-reach-filter`,"aria-label":`Reach`,value:c.reach,onChange:e=>g({reach:e.target.value}),children:[(0,w.jsx)(`option`,{value:``,children:`Any reach`}),(0,w.jsx)(`option`,{value:`reachable`,children:`Reachable by a key`}),(0,w.jsx)(`option`,{value:`unreachable`,children:`Reachable by no key`})]}),(0,w.jsx)(`input`,{id:`accessmap-filter`,className:`search`,type:`search`,placeholder:`Filter by query or key…`,value:c.q,onChange:e=>g({q:e.target.value})})]}),(0,w.jsx)(`div`,{id:`accessmap-body`,className:`panel`,children:V})]})}function N({rows:e,keyCols:t,roleCols:n,keyMap:r,roleMap:i,st:a,onSort:o,onOpen:s,onInfo:c}){let l=a.sort,u=(e,t=null,n=null)=>l.field===e&&l.colType===t&&l.colName===n,d=(e,t=null,n=null)=>u(e,t,n)?l.dir===1?` ▲`:` ▼`:``,f=(e,t=null,n=null)=>{let r=u(e,t,n);o(r&&l.dir===1?{field:e,colType:t,colName:n,dir:-1}:r?{field:null,colType:null,colName:null,dir:1}:{field:e,colType:t,colName:n,dir:1})},p=(e,t,n)=>(0,w.jsx)(`th`,{className:t,onClick:()=>f(n),children:e+d(n)},n),m=(e,t,n,r)=>(0,w.jsxs)(`th`,{className:`amap-key-head amap-sortable`+(n?` revoked`:``),title:n?e+n:e,onClick:()=>f(`col`,r,e),children:[(0,w.jsxs)(`span`,{className:`amap-key-name`,children:[e,d(`col`,r,e),n?(0,w.jsx)(`span`,{className:`dim`,children:n}):null]}),(0,w.jsxs)(`span`,{className:`amap-key-sub`,children:[(t.allow_writes?`Read/write`:`Read-only`)+` · `+(t.rate_limit||`server default`),t.mask?.length?(0,w.jsx)(`span`,{className:`tag`,title:t.mask.map(e=>`${e.column}: ${e.action}`).join(`, `),children:`masked`}):null]})]},r+`:`+e),_=(e,t,n,r)=>{let i=h(e,t.name,t.collection,t.connection_name);if(!i.length)return(0,w.jsx)(`td`,{className:`amap-cell`},r);let a=[...new Set(i.map(e=>O[e.kind]))],o=a.length===1&&a[0]===`W`;return(0,w.jsx)(`td`,{className:`amap-cell`,title:i.map(e=>e.label).join(`, `),children:(0,w.jsx)(`span`,{className:`amap-dot`+(n?` role`:``)+(o?` conn`:``),children:a.join(`·`)})},r)},v=j();return(0,w.jsx)(`div`,{style:{overflow:`auto`},children:(0,w.jsxs)(`table`,{className:`amap`,children:[(0,w.jsxs)(`thead`,{children:[(0,w.jsxs)(`tr`,{children:[(0,w.jsx)(`th`,{colSpan:3+k.length}),t.length?(0,w.jsx)(`th`,{colSpan:t.length,className:`amap-group`,children:`API KEYS`}):null,n.length?(0,w.jsx)(`th`,{colSpan:n.length,className:`amap-group role`,title:`What a key created from each role would reach - a role grants nothing on its own`,children:`ROLES`}):null]}),(0,w.jsxs)(`tr`,{children:[p(`Query`,`amap-query amap-sortable`,`query`),A.map((e,t)=>p(k[t],`amap-sortable`,e)),p(`Connection`,`amap-sortable`,`connection`),p(`Reach`,`num amap-sortable`,`reach`),t.map(e=>{let t=r[e],n=!!(t.expires_at&&t.expires_at<v);return m(e,t,t.active?n?` (expired ${t.expires_at})`:``:` (revoked)`,`key`)}),n.map(e=>m(e,i[e],``,`role`))]})]}),(0,w.jsx)(`tbody`,{children:e.map(e=>{let a=e.q;return(0,w.jsxs)(`tr`,{children:[(0,w.jsxs)(`td`,{className:`amap-query`,children:[(0,w.jsxs)(`div`,{className:`amap-q-row`,children:[(0,w.jsx)(`button`,{type:`button`,className:`amap-q-name`,title:`Open ${a.name} in API Repository`,onClick:()=>s(a.name),children:a.name}),(0,w.jsx)(`button`,{type:`button`,className:`amap-info`,title:`Details for ${a.name}`,"aria-label":`Details for ${a.name}`,onClick:()=>c(e),children:`i`})]}),a.collection?(0,w.jsx)(`span`,{className:`amap-coll`,children:a.collection}):null]}),(0,w.jsx)(`td`,{children:e.dbType?(0,w.jsx)(`span`,{className:`tag`,children:e.dbType}):(0,w.jsx)(`span`,{className:`dim`,children:`—`})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(g,{value:a.created_at})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(g,{value:a.updated_at})}),(0,w.jsx)(`td`,{className:a.last_used_at?`mono`:`mono dim`,children:(0,w.jsx)(g,{value:a.last_used_at,fallback:`never`})}),(0,w.jsx)(`td`,{className:`mono`,children:`v`+a.latest_version}),(0,w.jsx)(`td`,{className:`mono dim`,children:a.connection_name||`—`}),(0,w.jsx)(`td`,{className:`num`,title:`Reachable by an actual API key (roles alone reach nothing)`,children:e.reach?String(e.reach):`—`}),t.map(e=>_(r[e],a,!1,`k:`+e)),n.map(e=>_(i[e],a,!0,`r:`+e))]},a.name)})}),(0,w.jsx)(`tfoot`,{children:(0,w.jsxs)(`tr`,{children:[(0,w.jsx)(`td`,{className:`amap-query`,children:`Reaches`}),k.map(e=>(0,w.jsx)(`td`,{},e)),(0,w.jsx)(`td`,{}),(0,w.jsx)(`td`,{}),t.map(t=>(0,w.jsx)(`td`,{className:`num`,children:`${e.filter(e=>h(r[t],e.q.name,e.q.collection,e.q.connection_name).length).length} / ${e.length}`},t)),n.map(t=>(0,w.jsx)(`td`,{className:`num`,children:`${e.filter(e=>h(i[t],e.q.name,e.q.collection,e.q.connection_name).length).length} / ${e.length}`},t))]})})]})})}export{M as AccessMapPage};
@@ -1 +1 @@
1
- import{t as e}from"./useMutation-DGd9BCI3.js";import{$ as t,A as n,C as r,D as i,F as a,H as o,L as s,N as c,O as l,P as u,Q as d,S as f,X as p,Z as m,c as h,d as g,k as _,m as v}from"./index-DMiPsI_5.js";import{t as y}from"./Time-yK6sDFr-.js";import{t as b}from"./forms-6-RLlXrl.js";import{n as x,t as S}from"./icons-BPJHnFc_.js";var C=t(d(),1),w=m(),T=[{value:`owner`,label:`Owner`,text:`everything, including administrators`},{value:`admin`,label:`Admin`,text:`connections, API keys and roles, queries, cache, settings`},{value:`developer`,label:`Developer`,text:`saved queries and SQL; no keys, no connection changes`},{value:`auditor`,label:`Auditor`,text:`read only - audit log, history, configuration; no SQL`}],E=e=>T.find(t=>t.value===e)?.label??e,D=90,O=e=>new Date(Date.now()+e*864e5).toISOString().slice(0,10),k=`Store this now — it cannot be shown again. Paste it into the API key box at the bottom of the sidebar, or send it as the X-API-Key header.`;function A(){let{showError:e}=s();return t=>t instanceof f?e(t.message,{status:t.status}):e(t.message)}function j(e){let t=g(`admins.read`,`other administrators`);return o({queryKey:[`administrators`],queryFn:async()=>(await t.check(),i(await r.GET(`/api/v1/administrators`)).items),enabled:e,retry:!1})}function M(e){return o({queryKey:[`administrators`,e,`tokens`],queryFn:async()=>i(await r.GET(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:e}}})).items,retry:!1})}function N(){let e=v(),t=h(),n=!!e.data&&t(`admins.read`),r=j(n),{openDrawer:i}=s(),o=()=>i({title:`New administrator`,content:(0,w.jsx)(I,{})}),c;return c=e.isPending?(0,w.jsx)(a,{}):e.isError?(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`strong`,{children:`Not signed in as an administrator`}),(0,w.jsx)(`span`,{children:`Apply an admin token (qagadm_…) or the shared admin key in the box at the bottom of the sidebar.`})]}):n?r.isPending?(0,w.jsx)(a,{text:`Loading administrators…`}):r.isError?(0,w.jsx)(`div`,{className:`hint`,children:r.error.message}):r.data.length?(0,w.jsx)(P,{items:r.data,self:e.data.via===`token`||e.data.via===`sso`?e.data.name:null}):(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`strong`,{children:`No administrators yet`}),(0,w.jsx)(`span`,{children:`Everyone shares QUERYAPIGATE_API_KEY today, so the audit log can only say “admin”. Give each person their own sign-in, with a role that fits what they do — then the shared key becomes a break-glass key.`}),(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:o,children:`New administrator`})]}):(0,w.jsx)(F,{name:e.data.name??``,role:e.data.role}),(0,w.jsxs)(w.Fragment,{children:[(0,w.jsxs)(`div`,{className:`page-head`,children:[(0,w.jsxs)(`div`,{className:`titles`,children:[(0,w.jsx)(`h1`,{children:`Administrators`}),(0,w.jsx)(`span`,{className:`sub`,id:`administrators-sub`,children:n?`The people and pipelines that manage this server, each with a role and their own tokens.`:`Your account and your admin tokens.`})]}),(0,w.jsx)(`span`,{className:`spacer`}),n&&(0,w.jsx)(`button`,{id:`new-administrator`,type:`button`,className:`btn primary`,onClick:o,children:`New administrator`})]}),(0,w.jsx)(`div`,{id:`administrators-table`,className:`panel`,children:c})]})}function P({items:e,self:t}){let{openDrawer:n,toast:i}=s(),a=A(),o=p(),c=async e=>{if(window.confirm(`Remove administrator '${e}'? Every token of theirs stops working immediately.`))try{l(await r.DELETE(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),i(`Removed `+e),o.invalidateQueries({queryKey:[`administrators`]})}catch(e){a(e)}};return(0,w.jsx)(`div`,{style:{overflowX:`auto`},children:(0,w.jsxs)(`table`,{className:`grid`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsx)(`tr`,{children:[`Name`,`Role`,`Email`,`Last seen`,`Tokens`,``].map((e,t)=>(0,w.jsx)(`th`,{children:e},t))})}),(0,w.jsx)(`tbody`,{children:e.map(e=>(0,w.jsxs)(`tr`,{"data-name":e.name,children:[(0,w.jsx)(`td`,{children:(0,w.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,w.jsx)(`span`,{className:`dot ${e.active?`ok`:`off`}`,title:e.active?`Active`:`Inactive`}),(0,w.jsx)(`span`,{className:`name`,children:e.name}),e.name===t&&(0,w.jsx)(`span`,{className:`tag`,children:`you`}),e.sso?.status===`linked`&&(0,w.jsx)(`span`,{className:`tag`,title:`Signs in through ${e.sso.issuer??`SSO`}; role from their groups`,children:`SSO`}),e.sso?.status===`pending`&&(0,w.jsx)(`span`,{className:`tag`,title:`Linked at their next SSO sign-in, by their verified email`,children:`SSO pending`})]})}),(0,w.jsx)(`td`,{children:E(e.role)}),(0,w.jsx)(`td`,{className:e.email?void 0:`dim`,children:e.email??`—`}),(0,w.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,w.jsx)(y,{value:e.last_seen_at,fallback:`never`})}),(0,w.jsx)(`td`,{className:`mono`,children:e.tokens}),(0,w.jsx)(`td`,{children:(0,w.jsxs)(`div`,{className:`actions`,children:[(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>n({title:`Admin tokens`,kicker:e.name,wide:!0,content:(0,w.jsx)(R,{name:e.name})}),children:`Tokens`}),(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm icon`,title:`Edit ${e.name}`,"aria-label":`Edit`,onClick:()=>n({title:`Edit administrator`,kicker:e.name,content:(0,w.jsx)(I,{name:e.name})}),children:(0,w.jsx)(S,{})}),(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm icon danger`,title:`Remove ${e.name}`,"aria-label":`Remove`,onClick:()=>void c(e.name),children:(0,w.jsx)(x,{})})]})})]},e.name))})]})})}function F({name:e,role:t}){return(0,w.jsxs)(`div`,{className:`form`,style:{padding:16},children:[(0,w.jsxs)(`div`,{children:[`Signed in as `,(0,w.jsx)(`strong`,{children:e}),` · `,E(t),` — `,T.find(e=>e.value===t)?.text,`.`]}),(0,w.jsx)(R,{name:e})]})}function I({name:e}){let t=o({queryKey:[`administrators`,`detail`,e],queryFn:async()=>_(await r.GET(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),enabled:!!e,retry:!1,gcTime:0});return e&&t.isPending?(0,w.jsx)(a,{}):e&&t.isError?(0,w.jsx)(`div`,{className:`hint`,children:t.error.message}):(0,w.jsx)(L,{name:e,existing:t.data?.data,etag:t.data?.etag??null},e??``)}function L({name:t,existing:a,etag:o}){let l=!!t,{closeDrawer:d,openDrawer:f,showError:m,toast:h}=s(),g=A(),_=p(),[v,y]=(0,C.useState)(t??``),[x,S]=(0,C.useState)(a?.role??`developer`),[E,j]=(0,C.useState)(a?.email??``),[M,N]=(0,C.useState)(!a||a.active),P=n(),F=a?.sso?.status===`linked`,[I,L]=(0,C.useState)(a?.sso?.status??`none`),R=e({mutationFn:async()=>{let e={role:x,email:E.trim()||null};if(l)return i(await r.PATCH(`/api/v1/administrators/{name}`,{params:{path:{name:t},header:o?{"If-Match":o}:{}},body:{...F?{email:e.email}:e,active:M,...I===(a?.sso?.status??`none`)?{}:{sso:I===`pending`?`pending`:null}}})),null;let n=i(await r.POST(`/api/v1/administrators`,{body:{name:v.trim(),...e}}));return i(await r.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:n.name}},body:{label:`first token`,expires_at:O(D)}}))},onSuccess:e=>{if(m(``),_.invalidateQueries({queryKey:[`administrators`]}),!e){d(),h(`Updated `+t);return}f({title:`Administrator created`,kicker:e.admin,content:(0,w.jsx)(b,{secret:e.secret,label:`Admin token for ${e.admin} (expires ${e.expires_at})`,hint:k})})},onError:g});return(0,w.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{if(e.preventDefault(),!l&&!v.trim()){m(`A name is required.`,{errors:{name:`This field is required`}});return}R.mutate()},children:[(0,w.jsx)(c,{id:`a-name`,label:`Name`,hint:l?void 0:`A person (alice) or a pipeline (ci-deploy). Shown in the audit log.`,children:(0,w.jsx)(`input`,{id:`a-name`,value:v,placeholder:`alice`,autoComplete:`off`,spellCheck:!1,disabled:l,autoFocus:!l,onChange:e=>y(e.target.value)})}),(0,w.jsx)(c,{id:`a-role`,label:`Role`,hint:F?`From their groups at the identity provider, at every sign-in - change it there.`:T.find(e=>e.value===x)?.text,children:(0,w.jsx)(`select`,{id:`a-role`,value:x,disabled:F,onChange:e=>S(e.target.value),children:T.map(e=>(0,w.jsx)(`option`,{value:e.value,children:e.label},e.value))})}),(0,w.jsx)(c,{id:`a-email`,label:`Email`,hint:`Optional.`,children:(0,w.jsx)(`input`,{id:`a-email`,type:`email`,value:E,autoComplete:`off`,onChange:e=>j(e.target.value)})}),l&&(0,w.jsxs)(`label`,{className:`switch`,children:[(0,w.jsx)(`input`,{id:`a-active`,type:`checkbox`,checked:M,onChange:e=>N(e.target.checked)}),`Active`,(0,w.jsx)(`span`,{className:`hint`,children:`— unchecking stops every token of theirs immediately`})]}),l&&P.data?.sso&&(0,w.jsx)(c,{id:`a-sso`,label:`Sign-in through SSO`,hint:I===`linked`?`They sign in through the identity provider. Unlinking ends their sessions; their next SSO sign-in makes a new administrator.`:`Pending: their next SSO sign-in, with a verified email matching the one above, is bound to this account - once.`,children:(0,w.jsxs)(`select`,{id:`a-sso`,value:I,onChange:e=>L(e.target.value),children:[F&&(0,w.jsx)(`option`,{value:`linked`,children:`Linked`}),!F&&(0,w.jsx)(`option`,{value:`pending`,children:`Link at their next sign-in`}),(0,w.jsx)(`option`,{value:`none`,children:F?`Unlink`:`Not through SSO`})]})}),!l&&(0,w.jsxs)(`div`,{className:`hint`,children:[`Their first admin token, valid for `,D,` days, is shown once after you create them.`]}),(0,w.jsx)(u,{onCancel:d,children:(0,w.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:R.isPending,children:l?`Save`:`Create`})})]})}function R({name:t}){let n=M(t),o=p(),{toast:u}=s(),d=A(),[f,m]=(0,C.useState)(``),[h,g]=(0,C.useState)(O(D)),[_,v]=(0,C.useState)(null),x=()=>void o.invalidateQueries({queryKey:[`administrators`]}),S=e({mutationFn:async()=>i(await r.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:t}},body:{label:f.trim()||null,expires_at:h||null}})),onSuccess:e=>{v(e),m(``),x()},onError:d}),T=async e=>{if(window.confirm(`Revoke ${e.label?`'${e.label}'`:e.id}? Anything using it stops working now.`))try{l(await r.DELETE(`/api/v1/administrators/{name}/tokens/{token_id}`,{params:{path:{name:t,token_id:e.id}}})),u(`Revoked `+(e.label??e.id)),x()}catch(e){d(e)}},E=new Date().toISOString().slice(0,10);return(0,w.jsxs)(`div`,{className:`form`,id:`admin-tokens`,children:[n.isPending?(0,w.jsx)(a,{text:`Loading tokens…`}):n.isError?(0,w.jsx)(`div`,{className:`hint`,children:n.error.message}):n.data.length?(0,w.jsxs)(`table`,{className:`grid`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsx)(`tr`,{children:[`Label`,`Expires`,`Last used`,``].map((e,t)=>(0,w.jsx)(`th`,{children:e},t))})}),(0,w.jsx)(`tbody`,{children:n.data.map(e=>(0,w.jsxs)(`tr`,{"data-token":e.id,children:[(0,w.jsx)(`td`,{title:e.id,children:e.label??(0,w.jsx)(`span`,{className:`dim mono`,children:e.id})}),(0,w.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:e.expires_at?(0,w.jsx)(`span`,{style:e.expired?{color:`var(--danger)`}:void 0,children:e.expires_at+(e.expired?` (expired)`:e.expires_at===E?` (today)`:``)}):(0,w.jsx)(`span`,{className:`dim`,children:`never`})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(y,{value:e.last_used_at,fallback:`never`})}),(0,w.jsx)(`td`,{children:(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm danger`,onClick:()=>void T(e),children:`Revoke`})})]},e.id))})]}):(0,w.jsx)(`div`,{className:`hint`,children:`No tokens. Issue one below to sign in with.`}),_?(0,w.jsx)(b,{secret:_.secret,label:`New token${_.label?` '${_.label}'`:``} (${_.expires_at?`expires ${_.expires_at}`:`never expires`})`,hint:k,onDone:()=>v(null)}):(0,w.jsxs)(`form`,{className:`grid2`,noValidate:!0,onSubmit:e=>{e.preventDefault(),S.mutate()},children:[(0,w.jsx)(c,{id:`t-label`,label:`New token: label`,hint:`What it's for - laptop, ci.`,children:(0,w.jsx)(`input`,{id:`t-label`,value:f,autoComplete:`off`,onChange:e=>m(e.target.value)})}),(0,w.jsx)(c,{id:`t-expires`,label:`Expires`,hint:`Leave blank for no expiry.`,children:(0,w.jsx)(`input`,{id:`t-expires`,type:`date`,value:h,onChange:e=>g(e.target.value)})}),(0,w.jsx)(`div`,{children:(0,w.jsx)(`button`,{type:`submit`,className:`btn`,disabled:S.isPending,children:`Issue token`})})]})]})}export{N as AdministratorsPage};
1
+ import{t as e}from"./useMutation-ptto6u1J.js";import{$ as t,A as n,C as r,D as i,F as a,H as o,L as s,N as c,O as l,P as u,Q as d,S as f,X as p,Z as m,c as h,d as g,k as _,m as v}from"./index-Efe2sJ9A.js";import{t as y}from"./Time-BMys0hNR.js";import{t as b}from"./forms-DXnuEzsc.js";import{n as x,t as S}from"./icons-BdwlYqAC.js";var C=t(d(),1),w=m(),T=[{value:`owner`,label:`Owner`,text:`everything, including administrators`},{value:`admin`,label:`Admin`,text:`connections, API keys and roles, queries, cache, settings`},{value:`developer`,label:`Developer`,text:`saved queries and SQL; no keys, no connection changes`},{value:`auditor`,label:`Auditor`,text:`read only - audit log, history, configuration; no SQL`}],E=e=>T.find(t=>t.value===e)?.label??e,D=90,O=e=>new Date(Date.now()+e*864e5).toISOString().slice(0,10),k=`Store this now — it cannot be shown again. Paste it into the API key box at the bottom of the sidebar, or send it as the X-API-Key header.`;function A(){let{showError:e}=s();return t=>t instanceof f?e(t.message,{status:t.status}):e(t.message)}function j(e){let t=g(`admins.read`,`other administrators`);return o({queryKey:[`administrators`],queryFn:async()=>(await t.check(),i(await r.GET(`/api/v1/administrators`)).items),enabled:e,retry:!1})}function M(e){return o({queryKey:[`administrators`,e,`tokens`],queryFn:async()=>i(await r.GET(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:e}}})).items,retry:!1})}function N(){let e=v(),t=h(),n=!!e.data&&t(`admins.read`),r=j(n),{openDrawer:i}=s(),o=()=>i({title:`New administrator`,content:(0,w.jsx)(I,{})}),c;return c=e.isPending?(0,w.jsx)(a,{}):e.isError?(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`strong`,{children:`Not signed in as an administrator`}),(0,w.jsx)(`span`,{children:`Apply an admin token (qagadm_…) or the shared admin key in the box at the bottom of the sidebar.`})]}):n?r.isPending?(0,w.jsx)(a,{text:`Loading administrators…`}):r.isError?(0,w.jsx)(`div`,{className:`hint`,children:r.error.message}):r.data.length?(0,w.jsx)(P,{items:r.data,self:e.data.via===`token`||e.data.via===`sso`?e.data.name:null}):(0,w.jsxs)(`div`,{className:`empty`,children:[(0,w.jsx)(`strong`,{children:`No administrators yet`}),(0,w.jsx)(`span`,{children:`Everyone shares QUERYAPIGATE_API_KEY today, so the audit log can only say “admin”. Give each person their own sign-in, with a role that fits what they do — then the shared key becomes a break-glass key.`}),(0,w.jsx)(`button`,{type:`button`,className:`btn primary`,onClick:o,children:`New administrator`})]}):(0,w.jsx)(F,{name:e.data.name??``,role:e.data.role}),(0,w.jsxs)(w.Fragment,{children:[(0,w.jsxs)(`div`,{className:`page-head`,children:[(0,w.jsxs)(`div`,{className:`titles`,children:[(0,w.jsx)(`h1`,{children:`Administrators`}),(0,w.jsx)(`span`,{className:`sub`,id:`administrators-sub`,children:n?`The people and pipelines that manage this server, each with a role and their own tokens.`:`Your account and your admin tokens.`})]}),(0,w.jsx)(`span`,{className:`spacer`}),n&&(0,w.jsx)(`button`,{id:`new-administrator`,type:`button`,className:`btn primary`,onClick:o,children:`New administrator`})]}),(0,w.jsx)(`div`,{id:`administrators-table`,className:`panel`,children:c})]})}function P({items:e,self:t}){let{openDrawer:n,toast:i}=s(),a=A(),o=p(),c=async e=>{if(window.confirm(`Remove administrator '${e}'? Every token of theirs stops working immediately.`))try{l(await r.DELETE(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),i(`Removed `+e),o.invalidateQueries({queryKey:[`administrators`]})}catch(e){a(e)}};return(0,w.jsx)(`div`,{style:{overflowX:`auto`},children:(0,w.jsxs)(`table`,{className:`grid`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsx)(`tr`,{children:[`Name`,`Role`,`Email`,`Last seen`,`Tokens`,``].map((e,t)=>(0,w.jsx)(`th`,{children:e},t))})}),(0,w.jsx)(`tbody`,{children:e.map(e=>(0,w.jsxs)(`tr`,{"data-name":e.name,children:[(0,w.jsx)(`td`,{children:(0,w.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,w.jsx)(`span`,{className:`dot ${e.active?`ok`:`off`}`,title:e.active?`Active`:`Inactive`}),(0,w.jsx)(`span`,{className:`name`,children:e.name}),e.name===t&&(0,w.jsx)(`span`,{className:`tag`,children:`you`}),e.sso?.status===`linked`&&(0,w.jsx)(`span`,{className:`tag`,title:`Signs in through ${e.sso.issuer??`SSO`}; role from their groups`,children:`SSO`}),e.sso?.status===`pending`&&(0,w.jsx)(`span`,{className:`tag`,title:`Linked at their next SSO sign-in, by their verified email`,children:`SSO pending`})]})}),(0,w.jsx)(`td`,{children:E(e.role)}),(0,w.jsx)(`td`,{className:e.email?void 0:`dim`,children:e.email??`—`}),(0,w.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,w.jsx)(y,{value:e.last_seen_at,fallback:`never`})}),(0,w.jsx)(`td`,{className:`mono`,children:e.tokens}),(0,w.jsx)(`td`,{children:(0,w.jsxs)(`div`,{className:`actions`,children:[(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>n({title:`Admin tokens`,kicker:e.name,wide:!0,content:(0,w.jsx)(R,{name:e.name})}),children:`Tokens`}),(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm icon`,title:`Edit ${e.name}`,"aria-label":`Edit`,onClick:()=>n({title:`Edit administrator`,kicker:e.name,content:(0,w.jsx)(I,{name:e.name})}),children:(0,w.jsx)(S,{})}),(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm icon danger`,title:`Remove ${e.name}`,"aria-label":`Remove`,onClick:()=>void c(e.name),children:(0,w.jsx)(x,{})})]})})]},e.name))})]})})}function F({name:e,role:t}){return(0,w.jsxs)(`div`,{className:`form`,style:{padding:16},children:[(0,w.jsxs)(`div`,{children:[`Signed in as `,(0,w.jsx)(`strong`,{children:e}),` · `,E(t),` — `,T.find(e=>e.value===t)?.text,`.`]}),(0,w.jsx)(R,{name:e})]})}function I({name:e}){let t=o({queryKey:[`administrators`,`detail`,e],queryFn:async()=>_(await r.GET(`/api/v1/administrators/{name}`,{params:{path:{name:e}}})),enabled:!!e,retry:!1,gcTime:0});return e&&t.isPending?(0,w.jsx)(a,{}):e&&t.isError?(0,w.jsx)(`div`,{className:`hint`,children:t.error.message}):(0,w.jsx)(L,{name:e,existing:t.data?.data,etag:t.data?.etag??null},e??``)}function L({name:t,existing:a,etag:o}){let l=!!t,{closeDrawer:d,openDrawer:f,showError:m,toast:h}=s(),g=A(),_=p(),[v,y]=(0,C.useState)(t??``),[x,S]=(0,C.useState)(a?.role??`developer`),[E,j]=(0,C.useState)(a?.email??``),[M,N]=(0,C.useState)(!a||a.active),P=n(),F=a?.sso?.status===`linked`,[I,L]=(0,C.useState)(a?.sso?.status??`none`),R=e({mutationFn:async()=>{let e={role:x,email:E.trim()||null};if(l)return i(await r.PATCH(`/api/v1/administrators/{name}`,{params:{path:{name:t},header:o?{"If-Match":o}:{}},body:{...F?{email:e.email}:e,active:M,...I===(a?.sso?.status??`none`)?{}:{sso:I===`pending`?`pending`:null}}})),null;let n=i(await r.POST(`/api/v1/administrators`,{body:{name:v.trim(),...e}}));return i(await r.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:n.name}},body:{label:`first token`,expires_at:O(D)}}))},onSuccess:e=>{if(m(``),_.invalidateQueries({queryKey:[`administrators`]}),!e){d(),h(`Updated `+t);return}f({title:`Administrator created`,kicker:e.admin,content:(0,w.jsx)(b,{secret:e.secret,label:`Admin token for ${e.admin} (expires ${e.expires_at})`,hint:k})})},onError:g});return(0,w.jsxs)(`form`,{className:`form`,noValidate:!0,onSubmit:e=>{if(e.preventDefault(),!l&&!v.trim()){m(`A name is required.`,{errors:{name:`This field is required`}});return}R.mutate()},children:[(0,w.jsx)(c,{id:`a-name`,label:`Name`,hint:l?void 0:`A person (alice) or a pipeline (ci-deploy). Shown in the audit log.`,children:(0,w.jsx)(`input`,{id:`a-name`,value:v,placeholder:`alice`,autoComplete:`off`,spellCheck:!1,disabled:l,autoFocus:!l,onChange:e=>y(e.target.value)})}),(0,w.jsx)(c,{id:`a-role`,label:`Role`,hint:F?`From their groups at the identity provider, at every sign-in - change it there.`:T.find(e=>e.value===x)?.text,children:(0,w.jsx)(`select`,{id:`a-role`,value:x,disabled:F,onChange:e=>S(e.target.value),children:T.map(e=>(0,w.jsx)(`option`,{value:e.value,children:e.label},e.value))})}),(0,w.jsx)(c,{id:`a-email`,label:`Email`,hint:`Optional.`,children:(0,w.jsx)(`input`,{id:`a-email`,type:`email`,value:E,autoComplete:`off`,onChange:e=>j(e.target.value)})}),l&&(0,w.jsxs)(`label`,{className:`switch`,children:[(0,w.jsx)(`input`,{id:`a-active`,type:`checkbox`,checked:M,onChange:e=>N(e.target.checked)}),`Active`,(0,w.jsx)(`span`,{className:`hint`,children:`— unchecking stops every token of theirs immediately`})]}),l&&P.data?.sso&&(0,w.jsx)(c,{id:`a-sso`,label:`Sign-in through SSO`,hint:I===`linked`?`They sign in through the identity provider. Unlinking ends their sessions; their next SSO sign-in makes a new administrator.`:`Pending: their next SSO sign-in, with a verified email matching the one above, is bound to this account - once.`,children:(0,w.jsxs)(`select`,{id:`a-sso`,value:I,onChange:e=>L(e.target.value),children:[F&&(0,w.jsx)(`option`,{value:`linked`,children:`Linked`}),!F&&(0,w.jsx)(`option`,{value:`pending`,children:`Link at their next sign-in`}),(0,w.jsx)(`option`,{value:`none`,children:F?`Unlink`:`Not through SSO`})]})}),!l&&(0,w.jsxs)(`div`,{className:`hint`,children:[`Their first admin token, valid for `,D,` days, is shown once after you create them.`]}),(0,w.jsx)(u,{onCancel:d,children:(0,w.jsx)(`button`,{type:`submit`,className:`btn primary`,disabled:R.isPending,children:l?`Save`:`Create`})})]})}function R({name:t}){let n=M(t),o=p(),{toast:u}=s(),d=A(),[f,m]=(0,C.useState)(``),[h,g]=(0,C.useState)(O(D)),[_,v]=(0,C.useState)(null),x=()=>void o.invalidateQueries({queryKey:[`administrators`]}),S=e({mutationFn:async()=>i(await r.POST(`/api/v1/administrators/{name}/tokens`,{params:{path:{name:t}},body:{label:f.trim()||null,expires_at:h||null}})),onSuccess:e=>{v(e),m(``),x()},onError:d}),T=async e=>{if(window.confirm(`Revoke ${e.label?`'${e.label}'`:e.id}? Anything using it stops working now.`))try{l(await r.DELETE(`/api/v1/administrators/{name}/tokens/{token_id}`,{params:{path:{name:t,token_id:e.id}}})),u(`Revoked `+(e.label??e.id)),x()}catch(e){d(e)}},E=new Date().toISOString().slice(0,10);return(0,w.jsxs)(`div`,{className:`form`,id:`admin-tokens`,children:[n.isPending?(0,w.jsx)(a,{text:`Loading tokens…`}):n.isError?(0,w.jsx)(`div`,{className:`hint`,children:n.error.message}):n.data.length?(0,w.jsxs)(`table`,{className:`grid`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsx)(`tr`,{children:[`Label`,`Expires`,`Last used`,``].map((e,t)=>(0,w.jsx)(`th`,{children:e},t))})}),(0,w.jsx)(`tbody`,{children:n.data.map(e=>(0,w.jsxs)(`tr`,{"data-token":e.id,children:[(0,w.jsx)(`td`,{title:e.id,children:e.label??(0,w.jsx)(`span`,{className:`dim mono`,children:e.id})}),(0,w.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:e.expires_at?(0,w.jsx)(`span`,{style:e.expired?{color:`var(--danger)`}:void 0,children:e.expires_at+(e.expired?` (expired)`:e.expires_at===E?` (today)`:``)}):(0,w.jsx)(`span`,{className:`dim`,children:`never`})}),(0,w.jsx)(`td`,{className:`mono dim`,children:(0,w.jsx)(y,{value:e.last_used_at,fallback:`never`})}),(0,w.jsx)(`td`,{children:(0,w.jsx)(`button`,{type:`button`,className:`btn ghost sm danger`,onClick:()=>void T(e),children:`Revoke`})})]},e.id))})]}):(0,w.jsx)(`div`,{className:`hint`,children:`No tokens. Issue one below to sign in with.`}),_?(0,w.jsx)(b,{secret:_.secret,label:`New token${_.label?` '${_.label}'`:``} (${_.expires_at?`expires ${_.expires_at}`:`never expires`})`,hint:k,onDone:()=>v(null)}):(0,w.jsxs)(`form`,{className:`grid2`,noValidate:!0,onSubmit:e=>{e.preventDefault(),S.mutate()},children:[(0,w.jsx)(c,{id:`t-label`,label:`New token: label`,hint:`What it's for - laptop, ci.`,children:(0,w.jsx)(`input`,{id:`t-label`,value:f,autoComplete:`off`,onChange:e=>m(e.target.value)})}),(0,w.jsx)(c,{id:`t-expires`,label:`Expires`,hint:`Leave blank for no expiry.`,children:(0,w.jsx)(`input`,{id:`t-expires`,type:`date`,value:h,onChange:e=>g(e.target.value)})}),(0,w.jsx)(`div`,{children:(0,w.jsx)(`button`,{type:`submit`,className:`btn`,disabled:S.isPending,children:`Issue token`})})]})]})}export{N as AdministratorsPage};
@@ -1 +1 @@
1
- import{$ as e,C as t,D as n,F as r,H as i,Q as a,Z as o}from"./index-DMiPsI_5.js";import{t as s}from"./Time-yK6sDFr-.js";import{t as c}from"./CodeBox-5YJrpfHh.js";var l=e(a(),1),u=o(),d=[{id:`1h`,label:`Last hour`,hours:1},{id:`24h`,label:`Last 24 hours`,hours:24},{id:`7d`,label:`Last 7 days`,hours:168},{id:`30d`,label:`Last 30 days`,hours:720}],f=e=>e.toLocaleString();function p({refusals:e}){let t=Object.entries(e);return t.length?(0,u.jsx)(`span`,{style:{display:`inline-flex`,flexWrap:`wrap`,gap:4},children:t.map(([e,t])=>(0,u.jsx)(`span`,{className:`tag`,title:`${t} call${t===1?``:`s`} failed with ${e}`,children:`${e} × ${t}`},e))}):(0,u.jsx)(`span`,{className:`dim`,children:`none`})}function m({writes:e}){let t=Object.entries(e??{}).filter(([,e])=>e).map(([e,t])=>`${t} ${e}`);return(0,u.jsx)(`div`,{className:`hint`,children:t.length?t.join(` · `):`None.`})}function h({agent:e}){let t=Object.entries(e.queries);return(0,u.jsxs)(`div`,{className:`agent-detail`,children:[(0,u.jsx)(`p`,{className:`sub-h`,children:`Writes asked for`}),(0,u.jsx)(m,{writes:e.writes}),(0,u.jsx)(`p`,{className:`sub-h`,style:{marginTop:12},children:`Saved queries`}),t.length?(0,u.jsx)(`div`,{className:`hint`,children:t.map(([e,t])=>`${e} × ${t}`).join(` · `)}):(0,u.jsx)(`div`,{className:`hint`,children:`None - only ad-hoc SQL.`}),(0,u.jsx)(`p`,{className:`sub-h`,style:{marginTop:12},children:`Most frequent ad-hoc statements`}),e.top_statements.length?e.top_statements.map((e,t)=>(0,u.jsxs)(`div`,{className:`agent-statement`,children:[(0,u.jsx)(`div`,{className:`hint`,children:`${e.count} call${e.count===1?``:`s`} · ${f(e.rows)} rows`+(e.errors?` · ${e.errors} failed`:``)}),e.sql?(0,u.jsx)(c,{sql:e.sql}):(0,u.jsx)(`span`,{className:`mono dim`,children:`sha256 ${e.sql_sha256}`})]},t)):(0,u.jsx)(`div`,{className:`hint`,children:e.adhoc_calls?`Run history keeps no SQL text here (QUERYAPIGATE_HISTORY_ADHOC_SQL is none).`:`No ad-hoc SQL.`})]})}function g(){let[e,a]=(0,l.useState)(`24h`),[o,c]=(0,l.useState)(null),m=d.find(t=>t.id===e).hours,g=i({queryKey:[`usage`,`agents`,e],queryFn:async()=>n(await t.GET(`/api/v1/usage/agents`,{params:{query:{hours:m}}})),retry:!1}),_=g.data?.items??[],v;v=g.isPending?(0,u.jsx)(r,{text:`Loading agents…`}):g.isError?(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`Couldn’t load agent usage`}),(0,u.jsx)(`span`,{children:g.error.message})]}):_.length?(0,u.jsx)(`div`,{style:{overflowX:`auto`},children:(0,u.jsxs)(`table`,{className:`grid`,children:[(0,u.jsx)(`thead`,{children:(0,u.jsx)(`tr`,{children:[`Agent`,`Calls`,`Rows`,`Failed`,`Masked`,`Refused or failed with`,`Last seen`].map(e=>(0,u.jsx)(`th`,{children:e},e))})}),(0,u.jsx)(`tbody`,{children:_.map(e=>(0,u.jsxs)(l.Fragment,{children:[(0,u.jsxs)(`tr`,{children:[(0,u.jsxs)(`td`,{children:[(0,u.jsx)(`button`,{type:`button`,className:`agent-name`,"aria-expanded":o===e.agent,onClick:()=>c(o===e.agent?null:e.agent),children:e.agent}),(0,u.jsx)(`div`,{className:`dim`,style:{fontSize:12},children:e.connections.join(`, `)})]}),(0,u.jsx)(`td`,{className:`num`,children:f(e.calls)}),(0,u.jsx)(`td`,{className:`num`,children:f(e.rows)}),(0,u.jsx)(`td`,{className:`num`,children:f(e.errors)}),(0,u.jsx)(`td`,{className:`num`,children:f(e.masked_calls)}),(0,u.jsx)(`td`,{children:(0,u.jsx)(p,{refusals:e.refusals})}),(0,u.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,u.jsx)(s,{value:e.last_seen,fallback:``})})]}),o===e.agent&&(0,u.jsx)(`tr`,{children:(0,u.jsx)(`td`,{colSpan:7,children:(0,u.jsx)(h,{agent:e})})})]},e.agent))})]})}):(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`No agent called the MCP server in this window`}),(0,u.jsx)(`span`,{children:`Runs over MCP - saved-query tools and execute_sql - appear here, per API key or signed-in user.`})]});let y=[];return g.data&&g.data.sample_rate<1&&y.push(`Run history keeps ${Math.round(g.data.sample_rate*100)}% of successful runs, so successful calls and rows are a sample; failures are all here.`),g.data?.truncated&&y.push(`More runs than one summary reads (100,000) - narrow the window.`),(0,u.jsxs)(u.Fragment,{children:[(0,u.jsxs)(`div`,{className:`page-head`,children:[(0,u.jsxs)(`div`,{className:`titles`,children:[(0,u.jsx)(`h1`,{children:`Agents`}),(0,u.jsx)(`span`,{className:`sub`,children:`What each AI agent did over MCP - per API key or signed-in user, from run history.`})]}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>void g.refetch(),children:`Refresh`})]}),(0,u.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,u.jsxs)(`div`,{className:`panel-bar`,children:[(0,u.jsx)(`select`,{"aria-label":`Window`,value:e,onChange:e=>a(e.target.value),children:d.map(e=>(0,u.jsx)(`option`,{value:e.id,children:e.label},e.id))}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`span`,{className:`panel-count`,children:g.data?`${_.length} agent${_.length===1?``:`s`}`:``})]}),y.map(e=>(0,u.jsx)(`div`,{className:`hint`,style:{padding:`6px 12px`},children:e},e)),v]})]})}export{g as AgentsPage};
1
+ import{$ as e,C as t,D as n,F as r,H as i,Q as a,Z as o}from"./index-Efe2sJ9A.js";import{t as s}from"./Time-BMys0hNR.js";import{t as c}from"./CodeBox-B933gn9I.js";var l=e(a(),1),u=o(),d=[{id:`1h`,label:`Last hour`,hours:1},{id:`24h`,label:`Last 24 hours`,hours:24},{id:`7d`,label:`Last 7 days`,hours:168},{id:`30d`,label:`Last 30 days`,hours:720}],f=e=>e.toLocaleString();function p({refusals:e}){let t=Object.entries(e);return t.length?(0,u.jsx)(`span`,{style:{display:`inline-flex`,flexWrap:`wrap`,gap:4},children:t.map(([e,t])=>(0,u.jsx)(`span`,{className:`tag`,title:`${t} call${t===1?``:`s`} failed with ${e}`,children:`${e} × ${t}`},e))}):(0,u.jsx)(`span`,{className:`dim`,children:`none`})}function m({writes:e}){let t=Object.entries(e??{}).filter(([,e])=>e).map(([e,t])=>`${t} ${e}`);return(0,u.jsx)(`div`,{className:`hint`,children:t.length?t.join(` · `):`None.`})}function h({agent:e}){let t=Object.entries(e.queries);return(0,u.jsxs)(`div`,{className:`agent-detail`,children:[(0,u.jsx)(`p`,{className:`sub-h`,children:`Writes asked for`}),(0,u.jsx)(m,{writes:e.writes}),(0,u.jsx)(`p`,{className:`sub-h`,style:{marginTop:12},children:`Saved queries`}),t.length?(0,u.jsx)(`div`,{className:`hint`,children:t.map(([e,t])=>`${e} × ${t}`).join(` · `)}):(0,u.jsx)(`div`,{className:`hint`,children:`None - only ad-hoc SQL.`}),(0,u.jsx)(`p`,{className:`sub-h`,style:{marginTop:12},children:`Most frequent ad-hoc statements`}),e.top_statements.length?e.top_statements.map((e,t)=>(0,u.jsxs)(`div`,{className:`agent-statement`,children:[(0,u.jsx)(`div`,{className:`hint`,children:`${e.count} call${e.count===1?``:`s`} · ${f(e.rows)} rows`+(e.errors?` · ${e.errors} failed`:``)}),e.sql?(0,u.jsx)(c,{sql:e.sql}):(0,u.jsx)(`span`,{className:`mono dim`,children:`sha256 ${e.sql_sha256}`})]},t)):(0,u.jsx)(`div`,{className:`hint`,children:e.adhoc_calls?`Run history keeps no SQL text here (QUERYAPIGATE_HISTORY_ADHOC_SQL is none).`:`No ad-hoc SQL.`})]})}function g(){let[e,a]=(0,l.useState)(`24h`),[o,c]=(0,l.useState)(null),m=d.find(t=>t.id===e).hours,g=i({queryKey:[`usage`,`agents`,e],queryFn:async()=>n(await t.GET(`/api/v1/usage/agents`,{params:{query:{hours:m}}})),retry:!1}),_=g.data?.items??[],v;v=g.isPending?(0,u.jsx)(r,{text:`Loading agents…`}):g.isError?(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`Couldn’t load agent usage`}),(0,u.jsx)(`span`,{children:g.error.message})]}):_.length?(0,u.jsx)(`div`,{style:{overflowX:`auto`},children:(0,u.jsxs)(`table`,{className:`grid`,children:[(0,u.jsx)(`thead`,{children:(0,u.jsx)(`tr`,{children:[`Agent`,`Calls`,`Rows`,`Failed`,`Masked`,`Refused or failed with`,`Last seen`].map(e=>(0,u.jsx)(`th`,{children:e},e))})}),(0,u.jsx)(`tbody`,{children:_.map(e=>(0,u.jsxs)(l.Fragment,{children:[(0,u.jsxs)(`tr`,{children:[(0,u.jsxs)(`td`,{children:[(0,u.jsx)(`button`,{type:`button`,className:`agent-name`,"aria-expanded":o===e.agent,onClick:()=>c(o===e.agent?null:e.agent),children:e.agent}),(0,u.jsx)(`div`,{className:`dim`,style:{fontSize:12},children:e.connections.join(`, `)})]}),(0,u.jsx)(`td`,{className:`num`,children:f(e.calls)}),(0,u.jsx)(`td`,{className:`num`,children:f(e.rows)}),(0,u.jsx)(`td`,{className:`num`,children:f(e.errors)}),(0,u.jsx)(`td`,{className:`num`,children:f(e.masked_calls)}),(0,u.jsx)(`td`,{children:(0,u.jsx)(p,{refusals:e.refusals})}),(0,u.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,u.jsx)(s,{value:e.last_seen,fallback:``})})]}),o===e.agent&&(0,u.jsx)(`tr`,{children:(0,u.jsx)(`td`,{colSpan:7,children:(0,u.jsx)(h,{agent:e})})})]},e.agent))})]})}):(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`No agent called the MCP server in this window`}),(0,u.jsx)(`span`,{children:`Runs over MCP - saved-query tools and execute_sql - appear here, per API key or signed-in user.`})]});let y=[];return g.data&&g.data.sample_rate<1&&y.push(`Run history keeps ${Math.round(g.data.sample_rate*100)}% of successful runs, so successful calls and rows are a sample; failures are all here.`),g.data?.truncated&&y.push(`More runs than one summary reads (100,000) - narrow the window.`),(0,u.jsxs)(u.Fragment,{children:[(0,u.jsxs)(`div`,{className:`page-head`,children:[(0,u.jsxs)(`div`,{className:`titles`,children:[(0,u.jsx)(`h1`,{children:`Agents`}),(0,u.jsx)(`span`,{className:`sub`,children:`What each AI agent did over MCP - per API key or signed-in user, from run history.`})]}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>void g.refetch(),children:`Refresh`})]}),(0,u.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,u.jsxs)(`div`,{className:`panel-bar`,children:[(0,u.jsx)(`select`,{"aria-label":`Window`,value:e,onChange:e=>a(e.target.value),children:d.map(e=>(0,u.jsx)(`option`,{value:e.id,children:e.label},e.id))}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`span`,{className:`panel-count`,children:g.data?`${_.length} agent${_.length===1?``:`s`}`:``})]}),y.map(e=>(0,u.jsx)(`div`,{className:`hint`,style:{padding:`6px 12px`},children:e},e)),v]})]})}export{g as AgentsPage};
@@ -1 +1 @@
1
- import{$ as e,B as t,F as n,Q as r,Z as i,_ as a,b as o,x as s,y as c,z as l}from"./index-DMiPsI_5.js";import{t as u}from"./Time-yK6sDFr-.js";import{n as d}from"./state-O13cl7cO.js";var f=e(r(),1),p=i(),m=[{id:`critical`,label:`Critical`},{id:`warning`,label:`Warning`},{id:`info`,label:`For your information`}],h={key:`Open API keys`,query:`Open query`,connection:`Open connections`,settings:`Open settings`,export:`Open exports`,agent:`Open agents`,approvals:`Open approvals`,audit:`Open audit log`};function g(){let e=l();return t=>{t.type===`query`?e(`/queries/${encodeURIComponent(t.name)}`):t.type===`key`?e(`/api-keys`):t.type===`connection`?e(`/connections`):t.type===`export`?e(`/exports`):t.type===`agent`?e(`/agents`):t.type===`approvals`?e(`/approvals`):t.type===`audit`?e(`/audit-log`):(d(t.name),e(`/settings`))}}function _({alert:e,dismissed:t}){let n=g();return(0,p.jsxs)(`div`,{className:`alert-row `+e.severity,"data-alert":e.id,children:[(0,p.jsx)(`span`,{className:`sev `+e.severity,children:e.severity}),(0,p.jsxs)(`div`,{className:`alert-what`,children:[(0,p.jsx)(`strong`,{children:e.title}),(0,p.jsx)(`span`,{className:`alert-detail`,children:e.detail}),e.since&&!e.detail.includes(e.since)&&(0,p.jsxs)(`span`,{className:`alert-since`,children:[e.kind===`key_expiring`?`Expires `:`Since `,(0,p.jsx)(u,{value:e.since})]})]}),(0,p.jsxs)(`div`,{className:`actions`,children:[e.target&&(0,p.jsx)(`button`,{type:`button`,className:`btn sm outlined`,onClick:()=>n(e.target),children:h[e.target.type]??`Open`}),t?(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>o(e.id),children:`Restore`}):(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,title:`Hide it in this browser until it clears`,onClick:()=>c(e.id),children:`Dismiss`})]})]})}function v({alerts:e}){return(0,p.jsx)(p.Fragment,{children:m.map(({id:t,label:n})=>{let r=e.filter(e=>e.severity===t);return r.length?(0,p.jsxs)(`div`,{className:`panel alerts-group`,id:`alerts-`+t,children:[(0,p.jsxs)(`h2`,{children:[n,` `,(0,p.jsx)(`span`,{className:`count`,children:r.length})]}),r.map(e=>(0,p.jsx)(_,{alert:e},e.id))]},t):null})})}function y(){let e=l(),r=t(),i=a.find(e=>e.id===r.tab),{query:o,active:c,dismissed:d}=s(),[m,h]=(0,f.useState)(!1),g=e=>!i||i.kinds.includes(e.kind),y=c.filter(g),b=d.filter(g),x=e=>c.filter(t=>!e||e.includes(t.kind)),S;S=o.isPending?(0,p.jsx)(n,{text:`Checking…`}):o.isError?(0,p.jsx)(`div`,{className:`panel`,children:(0,p.jsxs)(`div`,{className:`empty`,children:[(0,p.jsx)(`strong`,{children:`Couldn’t check for alerts`}),(0,p.jsx)(`span`,{children:o.error.message}),(0,p.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>void o.refetch(),children:`Retry`})]})}):(0,p.jsxs)(p.Fragment,{children:[i&&(0,p.jsx)(`p`,{className:`alerts-about`,children:i.about}),y.length?(0,p.jsx)(v,{alerts:y}):(0,p.jsx)(`div`,{className:`panel`,id:`alerts-clear`,children:(0,p.jsxs)(`div`,{className:`empty`,children:[(0,p.jsx)(`strong`,{children:`All clear`}),(0,p.jsx)(`span`,{children:i?`Nothing to report from this check right now.`:`Nothing needs your attention right now.`})]})}),b.length>0&&(0,p.jsxs)(`div`,{className:`alerts-dismissed`,children:[(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>h(e=>!e),children:`${b.length} dismissed in this browser · ${m?`Hide`:`Show`}`}),m&&(0,p.jsx)(`div`,{className:`panel alerts-group`,id:`alerts-dismissed`,children:b.map(e=>(0,p.jsx)(_,{alert:e,dismissed:!0},e.id))})]})]});let C=(t,n,r)=>(0,p.jsxs)(`button`,{type:`button`,role:`tab`,"aria-selected":(i?.id??`all`)===t,className:`minitab`+((i?.id??`all`)===t?` active`:``),onClick:()=>e(t===`all`?`/alerts`:`/alerts/`+t),children:[n,o.data?(0,p.jsx)(`span`,{className:`n`+(r.length?` on`:``)+(r.some(e=>e.severity===`critical`)?` critical`:``),children:r.length}):null]},t);return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)(`div`,{className:`page-head`,children:[(0,p.jsxs)(`div`,{className:`titles`,children:[(0,p.jsx)(`h1`,{children:`Alerts`}),(0,p.jsxs)(`span`,{className:`sub`,children:[`What needs attention now, checked every minute. Each clears by itself once its cause does.`,o.data?(0,p.jsxs)(p.Fragment,{children:[` Last checked `,(0,p.jsx)(u,{value:o.data.checked_at}),`.`]}):null]})]}),(0,p.jsx)(`span`,{className:`spacer`}),(0,p.jsx)(`button`,{id:`refresh-alerts`,type:`button`,className:`btn`,onClick:()=>void o.refetch(),children:`Check now`})]}),(0,p.jsxs)(`div`,{className:`minitabs alerts-tabs`,role:`tablist`,"aria-label":`Checks`,children:[C(`all`,`All`,x()),a.map(e=>C(e.id,e.label,x(e.kinds)))]}),S]})}export{y as AlertsPage};
1
+ import{$ as e,B as t,F as n,Q as r,Z as i,_ as a,b as o,x as s,y as c,z as l}from"./index-Efe2sJ9A.js";import{t as u}from"./Time-BMys0hNR.js";import{n as d}from"./state-O13cl7cO.js";var f=e(r(),1),p=i(),m=[{id:`critical`,label:`Critical`},{id:`warning`,label:`Warning`},{id:`info`,label:`For your information`}],h={key:`Open API keys`,query:`Open query`,connection:`Open connections`,settings:`Open settings`,export:`Open exports`,agent:`Open agents`,approvals:`Open approvals`,audit:`Open audit log`};function g(){let e=l();return t=>{t.type===`query`?e(`/queries/${encodeURIComponent(t.name)}`):t.type===`key`?e(`/api-keys`):t.type===`connection`?e(`/connections`):t.type===`export`?e(`/exports`):t.type===`agent`?e(`/agents`):t.type===`approvals`?e(`/approvals`):t.type===`audit`?e(`/audit-log`):(d(t.name),e(`/settings`))}}function _({alert:e,dismissed:t}){let n=g();return(0,p.jsxs)(`div`,{className:`alert-row `+e.severity,"data-alert":e.id,children:[(0,p.jsx)(`span`,{className:`sev `+e.severity,children:e.severity}),(0,p.jsxs)(`div`,{className:`alert-what`,children:[(0,p.jsx)(`strong`,{children:e.title}),(0,p.jsx)(`span`,{className:`alert-detail`,children:e.detail}),e.since&&!e.detail.includes(e.since)&&(0,p.jsxs)(`span`,{className:`alert-since`,children:[e.kind===`key_expiring`?`Expires `:`Since `,(0,p.jsx)(u,{value:e.since})]})]}),(0,p.jsxs)(`div`,{className:`actions`,children:[e.target&&(0,p.jsx)(`button`,{type:`button`,className:`btn sm outlined`,onClick:()=>n(e.target),children:h[e.target.type]??`Open`}),t?(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>o(e.id),children:`Restore`}):(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,title:`Hide it in this browser until it clears`,onClick:()=>c(e.id),children:`Dismiss`})]})]})}function v({alerts:e}){return(0,p.jsx)(p.Fragment,{children:m.map(({id:t,label:n})=>{let r=e.filter(e=>e.severity===t);return r.length?(0,p.jsxs)(`div`,{className:`panel alerts-group`,id:`alerts-`+t,children:[(0,p.jsxs)(`h2`,{children:[n,` `,(0,p.jsx)(`span`,{className:`count`,children:r.length})]}),r.map(e=>(0,p.jsx)(_,{alert:e},e.id))]},t):null})})}function y(){let e=l(),r=t(),i=a.find(e=>e.id===r.tab),{query:o,active:c,dismissed:d}=s(),[m,h]=(0,f.useState)(!1),g=e=>!i||i.kinds.includes(e.kind),y=c.filter(g),b=d.filter(g),x=e=>c.filter(t=>!e||e.includes(t.kind)),S;S=o.isPending?(0,p.jsx)(n,{text:`Checking…`}):o.isError?(0,p.jsx)(`div`,{className:`panel`,children:(0,p.jsxs)(`div`,{className:`empty`,children:[(0,p.jsx)(`strong`,{children:`Couldn’t check for alerts`}),(0,p.jsx)(`span`,{children:o.error.message}),(0,p.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>void o.refetch(),children:`Retry`})]})}):(0,p.jsxs)(p.Fragment,{children:[i&&(0,p.jsx)(`p`,{className:`alerts-about`,children:i.about}),y.length?(0,p.jsx)(v,{alerts:y}):(0,p.jsx)(`div`,{className:`panel`,id:`alerts-clear`,children:(0,p.jsxs)(`div`,{className:`empty`,children:[(0,p.jsx)(`strong`,{children:`All clear`}),(0,p.jsx)(`span`,{children:i?`Nothing to report from this check right now.`:`Nothing needs your attention right now.`})]})}),b.length>0&&(0,p.jsxs)(`div`,{className:`alerts-dismissed`,children:[(0,p.jsx)(`button`,{type:`button`,className:`btn ghost sm`,onClick:()=>h(e=>!e),children:`${b.length} dismissed in this browser · ${m?`Hide`:`Show`}`}),m&&(0,p.jsx)(`div`,{className:`panel alerts-group`,id:`alerts-dismissed`,children:b.map(e=>(0,p.jsx)(_,{alert:e,dismissed:!0},e.id))})]})]});let C=(t,n,r)=>(0,p.jsxs)(`button`,{type:`button`,role:`tab`,"aria-selected":(i?.id??`all`)===t,className:`minitab`+((i?.id??`all`)===t?` active`:``),onClick:()=>e(t===`all`?`/alerts`:`/alerts/`+t),children:[n,o.data?(0,p.jsx)(`span`,{className:`n`+(r.length?` on`:``)+(r.some(e=>e.severity===`critical`)?` critical`:``),children:r.length}):null]},t);return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)(`div`,{className:`page-head`,children:[(0,p.jsxs)(`div`,{className:`titles`,children:[(0,p.jsx)(`h1`,{children:`Alerts`}),(0,p.jsxs)(`span`,{className:`sub`,children:[`What needs attention now, checked every minute. Each clears by itself once its cause does.`,o.data?(0,p.jsxs)(p.Fragment,{children:[` Last checked `,(0,p.jsx)(u,{value:o.data.checked_at}),`.`]}):null]})]}),(0,p.jsx)(`span`,{className:`spacer`}),(0,p.jsx)(`button`,{id:`refresh-alerts`,type:`button`,className:`btn`,onClick:()=>void o.refetch(),children:`Check now`})]}),(0,p.jsxs)(`div`,{className:`minitabs alerts-tabs`,role:`tablist`,"aria-label":`Checks`,children:[C(`all`,`All`,x()),a.map(e=>C(e.id,e.label,x(e.kinds)))]}),S]})}export{y as AlertsPage};
@@ -1 +1 @@
1
- import{F as e,Z as t,c as n,l as r,s as i}from"./index-DMiPsI_5.js";import{t as a}from"./reach-Czf5bpIV.js";import{t as o}from"./Time-yK6sDFr-.js";import{i as s,n as c,o as l}from"./forms-6-RLlXrl.js";import{t as u}from"./icons-BPJHnFc_.js";import{t as d}from"./Managed-DHCfC-9H.js";var f=t();function p(e,t){let n=new Date(Date.now()+6048e5).toISOString().slice(0,10);return!!(e&&e>=t&&e<=n)}function m({usage:e}){let t=[`calls`,`rows`].map(t=>({what:t,used:e[t],limit:e[`${t}_limit`]})).filter(e=>e.limit!=null),n=t.reduce((e,t)=>t.used/t.limit>e.used/e.limit?t:e,t[0]),r=Math.min(1,n.used/n.limit),i=e.per===`day`?`today`:`this month`;return(0,f.jsxs)(`div`,{className:`quota-usage`,title:t.map(e=>`${e.used.toLocaleString()} of ${e.limit.toLocaleString()} ${e.what}`).join(` · `)+` ${i} · resets ${e.resets_at}`,children:[(0,f.jsxs)(`span`,{className:`dim`,children:[n.used.toLocaleString(),` / `,n.limit.toLocaleString(),` `,n.what,` `,i]}),(0,f.jsx)(`div`,{className:`quota-bar${r>=1?` full`:r>=.8?` near`:``}`,children:(0,f.jsx)(`span`,{style:{width:`${Math.round(r*100)}%`}})})]})}function h(e){if(!e.queries)return null;let t=[e.queries+(e.queries===1?` query`:` queries`)];return e.errors&&t.push(e.errors+` failed`),t.join(` · `)}function g(){let t=n(),g=i(),_=r(),v=c(),y=s(),b=Object.keys(g.data??{}).sort(),x=new Date().toISOString().slice(0,10),S;return S=g.isPending?(0,f.jsx)(e,{text:`Loading API keys…`}):g.isError?(0,f.jsxs)(`div`,{className:`empty`,children:[(0,f.jsx)(`strong`,{children:`Couldn’t load API keys`}),(0,f.jsx)(`span`,{children:g.error.message}),(0,f.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>g.refetch(),children:`Retry`})]}):b.length?(0,f.jsx)(`div`,{style:{overflowX:`auto`},children:(0,f.jsxs)(`table`,{className:`grid`,children:[(0,f.jsx)(`thead`,{children:(0,f.jsx)(`tr`,{children:[`Name`,`Scope`,`Access`,`Rate limit`,`IPs`,`Expires`,`Last used`,`Usage`,``].map((e,t)=>(0,f.jsx)(`th`,{children:e},t))})}),(0,f.jsx)(`tbody`,{children:b.map(e=>{let n=g.data[e],r=!!(n.expires_at&&n.expires_at<x),i=!r&&p(n.expires_at,x),s=n.allowed_ips??[],c=h(n.usage);return(0,f.jsxs)(`tr`,{"data-name":e,children:[(0,f.jsx)(`td`,{children:(0,f.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,f.jsx)(`span`,{className:`dot ${n.active&&!r?`ok`:`off`}`,title:n.active?r?`Expired`:`Active`:`Revoked`}),(0,f.jsx)(`span`,{className:`name`,title:n.created_at?`Created `+n.created_at+(n.created_from_role?` from role `+n.created_from_role:``):void 0,children:e}),(0,f.jsx)(d,{kind:`api_keys`,name:e}),n.previous_expires_at?(0,f.jsx)(`span`,{className:`tag`,title:`Rotated: the old secret works until then too`,children:`rotating`}):null,n.mask?.length?(0,f.jsx)(`span`,{className:`tag`,title:n.mask.map(e=>`${e.column}: ${e.action}`).join(`, `),children:`masked`}):null]})}),(0,f.jsx)(`td`,{children:(0,f.jsx)(l,{entry:n,collections:_.data?.collections})}),(0,f.jsx)(a,{entry:n}),(0,f.jsx)(`td`,{className:n.rate_limit?`mono`:`mono dim`,style:{whiteSpace:`nowrap`},children:n.rate_limit||`server default`}),(0,f.jsx)(`td`,{className:`dim`,children:s.length?(0,f.jsx)(`span`,{title:s.join(`, `),children:s.length+(s.length===1?` IP`:` IPs`)}):(0,f.jsx)(`span`,{className:`dim`,children:`any`})}),(0,f.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:n.expires_at?(0,f.jsx)(`span`,{style:r?{color:`var(--danger)`}:i?{color:`var(--warn)`}:void 0,children:n.expires_at+(r?` (expired)`:i?` (expires soon)`:``)}):(0,f.jsx)(`span`,{className:`dim`,children:`never`})}),(0,f.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,f.jsx)(o,{value:n.last_used_at,fallback:`never`})}),n.usage_this_period?(0,f.jsxs)(`td`,{style:{whiteSpace:`nowrap`},children:[c,(0,f.jsx)(m,{usage:n.usage_this_period})]}):c?(0,f.jsx)(`td`,{title:`${n.usage.rows}${n.usage.rows===1?` row`:` rows`} returned in total · since this process started`,style:{whiteSpace:`nowrap`},children:c}):(0,f.jsx)(`td`,{className:`dim`,style:{whiteSpace:`nowrap`},children:`No activity yet`}),(0,f.jsx)(`td`,{children:(0,f.jsxs)(`div`,{className:`actions`,children:[(0,f.jsx)(`button`,{hidden:!t(`access.write`),type:`button`,className:`btn ghost sm icon`,title:`Edit ${e}`,"aria-label":`Edit`,onClick:()=>v.editKey(e),children:(0,f.jsx)(u,{})}),(0,f.jsx)(`button`,{hidden:!t(`access.write`),type:`button`,className:`btn ghost sm danger`,onClick:()=>void y(e),children:`Revoke`})]})})]},e)})})]})}):(0,f.jsxs)(`div`,{className:`empty`,children:[(0,f.jsx)(`strong`,{children:`No scoped API keys yet`}),(0,f.jsx)(`span`,{children:`QUERYAPIGATE_API_KEY is a full-access admin key. Create a scoped one to limit a caller to specific connections, read-only.`}),(0,f.jsx)(`button`,{hidden:!t(`access.write`),type:`button`,className:`btn primary`,onClick:()=>v.newKey(),children:`New API key`})]}),(0,f.jsxs)(f.Fragment,{children:[(0,f.jsxs)(`div`,{className:`page-head`,children:[(0,f.jsxs)(`div`,{className:`titles`,children:[(0,f.jsx)(`h1`,{children:`API keys`}),(0,f.jsx)(`span`,{className:`sub`,id:`apikeys-sub`,children:(b.length?b.length+(b.length===1?` key. `:` keys. `):``)+`Scope each one to connections, collections or roles.`})]}),(0,f.jsx)(`span`,{className:`spacer`}),(0,f.jsx)(`button`,{hidden:!t(`access.write`),id:`new-apikey`,type:`button`,className:`btn primary`,onClick:()=>v.newKey(),children:`New API key`})]}),(0,f.jsx)(`div`,{id:`apikeys-table`,className:`panel`,children:S})]})}export{g as ApiKeysPage};
1
+ import{F as e,Z as t,c as n,l as r,s as i}from"./index-Efe2sJ9A.js";import{t as a}from"./reach-Jb8SEj5E.js";import{t as o}from"./Time-BMys0hNR.js";import{i as s,n as c,o as l}from"./forms-DXnuEzsc.js";import{t as u}from"./icons-BdwlYqAC.js";import{t as d}from"./Managed-BS06VUT9.js";var f=t();function p(e,t){let n=new Date(Date.now()+6048e5).toISOString().slice(0,10);return!!(e&&e>=t&&e<=n)}function m({usage:e}){let t=[`calls`,`rows`].map(t=>({what:t,used:e[t],limit:e[`${t}_limit`]})).filter(e=>e.limit!=null),n=t.reduce((e,t)=>t.used/t.limit>e.used/e.limit?t:e,t[0]),r=Math.min(1,n.used/n.limit),i=e.per===`day`?`today`:`this month`;return(0,f.jsxs)(`div`,{className:`quota-usage`,title:t.map(e=>`${e.used.toLocaleString()} of ${e.limit.toLocaleString()} ${e.what}`).join(` · `)+` ${i} · resets ${e.resets_at}`,children:[(0,f.jsxs)(`span`,{className:`dim`,children:[n.used.toLocaleString(),` / `,n.limit.toLocaleString(),` `,n.what,` `,i]}),(0,f.jsx)(`div`,{className:`quota-bar${r>=1?` full`:r>=.8?` near`:``}`,children:(0,f.jsx)(`span`,{style:{width:`${Math.round(r*100)}%`}})})]})}function h(e){if(!e.queries)return null;let t=[e.queries+(e.queries===1?` query`:` queries`)];return e.errors&&t.push(e.errors+` failed`),t.join(` · `)}function g(){let t=n(),g=i(),_=r(),v=c(),y=s(),b=Object.keys(g.data??{}).sort(),x=new Date().toISOString().slice(0,10),S;return S=g.isPending?(0,f.jsx)(e,{text:`Loading API keys…`}):g.isError?(0,f.jsxs)(`div`,{className:`empty`,children:[(0,f.jsx)(`strong`,{children:`Couldn’t load API keys`}),(0,f.jsx)(`span`,{children:g.error.message}),(0,f.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>g.refetch(),children:`Retry`})]}):b.length?(0,f.jsx)(`div`,{style:{overflowX:`auto`},children:(0,f.jsxs)(`table`,{className:`grid`,children:[(0,f.jsx)(`thead`,{children:(0,f.jsx)(`tr`,{children:[`Name`,`Scope`,`Access`,`Rate limit`,`IPs`,`Expires`,`Last used`,`Usage`,``].map((e,t)=>(0,f.jsx)(`th`,{children:e},t))})}),(0,f.jsx)(`tbody`,{children:b.map(e=>{let n=g.data[e],r=!!(n.expires_at&&n.expires_at<x),i=!r&&p(n.expires_at,x),s=n.allowed_ips??[],c=h(n.usage);return(0,f.jsxs)(`tr`,{"data-name":e,children:[(0,f.jsx)(`td`,{children:(0,f.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,f.jsx)(`span`,{className:`dot ${n.active&&!r?`ok`:`off`}`,title:n.active?r?`Expired`:`Active`:`Revoked`}),(0,f.jsx)(`span`,{className:`name`,title:n.created_at?`Created `+n.created_at+(n.created_from_role?` from role `+n.created_from_role:``):void 0,children:e}),(0,f.jsx)(d,{kind:`api_keys`,name:e}),n.previous_expires_at?(0,f.jsx)(`span`,{className:`tag`,title:`Rotated: the old secret works until then too`,children:`rotating`}):null,n.mask?.length?(0,f.jsx)(`span`,{className:`tag`,title:n.mask.map(e=>`${e.column}: ${e.action}`).join(`, `),children:`masked`}):null]})}),(0,f.jsx)(`td`,{children:(0,f.jsx)(l,{entry:n,collections:_.data?.collections})}),(0,f.jsx)(a,{entry:n}),(0,f.jsx)(`td`,{className:n.rate_limit?`mono`:`mono dim`,style:{whiteSpace:`nowrap`},children:n.rate_limit||`server default`}),(0,f.jsx)(`td`,{className:`dim`,children:s.length?(0,f.jsx)(`span`,{title:s.join(`, `),children:s.length+(s.length===1?` IP`:` IPs`)}):(0,f.jsx)(`span`,{className:`dim`,children:`any`})}),(0,f.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:n.expires_at?(0,f.jsx)(`span`,{style:r?{color:`var(--danger)`}:i?{color:`var(--warn)`}:void 0,children:n.expires_at+(r?` (expired)`:i?` (expires soon)`:``)}):(0,f.jsx)(`span`,{className:`dim`,children:`never`})}),(0,f.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,f.jsx)(o,{value:n.last_used_at,fallback:`never`})}),n.usage_this_period?(0,f.jsxs)(`td`,{style:{whiteSpace:`nowrap`},children:[c,(0,f.jsx)(m,{usage:n.usage_this_period})]}):c?(0,f.jsx)(`td`,{title:`${n.usage.rows}${n.usage.rows===1?` row`:` rows`} returned in total · since this process started`,style:{whiteSpace:`nowrap`},children:c}):(0,f.jsx)(`td`,{className:`dim`,style:{whiteSpace:`nowrap`},children:`No activity yet`}),(0,f.jsx)(`td`,{children:(0,f.jsxs)(`div`,{className:`actions`,children:[(0,f.jsx)(`button`,{hidden:!t(`access.write`),type:`button`,className:`btn ghost sm icon`,title:`Edit ${e}`,"aria-label":`Edit`,onClick:()=>v.editKey(e),children:(0,f.jsx)(u,{})}),(0,f.jsx)(`button`,{hidden:!t(`access.write`),type:`button`,className:`btn ghost sm danger`,onClick:()=>void y(e),children:`Revoke`})]})})]},e)})})]})}):(0,f.jsxs)(`div`,{className:`empty`,children:[(0,f.jsx)(`strong`,{children:`No scoped API keys yet`}),(0,f.jsx)(`span`,{children:`QUERYAPIGATE_API_KEY is a full-access admin key. Create a scoped one to limit a caller to specific connections, read-only.`}),(0,f.jsx)(`button`,{hidden:!t(`access.write`),type:`button`,className:`btn primary`,onClick:()=>v.newKey(),children:`New API key`})]}),(0,f.jsxs)(f.Fragment,{children:[(0,f.jsxs)(`div`,{className:`page-head`,children:[(0,f.jsxs)(`div`,{className:`titles`,children:[(0,f.jsx)(`h1`,{children:`API keys`}),(0,f.jsx)(`span`,{className:`sub`,id:`apikeys-sub`,children:(b.length?b.length+(b.length===1?` key. `:` keys. `):``)+`Scope each one to connections, collections or roles.`})]}),(0,f.jsx)(`span`,{className:`spacer`}),(0,f.jsx)(`button`,{hidden:!t(`access.write`),id:`new-apikey`,type:`button`,className:`btn primary`,onClick:()=>v.newKey(),children:`New API key`})]}),(0,f.jsx)(`div`,{id:`apikeys-table`,className:`panel`,children:S})]})}export{g as ApiKeysPage};
@@ -1 +1 @@
1
- import{t as e}from"./useMutation-DGd9BCI3.js";import{$ as t,B as n,C as r,D as i,F as a,H as o,L as s,N as c,Q as l,S as u,X as d,Z as f,d as p,z as m}from"./index-DMiPsI_5.js";import{t as h}from"./Time-yK6sDFr-.js";import{t as g}from"./CodeBox-5YJrpfHh.js";var _=t(l(),1),v=f(),y={pending:`Waiting`,approved:`Approved - not run yet`,running:`Running`,done:`Done`,failed:`Failed`,rejected:`Rejected`,expired:`Expired`};function b(e){let t=p(`writes.approve`,`write requests`);return o({queryKey:[`write-requests`,e||`all`],queryFn:async()=>(await t.check(),i(await r.GET(`/api/v1/write-requests`,{params:{query:e?{status:e}:{}}})).items),enabled:t.ready,retry:!1})}var x=e=>JSON.stringify(e);function S({args:e}){let t=Object.entries(e);return t.length?(0,v.jsx)(`table`,{className:`grid approval-args`,children:(0,v.jsx)(`tbody`,{children:t.map(([e,t])=>(0,v.jsxs)(`tr`,{children:[(0,v.jsx)(`td`,{className:`mono`,children:e}),(0,v.jsx)(`td`,{className:`mono`,children:x(t)})]},e))})}):(0,v.jsx)(`span`,{className:`dim`,children:`none`})}function C({id:t}){let{closeDrawer:n,showError:l,toast:f}=s(),p=d(),[m,b]=(0,_.useState)(``),C=o({queryKey:[`write-requests`,`one`,t],queryFn:async()=>i(await r.GET(`/api/v1/write-requests/{id}`,{params:{path:{id:t}}})),retry:!1}),w=e({mutationFn:async e=>i(e?await r.POST(`/api/v1/write-requests/{id}/approve`,{params:{path:{id:t}}}):await r.POST(`/api/v1/write-requests/{id}/reject`,{params:{path:{id:t}},body:m.trim()?{reason:m.trim()}:{}})),onSuccess:e=>{l(``),p.invalidateQueries({queryKey:[`write-requests`]}),p.invalidateQueries({queryKey:[`alerts`]}),n(),f(e.status===`approved`?`Approved - ${e.caller} may run it now`:`Rejected`)},onError:e=>l(e.message,e instanceof u?{status:e.status}:{})});if(C.isPending)return(0,v.jsx)(a,{});if(C.isError)return(0,v.jsx)(`div`,{className:`hint`,children:C.error.message});let T=C.data,E=T.status===`pending`;return(0,v.jsxs)(`div`,{className:`form`,children:[(0,v.jsxs)(`p`,{className:`hint`,children:[(0,v.jsx)(`strong`,{children:T.caller}),` asked to run `,(0,v.jsx)(`strong`,{children:T.query}),` (version `,T.version,`) on`,` `,(0,v.jsx)(`span`,{className:`mono`,children:T.connection}),`. It changes at most `,T.max_affected_rows,` row`,T.max_affected_rows===1?``:`s`,`; past that it is rolled back.`]}),(0,v.jsx)(c,{label:`SQL`,children:(0,v.jsx)(g,{sql:T.sql})}),(0,v.jsx)(c,{label:`With exactly these arguments`,hint:`An approval is for these alone - no other write.`,children:(0,v.jsx)(S,{args:T.arguments})}),(0,v.jsxs)(`div`,{className:`hint`,children:[`Asked `,(0,v.jsx)(h,{value:T.created_at}),` · `,y[T.status]??T.status,E||T.status===`approved`?(0,v.jsxs)(v.Fragment,{children:[` `,`· expires `,(0,v.jsx)(h,{value:T.expires_at})]}):null,T.decided_by?` · ${T.status===`rejected`?`rejected`:`approved`} by ${T.decided_by}`:``,T.reason?` - ${T.reason}`:``]}),T.result?(0,v.jsx)(c,{label:`Result`,children:(0,v.jsx)(`span`,{className:`mono`,children:x(T.result)})}):null,E?(0,v.jsxs)(v.Fragment,{children:[(0,v.jsx)(c,{id:`approval-reason`,label:`Reason`,hint:`Optional for a rejection - the agent is told.`,children:(0,v.jsx)(`textarea`,{id:`approval-reason`,rows:2,value:m,onChange:e=>b(e.target.value)})}),(0,v.jsxs)(`div`,{className:`form-actions`,children:[(0,v.jsx)(`button`,{type:`button`,className:`btn primary`,disabled:w.isPending,onClick:()=>w.mutate(!0),children:`Approve`}),(0,v.jsx)(`button`,{type:`button`,className:`btn danger`,disabled:w.isPending,onClick:()=>w.mutate(!1),children:`Reject`}),(0,v.jsx)(`button`,{type:`button`,className:`btn`,onClick:n,children:`Close`})]})]}):(0,v.jsx)(`div`,{className:`form-actions`,children:(0,v.jsx)(`button`,{type:`button`,className:`btn`,onClick:n,children:`Close`})})]})}function w(){let{id:e}=n(),t=m(),{openDrawer:r}=s(),[i,o]=(0,_.useState)(`pending`),c=b(i),l=c.data??[],u=e=>r({title:`Approve a write`,kicker:e,wide:!0,content:(0,v.jsx)(C,{id:e})});(0,_.useEffect)(()=>{e&&(u(e),t(`/approvals`,{replace:!0}))},[e]);let d;return d=c.isPending?(0,v.jsx)(a,{text:`Loading write requests…`}):c.isError?(0,v.jsxs)(`div`,{className:`empty`,children:[(0,v.jsx)(`strong`,{children:`Couldn’t load write requests`}),(0,v.jsx)(`span`,{children:c.error.message})]}):l.length?(0,v.jsx)(`div`,{style:{overflowX:`auto`},children:(0,v.jsxs)(`table`,{className:`grid`,children:[(0,v.jsx)(`thead`,{children:(0,v.jsx)(`tr`,{children:[`Asked`,`Agent`,`Tool`,`Arguments`,i===`pending`?`Expires`:`Status`].map(e=>(0,v.jsx)(`th`,{children:e},e))})}),(0,v.jsx)(`tbody`,{children:l.map(e=>(0,v.jsxs)(`tr`,{"data-request":e.id,children:[(0,v.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,v.jsx)(h,{value:e.created_at,fallback:``})}),(0,v.jsx)(`td`,{className:`mono`,children:e.caller}),(0,v.jsx)(`td`,{children:(0,v.jsx)(`button`,{type:`button`,className:`agent-name`,onClick:()=>u(e.id),children:e.query})}),(0,v.jsx)(`td`,{className:`mono dim approval-args-cell`,children:x(e.arguments)}),(0,v.jsx)(`td`,{className:`dim`,style:{whiteSpace:`nowrap`},children:i===`pending`?(0,v.jsx)(h,{value:e.expires_at}):y[e.status]??e.status})]},e.id))})]})}):(0,v.jsxs)(`div`,{className:`empty`,children:[(0,v.jsx)(`strong`,{children:i===`pending`?`No writes are waiting for approval`:`No write requests yet`}),(0,v.jsx)(`span`,{children:`When an AI agent calls a write tool, its write waits here for a person to approve it - the agent is given a link to this screen.`})]}),(0,v.jsxs)(v.Fragment,{children:[(0,v.jsxs)(`div`,{className:`page-head`,children:[(0,v.jsxs)(`div`,{className:`titles`,children:[(0,v.jsx)(`h1`,{children:`Approvals`}),(0,v.jsx)(`span`,{className:`sub`,children:`Writes AI agents asked to make over MCP. Each runs only once a person approves it - with exactly the arguments shown.`})]}),(0,v.jsx)(`span`,{className:`spacer`}),(0,v.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>void c.refetch(),children:`Refresh`})]}),(0,v.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,v.jsxs)(`div`,{className:`panel-bar`,children:[(0,v.jsxs)(`select`,{"aria-label":`Show`,value:i,onChange:e=>o(e.target.value),children:[(0,v.jsx)(`option`,{value:`pending`,children:`Waiting`}),(0,v.jsx)(`option`,{value:``,children:`All`}),(0,v.jsx)(`option`,{value:`done`,children:`Done`}),(0,v.jsx)(`option`,{value:`rejected`,children:`Rejected`}),(0,v.jsx)(`option`,{value:`expired`,children:`Expired`}),(0,v.jsx)(`option`,{value:`failed`,children:`Failed`})]}),(0,v.jsx)(`span`,{className:`spacer`}),(0,v.jsx)(`span`,{className:`panel-count`,children:c.data?`${l.length} request${l.length===1?``:`s`}`:``})]}),d]})]})}export{w as ApprovalsPage};
1
+ import{t as e}from"./useMutation-ptto6u1J.js";import{$ as t,B as n,C as r,D as i,F as a,H as o,L as s,N as c,Q as l,S as u,X as d,Z as f,d as p,z as m}from"./index-Efe2sJ9A.js";import{t as h}from"./Time-BMys0hNR.js";import{t as g}from"./CodeBox-B933gn9I.js";var _=t(l(),1),v=f(),y={pending:`Waiting`,approved:`Approved - not run yet`,running:`Running`,done:`Done`,failed:`Failed`,rejected:`Rejected`,expired:`Expired`};function b(e){let t=p(`writes.approve`,`write requests`);return o({queryKey:[`write-requests`,e||`all`],queryFn:async()=>(await t.check(),i(await r.GET(`/api/v1/write-requests`,{params:{query:e?{status:e}:{}}})).items),enabled:t.ready,retry:!1})}var x=e=>JSON.stringify(e);function S({args:e}){let t=Object.entries(e);return t.length?(0,v.jsx)(`table`,{className:`grid approval-args`,children:(0,v.jsx)(`tbody`,{children:t.map(([e,t])=>(0,v.jsxs)(`tr`,{children:[(0,v.jsx)(`td`,{className:`mono`,children:e}),(0,v.jsx)(`td`,{className:`mono`,children:x(t)})]},e))})}):(0,v.jsx)(`span`,{className:`dim`,children:`none`})}function C({id:t}){let{closeDrawer:n,showError:l,toast:f}=s(),p=d(),[m,b]=(0,_.useState)(``),C=o({queryKey:[`write-requests`,`one`,t],queryFn:async()=>i(await r.GET(`/api/v1/write-requests/{id}`,{params:{path:{id:t}}})),retry:!1}),w=e({mutationFn:async e=>i(e?await r.POST(`/api/v1/write-requests/{id}/approve`,{params:{path:{id:t}}}):await r.POST(`/api/v1/write-requests/{id}/reject`,{params:{path:{id:t}},body:m.trim()?{reason:m.trim()}:{}})),onSuccess:e=>{l(``),p.invalidateQueries({queryKey:[`write-requests`]}),p.invalidateQueries({queryKey:[`alerts`]}),n(),f(e.status===`approved`?`Approved - ${e.caller} may run it now`:`Rejected`)},onError:e=>l(e.message,e instanceof u?{status:e.status}:{})});if(C.isPending)return(0,v.jsx)(a,{});if(C.isError)return(0,v.jsx)(`div`,{className:`hint`,children:C.error.message});let T=C.data,E=T.status===`pending`;return(0,v.jsxs)(`div`,{className:`form`,children:[(0,v.jsxs)(`p`,{className:`hint`,children:[(0,v.jsx)(`strong`,{children:T.caller}),` asked to run `,(0,v.jsx)(`strong`,{children:T.query}),` (version `,T.version,`) on`,` `,(0,v.jsx)(`span`,{className:`mono`,children:T.connection}),`. It changes at most `,T.max_affected_rows,` row`,T.max_affected_rows===1?``:`s`,`; past that it is rolled back.`]}),(0,v.jsx)(c,{label:`SQL`,children:(0,v.jsx)(g,{sql:T.sql})}),(0,v.jsx)(c,{label:`With exactly these arguments`,hint:`An approval is for these alone - no other write.`,children:(0,v.jsx)(S,{args:T.arguments})}),(0,v.jsxs)(`div`,{className:`hint`,children:[`Asked `,(0,v.jsx)(h,{value:T.created_at}),` · `,y[T.status]??T.status,E||T.status===`approved`?(0,v.jsxs)(v.Fragment,{children:[` `,`· expires `,(0,v.jsx)(h,{value:T.expires_at})]}):null,T.decided_by?` · ${T.status===`rejected`?`rejected`:`approved`} by ${T.decided_by}`:``,T.reason?` - ${T.reason}`:``]}),T.result?(0,v.jsx)(c,{label:`Result`,children:(0,v.jsx)(`span`,{className:`mono`,children:x(T.result)})}):null,E?(0,v.jsxs)(v.Fragment,{children:[(0,v.jsx)(c,{id:`approval-reason`,label:`Reason`,hint:`Optional for a rejection - the agent is told.`,children:(0,v.jsx)(`textarea`,{id:`approval-reason`,rows:2,value:m,onChange:e=>b(e.target.value)})}),(0,v.jsxs)(`div`,{className:`form-actions`,children:[(0,v.jsx)(`button`,{type:`button`,className:`btn primary`,disabled:w.isPending,onClick:()=>w.mutate(!0),children:`Approve`}),(0,v.jsx)(`button`,{type:`button`,className:`btn danger`,disabled:w.isPending,onClick:()=>w.mutate(!1),children:`Reject`}),(0,v.jsx)(`button`,{type:`button`,className:`btn`,onClick:n,children:`Close`})]})]}):(0,v.jsx)(`div`,{className:`form-actions`,children:(0,v.jsx)(`button`,{type:`button`,className:`btn`,onClick:n,children:`Close`})})]})}function w(){let{id:e}=n(),t=m(),{openDrawer:r}=s(),[i,o]=(0,_.useState)(`pending`),c=b(i),l=c.data??[],u=e=>r({title:`Approve a write`,kicker:e,wide:!0,content:(0,v.jsx)(C,{id:e})});(0,_.useEffect)(()=>{e&&(u(e),t(`/approvals`,{replace:!0}))},[e]);let d;return d=c.isPending?(0,v.jsx)(a,{text:`Loading write requests…`}):c.isError?(0,v.jsxs)(`div`,{className:`empty`,children:[(0,v.jsx)(`strong`,{children:`Couldn’t load write requests`}),(0,v.jsx)(`span`,{children:c.error.message})]}):l.length?(0,v.jsx)(`div`,{style:{overflowX:`auto`},children:(0,v.jsxs)(`table`,{className:`grid`,children:[(0,v.jsx)(`thead`,{children:(0,v.jsx)(`tr`,{children:[`Asked`,`Agent`,`Tool`,`Arguments`,i===`pending`?`Expires`:`Status`].map(e=>(0,v.jsx)(`th`,{children:e},e))})}),(0,v.jsx)(`tbody`,{children:l.map(e=>(0,v.jsxs)(`tr`,{"data-request":e.id,children:[(0,v.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,v.jsx)(h,{value:e.created_at,fallback:``})}),(0,v.jsx)(`td`,{className:`mono`,children:e.caller}),(0,v.jsx)(`td`,{children:(0,v.jsx)(`button`,{type:`button`,className:`agent-name`,onClick:()=>u(e.id),children:e.query})}),(0,v.jsx)(`td`,{className:`mono dim approval-args-cell`,children:x(e.arguments)}),(0,v.jsx)(`td`,{className:`dim`,style:{whiteSpace:`nowrap`},children:i===`pending`?(0,v.jsx)(h,{value:e.expires_at}):y[e.status]??e.status})]},e.id))})]})}):(0,v.jsxs)(`div`,{className:`empty`,children:[(0,v.jsx)(`strong`,{children:i===`pending`?`No writes are waiting for approval`:`No write requests yet`}),(0,v.jsx)(`span`,{children:`When an AI agent calls a write tool, its write waits here for a person to approve it - the agent is given a link to this screen.`})]}),(0,v.jsxs)(v.Fragment,{children:[(0,v.jsxs)(`div`,{className:`page-head`,children:[(0,v.jsxs)(`div`,{className:`titles`,children:[(0,v.jsx)(`h1`,{children:`Approvals`}),(0,v.jsx)(`span`,{className:`sub`,children:`Writes AI agents asked to make over MCP. Each runs only once a person approves it - with exactly the arguments shown.`})]}),(0,v.jsx)(`span`,{className:`spacer`}),(0,v.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>void c.refetch(),children:`Refresh`})]}),(0,v.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,v.jsxs)(`div`,{className:`panel-bar`,children:[(0,v.jsxs)(`select`,{"aria-label":`Show`,value:i,onChange:e=>o(e.target.value),children:[(0,v.jsx)(`option`,{value:`pending`,children:`Waiting`}),(0,v.jsx)(`option`,{value:``,children:`All`}),(0,v.jsx)(`option`,{value:`done`,children:`Done`}),(0,v.jsx)(`option`,{value:`rejected`,children:`Rejected`}),(0,v.jsx)(`option`,{value:`expired`,children:`Expired`}),(0,v.jsx)(`option`,{value:`failed`,children:`Failed`})]}),(0,v.jsx)(`span`,{className:`spacer`}),(0,v.jsx)(`span`,{className:`panel-count`,children:c.data?`${l.length} request${l.length===1?``:`s`}`:``})]}),d]})]})}export{w as ApprovalsPage};
@@ -1,2 +1,2 @@
1
- import{$ as e,C as t,D as n,F as r,H as i,L as a,Q as o,Z as s}from"./index-DMiPsI_5.js";import{t as c}from"./Time-yK6sDFr-.js";var l=e(o(),1),u=s();function d(e){return/^(create|load|save)/.test(e)?`ok`:/^(delete|unload|revoke)/.test(e)?`bad`:``}function f(e){if(e==null||e===``)return`none`;let t=e=>e&&typeof e==`object`?JSON.stringify(e):String(e);return Array.isArray(e)?e.length?e.map(t).join(`, `):`none`:t(e)}var p=e=>e==null||e===``||Array.isArray(e)&&!e.length,m=e=>!!e&&typeof e==`object`&&!Array.isArray(e)&&(`from`in e||`to`in e);function h({changes:e}){let t=e&&typeof e==`object`?e:null,n=t?Object.keys(t).sort():[],r=n.filter(e=>m(t[e])||!p(t[e]));return r.length?(0,u.jsx)(`div`,{children:r.map(e=>{let n=t[e];return(0,u.jsx)(`div`,{className:`mono`,style:{fontSize:12,color:`var(--ink-2)`},children:m(n)?`${e}: ${f(n.from)} → ${f(n.to)}`:`${e}: ${f(n)}`},e)})}):(0,u.jsx)(`span`,{className:`dim`,children:n.length?`no other fields set`:`—`})}function g(e){let t=new Blob([JSON.stringify(e,null,2)+`
1
+ import{$ as e,C as t,D as n,F as r,H as i,L as a,Q as o,Z as s}from"./index-Efe2sJ9A.js";import{t as c}from"./Time-BMys0hNR.js";var l=e(o(),1),u=s();function d(e){return/^(create|load|save)/.test(e)?`ok`:/^(delete|unload|revoke)/.test(e)?`bad`:``}function f(e){if(e==null||e===``)return`none`;let t=e=>e&&typeof e==`object`?JSON.stringify(e):String(e);return Array.isArray(e)?e.length?e.map(t).join(`, `):`none`:t(e)}var p=e=>e==null||e===``||Array.isArray(e)&&!e.length,m=e=>!!e&&typeof e==`object`&&!Array.isArray(e)&&(`from`in e||`to`in e);function h({changes:e}){let t=e&&typeof e==`object`?e:null,n=t?Object.keys(t).sort():[],r=n.filter(e=>m(t[e])||!p(t[e]));return r.length?(0,u.jsx)(`div`,{children:r.map(e=>{let n=t[e];return(0,u.jsx)(`div`,{className:`mono`,style:{fontSize:12,color:`var(--ink-2)`},children:m(n)?`${e}: ${f(n.from)} → ${f(n.to)}`:`${e}: ${f(n)}`},e)})}):(0,u.jsx)(`span`,{className:`dim`,children:n.length?`no other fields set`:`—`})}function g(e){let t=new Blob([JSON.stringify(e,null,2)+`
2
2
  `],{type:`application/json`}),n=document.createElement(`a`);n.href=URL.createObjectURL(t),n.download=`queryapigate-audit-log.json`,n.click(),setTimeout(()=>URL.revokeObjectURL(n.href),0)}function _(){let{toast:e}=a(),o=i({queryKey:[`audit`],queryFn:async()=>n(await t.GET(`/api/v1/audit`)),retry:!1}),[s,f]=(0,l.useState)(``),[p,m]=(0,l.useState)(``),_=o.data?.items??[],v=p.trim().toLowerCase(),y=_.filter(e=>(!s||e.action===s)&&(!v||[e.timestamp,e.actor,e.target].join(` `).toLowerCase().includes(v))),b=o.data?.actions??[],x,S=``;return o.isPending?x=(0,u.jsx)(r,{text:`Loading audit log…`}):o.isError?x=(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`Couldn’t load the audit log`}),(0,u.jsx)(`span`,{children:o.error.message}),(0,u.jsx)(`button`,{type:`button`,className:`btn sm`,onClick:()=>o.refetch(),children:`Retry`})]}):_.length?(S=y.length===_.length?_.length+(_.length===1?` entry`:` entries`):`${y.length} of ${_.length} entries`,x=y.length?(0,u.jsx)(`div`,{style:{overflowX:`auto`},children:(0,u.jsxs)(`table`,{className:`grid`,children:[(0,u.jsx)(`thead`,{children:(0,u.jsx)(`tr`,{children:[`Time`,`Actor`,`Action`,`Target`,`Changes`].map(e=>(0,u.jsx)(`th`,{children:e},e))})}),(0,u.jsx)(`tbody`,{children:y.map((e,t)=>(0,u.jsxs)(`tr`,{children:[(0,u.jsx)(`td`,{className:`mono dim`,style:{whiteSpace:`nowrap`},children:(0,u.jsx)(c,{value:e.timestamp,fallback:``})}),(0,u.jsx)(`td`,{className:`mono`,children:e.actor||`-`}),(0,u.jsx)(`td`,{children:(0,u.jsx)(`span`,{className:`tag act `+d(e.action),children:e.action||``})}),(0,u.jsx)(`td`,{children:(0,u.jsx)(`span`,{className:`name`,children:e.target||``})}),(0,u.jsx)(`td`,{children:(0,u.jsx)(h,{changes:e.changes})})]},t))})]})}):(0,u.jsx)(`div`,{className:`empty`,children:(0,u.jsx)(`span`,{children:`No entries match this filter.`})})):x=(0,u.jsxs)(`div`,{className:`empty`,children:[(0,u.jsx)(`strong`,{children:`No administrative changes recorded yet`}),(0,u.jsx)(`span`,{children:`Creating or changing a connection, saved query or API key will appear here.`})]}),(0,u.jsxs)(u.Fragment,{children:[(0,u.jsxs)(`div`,{className:`page-head`,children:[(0,u.jsxs)(`div`,{className:`titles`,children:[(0,u.jsx)(`h1`,{children:`Audit log`}),(0,u.jsx)(`span`,{className:`sub`,id:`auditlog-sub`,children:`Administrative actions, newest first.`+(o.data?` Keeps the last ${o.data.retention} entries.`:``)})]}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`button`,{id:`refresh-auditlog`,type:`button`,className:`btn`,onClick:()=>void o.refetch(),children:`Refresh`}),(0,u.jsx)(`button`,{id:`export-auditlog`,type:`button`,className:`btn`,onClick:()=>{g(y),e(`Exported `+y.length+(y.length===1?` entry`:` entries`))},children:`Export`})]}),(0,u.jsxs)(`div`,{className:`panel`,style:{overflow:`hidden`},children:[(0,u.jsxs)(`div`,{className:`panel-bar`,children:[(0,u.jsxs)(`select`,{id:`auditlog-action-filter`,"aria-label":`Action`,value:b.includes(s)?s:``,onChange:e=>f(e.target.value),children:[(0,u.jsx)(`option`,{value:``,children:`All actions`}),b.map(e=>(0,u.jsx)(`option`,{value:e,children:e},e))]}),(0,u.jsx)(`input`,{id:`auditlog-filter`,className:`search`,style:{width:260},type:`search`,placeholder:`Filter by actor, target, time…`,value:p,onChange:e=>m(e.target.value)}),(0,u.jsx)(`span`,{className:`spacer`}),(0,u.jsx)(`span`,{className:`panel-count`,id:`auditlog-count`,children:S})]}),(0,u.jsx)(`div`,{id:`auditlog-table`,children:x})]})]})}export{_ as AuditLogPage};
@@ -1 +1 @@
1
- import{$ as e,C as t,D as n,H as r,L as i,O as a,Q as o,X as s,Z as c,g as l,o as u,w as d,z as f}from"./index-DMiPsI_5.js";import{i as p,n as m,t as h}from"./StatTile-BY-03DUD.js";import{n as g,t as _}from"./Results-BVRjDSO0.js";var v=e(o(),1),y=c();function b(){return r({queryKey:[`cache`,`entries`],queryFn:async()=>n(await t.GET(`/api/v1/cache/entries`)).items,retry:!1})}function x(){let e=f(),t=s(),n=l(),r=m(),i=u(),a=r.data??[],o=n.data?.find(e=>e.id===`cache`)?.rows[0]?.value??`…`,c=p(a,`queryapigate_cache_hits_total`),d=p(a,`queryapigate_cache_misses_total`),g=c+d,_=(i.data??[]).filter(e=>(e.cache_ttl??0)>0);return(0,y.jsxs)(y.Fragment,{children:[(0,y.jsxs)(`div`,{className:`page-head`,children:[(0,y.jsxs)(`div`,{className:`titles`,children:[(0,y.jsx)(`h1`,{children:`Caching`}),(0,y.jsxs)(`span`,{className:`sub`,children:[`The response cache backing saved queries' `,(0,y.jsx)(`code`,{children:`cache_ttl`}),` - see the Response cache row in Settings for the backend itself.`]})]}),(0,y.jsx)(`span`,{className:`spacer`}),(0,y.jsx)(`button`,{id:`refresh-caching`,type:`button`,className:`btn`,onClick:()=>{r.refetch(),t.invalidateQueries({queryKey:[`cache`,`entries`]})},children:`Refresh`})]}),(0,y.jsxs)(`div`,{id:`caching-body`,children:[(0,y.jsxs)(`div`,{className:`stat-tiles`,children:[(0,y.jsx)(h,{label:`Cache backend`,value:o}),(0,y.jsx)(h,{label:`Entries`,value:p(a,`queryapigate_cache_entries`)}),(0,y.jsx)(h,{label:`Hit rate`,value:g?(100*c/g).toFixed(1)+`%`:`—`}),(0,y.jsx)(h,{label:`Hits`,value:c}),(0,y.jsx)(h,{label:`Misses`,value:d})]}),(0,y.jsx)(`p`,{className:`sub-h`,style:{marginTop:14},children:`Saved queries with cache_ttl set`}),_.length?(0,y.jsx)(`div`,{className:`panel`,style:{overflowX:`auto`},children:(0,y.jsxs)(`table`,{className:`grid`,children:[(0,y.jsx)(`thead`,{children:(0,y.jsx)(`tr`,{children:[`Query`,`Collection`,`Connection`,`cache_ttl`].map((e,t)=>(0,y.jsx)(`th`,{className:t===3?`num`:``,children:e},e))})}),(0,y.jsx)(`tbody`,{children:_.map(t=>(0,y.jsxs)(`tr`,{children:[(0,y.jsx)(`td`,{children:(0,y.jsx)(`button`,{type:`button`,className:`target`,onClick:()=>e(`/queries/${encodeURIComponent(t.name)}`),children:t.name})}),(0,y.jsx)(`td`,{children:t.collection||(0,y.jsx)(`span`,{className:`dim`,children:`—`})}),(0,y.jsx)(`td`,{className:`mono`,children:t.connection_name||`—`}),(0,y.jsx)(`td`,{className:`mono num`,children:t.cache_ttl+`s`})]},t.name))})]})}):(0,y.jsx)(`div`,{className:`hint`,children:`No saved query declares a cache_ttl yet - set one on a read-only query to start caching its responses.`})]}),(0,y.jsx)(`div`,{id:`cache-entries-panel`,children:(0,y.jsx)(C,{})})]})}function S(e){return e<1024?e+` B`:(e/1024).toFixed(1)+` KB`}function C(){let e=s(),{openDrawer:n,showError:r,toast:o}=i(),c=b().data??[],l=()=>void e.invalidateQueries({queryKey:[`cache`,`entries`]}),u=e=>r(e.message),d=async()=>{if(window.confirm(`Evict all ${c.length} cache entries? The next call to each query will re-run for real.`))try{a(await t.DELETE(`/api/v1/cache/entries`)),o(`Cache cleared`),l()}catch(e){u(e)}},f=async e=>{try{a(await t.DELETE(`/api/v1/cache/entries/{key}`,{params:{path:{key:e}}})),o(`Entry deleted`),l()}catch(e){u(e)}};return(0,y.jsxs)(y.Fragment,{children:[(0,y.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:12},children:[(0,y.jsxs)(`div`,{className:`set-head`,style:{marginTop:14},children:[(0,y.jsx)(`h2`,{children:`Cache entries`}),(0,y.jsx)(`span`,{children:`${c.length}${c.length===1?` live entry`:` live entries`} right now.`})]}),(0,y.jsx)(`span`,{className:`spacer`}),(0,y.jsx)(`button`,{type:`button`,className:`btn sm`,disabled:!c.length,onClick:()=>void d(),children:`Clear cache`})]}),c.length?(0,y.jsx)(`div`,{className:`panel`,style:{overflowX:`auto`,marginTop:8},children:(0,y.jsxs)(`table`,{className:`grid`,children:[(0,y.jsx)(`thead`,{children:(0,y.jsx)(`tr`,{children:[`Query`,`Version`,`Connection`,`Format`,`Content type`,`Size`,`TTL left`,``].map((e,t)=>(0,y.jsx)(`th`,{className:t>=4&&t<=6?`num`:``,children:e},t))})}),(0,y.jsx)(`tbody`,{children:c.map(e=>{let t=e.meta??{};return(0,y.jsxs)(`tr`,{children:[(0,y.jsx)(`td`,{children:(0,y.jsx)(`button`,{type:`button`,className:`target`,onClick:()=>n({title:t.name||`Cache entry`,kicker:e.key,content:(0,y.jsx)(T,{entry:e})}),children:t.name||e.key.slice(0,12)+`…`})}),(0,y.jsx)(`td`,{className:`mono`,children:t.version===void 0?`—`:`v`+t.version}),(0,y.jsx)(`td`,{className:`mono`,children:t.connection||`—`}),(0,y.jsx)(`td`,{className:`mono`,children:t.format||`—`}),(0,y.jsx)(`td`,{className:`mono num`,children:e.content_type}),(0,y.jsx)(`td`,{className:`mono num`,children:S(e.size_bytes)}),(0,y.jsx)(`td`,{className:`mono num`,children:Math.max(0,Math.round(e.ttl_remaining_s))+`s`}),(0,y.jsx)(`td`,{children:(0,y.jsx)(`button`,{type:`button`,className:`btn sm ghost`,onClick:()=>void f(e.key),children:`Delete`})})]},e.key)})})]})}):(0,y.jsx)(`div`,{className:`hint`,children:`Nothing cached right now - call a query with a cache_ttl set, then check back.`})]})}var w=e=>({ok:!1,status:0,statusText:``,contentType:``,headers:[],page:1,size:0,hasMore:!1,elapsed:0,format:``,error:e});function T({entry:e}){let{showError:t}=i(),n=e.meta?.name||`cache-entry`,r=e.meta?.format||`json`,[a,o]=(0,v.useState)(null);return(0,v.useEffect)(()=>{let i=!0,a=performance.now();return d(`/api/v1/cache/entries/${encodeURIComponent(e.key)}`).then(e=>g(e,{page:1,size:0,format:r,elapsed:Math.round(performance.now()-a),filename:n})).then(e=>{i&&(e.error&&t(e.error,{status:e.status}),o(e))},e=>i&&o(w(`Network error: `+e.message))),()=>{i=!1}},[e.key,r,n,t]),(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(`div`,{className:`hint`,children:`The exact cached body, served with its real Content-Type - what a caller receives on a hit.`}),(0,y.jsx)(_,{result:a,running:!a,filename:n,onPage:()=>{},onPageSize:()=>{},layout:`drawer`})]})}export{x as CachingPage};
1
+ import{$ as e,C as t,D as n,H as r,L as i,O as a,Q as o,X as s,Z as c,g as l,o as u,w as d,z as f}from"./index-Efe2sJ9A.js";import{i as p,n as m,t as h}from"./StatTile-zVc4DpFR.js";import{n as g,t as _}from"./Results-BPGA2RXL.js";var v=e(o(),1),y=c();function b(){return r({queryKey:[`cache`,`entries`],queryFn:async()=>n(await t.GET(`/api/v1/cache/entries`)).items,retry:!1})}function x(){let e=f(),t=s(),n=l(),r=m(),i=u(),a=r.data??[],o=n.data?.find(e=>e.id===`cache`)?.rows[0]?.value??`…`,c=p(a,`queryapigate_cache_hits_total`),d=p(a,`queryapigate_cache_misses_total`),g=c+d,_=(i.data??[]).filter(e=>(e.cache_ttl??0)>0);return(0,y.jsxs)(y.Fragment,{children:[(0,y.jsxs)(`div`,{className:`page-head`,children:[(0,y.jsxs)(`div`,{className:`titles`,children:[(0,y.jsx)(`h1`,{children:`Caching`}),(0,y.jsxs)(`span`,{className:`sub`,children:[`The response cache backing saved queries' `,(0,y.jsx)(`code`,{children:`cache_ttl`}),` - see the Response cache row in Settings for the backend itself.`]})]}),(0,y.jsx)(`span`,{className:`spacer`}),(0,y.jsx)(`button`,{id:`refresh-caching`,type:`button`,className:`btn`,onClick:()=>{r.refetch(),t.invalidateQueries({queryKey:[`cache`,`entries`]})},children:`Refresh`})]}),(0,y.jsxs)(`div`,{id:`caching-body`,children:[(0,y.jsxs)(`div`,{className:`stat-tiles`,children:[(0,y.jsx)(h,{label:`Cache backend`,value:o}),(0,y.jsx)(h,{label:`Entries`,value:p(a,`queryapigate_cache_entries`)}),(0,y.jsx)(h,{label:`Hit rate`,value:g?(100*c/g).toFixed(1)+`%`:`—`}),(0,y.jsx)(h,{label:`Hits`,value:c}),(0,y.jsx)(h,{label:`Misses`,value:d})]}),(0,y.jsx)(`p`,{className:`sub-h`,style:{marginTop:14},children:`Saved queries with cache_ttl set`}),_.length?(0,y.jsx)(`div`,{className:`panel`,style:{overflowX:`auto`},children:(0,y.jsxs)(`table`,{className:`grid`,children:[(0,y.jsx)(`thead`,{children:(0,y.jsx)(`tr`,{children:[`Query`,`Collection`,`Connection`,`cache_ttl`].map((e,t)=>(0,y.jsx)(`th`,{className:t===3?`num`:``,children:e},e))})}),(0,y.jsx)(`tbody`,{children:_.map(t=>(0,y.jsxs)(`tr`,{children:[(0,y.jsx)(`td`,{children:(0,y.jsx)(`button`,{type:`button`,className:`target`,onClick:()=>e(`/queries/${encodeURIComponent(t.name)}`),children:t.name})}),(0,y.jsx)(`td`,{children:t.collection||(0,y.jsx)(`span`,{className:`dim`,children:`—`})}),(0,y.jsx)(`td`,{className:`mono`,children:t.connection_name||`—`}),(0,y.jsx)(`td`,{className:`mono num`,children:t.cache_ttl+`s`})]},t.name))})]})}):(0,y.jsx)(`div`,{className:`hint`,children:`No saved query declares a cache_ttl yet - set one on a read-only query to start caching its responses.`})]}),(0,y.jsx)(`div`,{id:`cache-entries-panel`,children:(0,y.jsx)(C,{})})]})}function S(e){return e<1024?e+` B`:(e/1024).toFixed(1)+` KB`}function C(){let e=s(),{openDrawer:n,showError:r,toast:o}=i(),c=b().data??[],l=()=>void e.invalidateQueries({queryKey:[`cache`,`entries`]}),u=e=>r(e.message),d=async()=>{if(window.confirm(`Evict all ${c.length} cache entries? The next call to each query will re-run for real.`))try{a(await t.DELETE(`/api/v1/cache/entries`)),o(`Cache cleared`),l()}catch(e){u(e)}},f=async e=>{try{a(await t.DELETE(`/api/v1/cache/entries/{key}`,{params:{path:{key:e}}})),o(`Entry deleted`),l()}catch(e){u(e)}};return(0,y.jsxs)(y.Fragment,{children:[(0,y.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:12},children:[(0,y.jsxs)(`div`,{className:`set-head`,style:{marginTop:14},children:[(0,y.jsx)(`h2`,{children:`Cache entries`}),(0,y.jsx)(`span`,{children:`${c.length}${c.length===1?` live entry`:` live entries`} right now.`})]}),(0,y.jsx)(`span`,{className:`spacer`}),(0,y.jsx)(`button`,{type:`button`,className:`btn sm`,disabled:!c.length,onClick:()=>void d(),children:`Clear cache`})]}),c.length?(0,y.jsx)(`div`,{className:`panel`,style:{overflowX:`auto`,marginTop:8},children:(0,y.jsxs)(`table`,{className:`grid`,children:[(0,y.jsx)(`thead`,{children:(0,y.jsx)(`tr`,{children:[`Query`,`Version`,`Connection`,`Format`,`Content type`,`Size`,`TTL left`,``].map((e,t)=>(0,y.jsx)(`th`,{className:t>=4&&t<=6?`num`:``,children:e},t))})}),(0,y.jsx)(`tbody`,{children:c.map(e=>{let t=e.meta??{};return(0,y.jsxs)(`tr`,{children:[(0,y.jsx)(`td`,{children:(0,y.jsx)(`button`,{type:`button`,className:`target`,onClick:()=>n({title:t.name||`Cache entry`,kicker:e.key,content:(0,y.jsx)(T,{entry:e})}),children:t.name||e.key.slice(0,12)+`…`})}),(0,y.jsx)(`td`,{className:`mono`,children:t.version===void 0?`—`:`v`+t.version}),(0,y.jsx)(`td`,{className:`mono`,children:t.connection||`—`}),(0,y.jsx)(`td`,{className:`mono`,children:t.format||`—`}),(0,y.jsx)(`td`,{className:`mono num`,children:e.content_type}),(0,y.jsx)(`td`,{className:`mono num`,children:S(e.size_bytes)}),(0,y.jsx)(`td`,{className:`mono num`,children:Math.max(0,Math.round(e.ttl_remaining_s))+`s`}),(0,y.jsx)(`td`,{children:(0,y.jsx)(`button`,{type:`button`,className:`btn sm ghost`,onClick:()=>void f(e.key),children:`Delete`})})]},e.key)})})]})}):(0,y.jsx)(`div`,{className:`hint`,children:`Nothing cached right now - call a query with a cache_ttl set, then check back.`})]})}var w=e=>({ok:!1,status:0,statusText:``,contentType:``,headers:[],page:1,size:0,hasMore:!1,elapsed:0,format:``,error:e});function T({entry:e}){let{showError:t}=i(),n=e.meta?.name||`cache-entry`,r=e.meta?.format||`json`,[a,o]=(0,v.useState)(null);return(0,v.useEffect)(()=>{let i=!0,a=performance.now();return d(`/api/v1/cache/entries/${encodeURIComponent(e.key)}`).then(e=>g(e,{page:1,size:0,format:r,elapsed:Math.round(performance.now()-a),filename:n})).then(e=>{i&&(e.error&&t(e.error,{status:e.status}),o(e))},e=>i&&o(w(`Network error: `+e.message))),()=>{i=!1}},[e.key,r,n,t]),(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(`div`,{className:`hint`,children:`The exact cached body, served with its real Content-Type - what a caller receives on a hit.`}),(0,y.jsx)(_,{result:a,running:!a,filename:n,onPage:()=>{},onPageSize:()=>{},layout:`drawer`})]})}export{x as CachingPage};
@@ -1,4 +1,4 @@
1
- import{Z as e}from"./index-DMiPsI_5.js";var t=new Set(`select from where and or not in is null as join left right inner outer full cross on using group by order having limit offset union all distinct insert into values update set delete create table view index drop alter with recursive case when then else end asc desc like ilike between exists returning primary key default top fetch next rows only over partition window count sum avg min max coalesce cast true false interval date timestamp extract filter lateral intersect except nulls first last`.split(` `)),n=/(--[^\n]*|\/\*[\s\S]*?(?:\*\/|$))|('(?:[^']|'')*'?)|("(?:[^"]|"")*"?|`[^`]*`?)|(::)|(:[A-Za-z_]\w*)|(\b\d+(?:\.\d+)?\b)|([A-Za-z_]\w*)/g;function r(e){let r=[],i=0;n.lastIndex=0;for(let a=n.exec(e);a;a=n.exec(e)){a.index>i&&r.push({cls:null,text:e.slice(i,a.index)});let o=null;a[1]?o=`c`:a[2]?o=`s`:a[5]?o=`p`:a[6]?o=`n`:a[7]&&t.has(a[7].toLowerCase())&&(o=`k`),r.push({cls:o,text:a[0]}),i=n.lastIndex}return i<e.length&&r.push({cls:null,text:e.slice(i)}),r}function i(e){let t=new Set;n.lastIndex=0;for(let r=n.exec(e);r;r=n.exec(e))r[5]&&t.add(r[5].slice(1));return[...t]}function a(e){return`'`+e.replace(/'/g,`'\\''`)+`'`}var o=new Set(`select from where having limit offset union with insert update set values join on group order delete left right inner outer full cross`.split(` `)),s=new Set([`left`,`right`,`inner`,`outer`,`full`,`cross`]);function c(e){let t=[],r=0,i=null;n.lastIndex=0;for(let a=n.exec(e);a;a=n.exec(e)){let c=e.slice(r,a.index).replace(/[ \t]{2,}/g,` `),l=a[7]?a[7].toLowerCase():null,u=l===`join`&&i!==null&&s.has(i)||l===`from`&&i===`delete`;l&&o.has(l)&&!u?t.push(c,`
1
+ import{Z as e}from"./index-Efe2sJ9A.js";var t=new Set(`select from where and or not in is null as join left right inner outer full cross on using group by order having limit offset union all distinct insert into values update set delete create table view index drop alter with recursive case when then else end asc desc like ilike between exists returning primary key default top fetch next rows only over partition window count sum avg min max coalesce cast true false interval date timestamp extract filter lateral intersect except nulls first last`.split(` `)),n=/(--[^\n]*|\/\*[\s\S]*?(?:\*\/|$))|('(?:[^']|'')*'?)|("(?:[^"]|"")*"?|`[^`]*`?)|(::)|(:[A-Za-z_]\w*)|(\b\d+(?:\.\d+)?\b)|([A-Za-z_]\w*)/g;function r(e){let r=[],i=0;n.lastIndex=0;for(let a=n.exec(e);a;a=n.exec(e)){a.index>i&&r.push({cls:null,text:e.slice(i,a.index)});let o=null;a[1]?o=`c`:a[2]?o=`s`:a[5]?o=`p`:a[6]?o=`n`:a[7]&&t.has(a[7].toLowerCase())&&(o=`k`),r.push({cls:o,text:a[0]}),i=n.lastIndex}return i<e.length&&r.push({cls:null,text:e.slice(i)}),r}function i(e){let t=new Set;n.lastIndex=0;for(let r=n.exec(e);r;r=n.exec(e))r[5]&&t.add(r[5].slice(1));return[...t]}function a(e){return`'`+e.replace(/'/g,`'\\''`)+`'`}var o=new Set(`select from where having limit offset union with insert update set values join on group order delete left right inner outer full cross`.split(` `)),s=new Set([`left`,`right`,`inner`,`outer`,`full`,`cross`]);function c(e){let t=[],r=0,i=null;n.lastIndex=0;for(let a=n.exec(e);a;a=n.exec(e)){let c=e.slice(r,a.index).replace(/[ \t]{2,}/g,` `),l=a[7]?a[7].toLowerCase():null,u=l===`join`&&i!==null&&s.has(i)||l===`from`&&i===`delete`;l&&o.has(l)&&!u?t.push(c,`
2
2
  `,a[0]):t.push(c,a[0]),i=l,r=n.lastIndex}return t.push(e.slice(r)),t.join(``).split(`
3
3
  `).map(e=>e.trim()).filter(e=>e!==``).join(`
4
4
  `)}var l=[`mysql`,`postgres`,`clickhouse`,`sqlite`,`duckdb`],u=e();function d({sql:e}){return(0,u.jsx)(`div`,{className:`codebox`,children:(0,u.jsx)(`div`,{className:`ln-rows`,children:e.split(`