@libredb/studio 0.9.7

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 (572) hide show
  1. package/.claude/settings.local.json +127 -0
  2. package/.cursorrules +426 -0
  3. package/.devin/wiki.json +143 -0
  4. package/.dockerignore +80 -0
  5. package/.env.example +159 -0
  6. package/.github/ISSUE_TEMPLATE/bug_report.md +49 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.md +29 -0
  8. package/.github/PULL_REQUEST_TEMPLATE.md +57 -0
  9. package/.github/workflows/ci.yml +185 -0
  10. package/.github/workflows/codeql.yml +57 -0
  11. package/.github/workflows/docker-build-push.yml +118 -0
  12. package/.github/workflows/helm-release.yml +113 -0
  13. package/CLAUDE.md +265 -0
  14. package/CODE_OF_CONDUCT.md +124 -0
  15. package/CONTRIBUTING.md +154 -0
  16. package/Dockerfile +73 -0
  17. package/LICENSE +21 -0
  18. package/README.md +614 -0
  19. package/SECURITY.md +107 -0
  20. package/artifacthub-repo.yml +4 -0
  21. package/bun.lock +1714 -0
  22. package/bunfig.toml +3 -0
  23. package/charts/libredb-studio/.helmignore +11 -0
  24. package/charts/libredb-studio/Chart.lock +6 -0
  25. package/charts/libredb-studio/Chart.yaml +50 -0
  26. package/charts/libredb-studio/README.md +206 -0
  27. package/charts/libredb-studio/templates/NOTES.txt +59 -0
  28. package/charts/libredb-studio/templates/_helpers.tpl +135 -0
  29. package/charts/libredb-studio/templates/configmap.yaml +37 -0
  30. package/charts/libredb-studio/templates/deployment.yaml +184 -0
  31. package/charts/libredb-studio/templates/hpa.yaml +32 -0
  32. package/charts/libredb-studio/templates/ingress.yaml +41 -0
  33. package/charts/libredb-studio/templates/networkpolicy.yaml +50 -0
  34. package/charts/libredb-studio/templates/pdb.yaml +18 -0
  35. package/charts/libredb-studio/templates/pvc.yaml +23 -0
  36. package/charts/libredb-studio/templates/secret.yaml +30 -0
  37. package/charts/libredb-studio/templates/seed-configmap.yaml +11 -0
  38. package/charts/libredb-studio/templates/service.yaml +22 -0
  39. package/charts/libredb-studio/templates/serviceaccount.yaml +13 -0
  40. package/charts/libredb-studio/values.schema.json +246 -0
  41. package/charts/libredb-studio/values.yaml +286 -0
  42. package/components.json +22 -0
  43. package/conductor/code_styleguides/typescript.md +43 -0
  44. package/conductor/product-guidelines.md +43 -0
  45. package/conductor/product.md +3 -0
  46. package/conductor/setup_state.json +1 -0
  47. package/conductor/tech-stack.md +39 -0
  48. package/conductor/tracks/enhance_postgres_monitoring_20251227/metadata.json +8 -0
  49. package/conductor/tracks/enhance_postgres_monitoring_20251227/plan.md +44 -0
  50. package/conductor/tracks/enhance_postgres_monitoring_20251227/spec.md +31 -0
  51. package/conductor/tracks.md +8 -0
  52. package/conductor/workflow.md +333 -0
  53. package/database-compose.yml +55 -0
  54. package/docker/postgres-init/01-extensions.sql +10 -0
  55. package/docker/postgres-init/02-sample-data.sql +585 -0
  56. package/docker/postgres.yml +68 -0
  57. package/docker-compose.yml +38 -0
  58. package/docs/AI_PLAN.md +74 -0
  59. package/docs/API_DOCS.md +875 -0
  60. package/docs/ARCHITECTURE.md +218 -0
  61. package/docs/DATABASE_PROVIDERS.md +358 -0
  62. package/docs/FEATURES.md +116 -0
  63. package/docs/HELM_CHART.md +252 -0
  64. package/docs/LOGIN_PAGE.md +178 -0
  65. package/docs/MONACO_EDITOR_PERFORMANCE.md +315 -0
  66. package/docs/OIDC_ARCH.md +681 -0
  67. package/docs/OIDC_SETUP.md +322 -0
  68. package/docs/POSTGRES_METRICS.md +516 -0
  69. package/docs/QUERY_OPTIMIZATION.md +370 -0
  70. package/docs/SEED_CONNECTIONS.md +468 -0
  71. package/docs/SQL_ALIAS_COMPLETION.md +190 -0
  72. package/docs/STORAGE_ARCHITECTURE.md +565 -0
  73. package/docs/STORAGE_QUICK_SETUP.md +419 -0
  74. package/docs/TECHNICAL_PLAN.md +36 -0
  75. package/docs/THEMING.md +345 -0
  76. package/docs/adding-a-new-database-provider.md +642 -0
  77. package/docs/backlogs/000-PLATFORM_DATA_SYNC_DATABASE.md +360 -0
  78. package/docs/backlogs/001-INLINE_DATA_EDITING.md +118 -0
  79. package/docs/backlogs/002-DATA_IMPORT.md +215 -0
  80. package/docs/backlogs/003-QUERY_TIME_MACHINE.md +183 -0
  81. package/docs/backlogs/004-AI_DATA_STORYTELLER.md +292 -0
  82. package/docs/backlogs/005-QUERY_PLAYGROUND.md +352 -0
  83. package/docs/backlogs/006-DATA_MASKING.md +418 -0
  84. package/docs/enterprise-features.md +718 -0
  85. package/docs/kubernetes-helm-chart-artifacthub-plan.md +803 -0
  86. package/docs/medium-koyeb-article-en.md +215 -0
  87. package/docs/plans/test-plans.md +445 -0
  88. package/docs/releases/RELEASE.V0.3.0.md +22 -0
  89. package/docs/releases/RELEASE.V0.4.0.md +154 -0
  90. package/docs/releases/RELEASE.V0.5.0.md +252 -0
  91. package/docs/releases/RELEASE_v0.5.6.md +145 -0
  92. package/docs/releases/RELEASE_v0.6.1.md +303 -0
  93. package/docs/releases/RELEASE_v0.6.7.md +292 -0
  94. package/docs/releases/RELEASE_v0.7.0.md +332 -0
  95. package/docs/releases/RELEASE_v0.8.0.md +521 -0
  96. package/docs/sampledb/titanic.sql +1379 -0
  97. package/docs/superpowers/plans/2026-03-25-seed-connections.md +1362 -0
  98. package/docs/superpowers/specs/2026-03-25-seed-connections-design.md +590 -0
  99. package/e2e/admin-dashboard.spec.ts +64 -0
  100. package/e2e/connection-management.spec.ts +58 -0
  101. package/e2e/export.spec.ts +34 -0
  102. package/e2e/login.spec.ts +85 -0
  103. package/e2e/query-execution.spec.ts +35 -0
  104. package/e2e/tab-management.spec.ts +64 -0
  105. package/eslint.config.mjs +28 -0
  106. package/fly.toml +43 -0
  107. package/next.config.ts +32 -0
  108. package/package.json +130 -0
  109. package/playwright.config.ts +34 -0
  110. package/postcss.config.mjs +7 -0
  111. package/public/favicon-32x32.png +0 -0
  112. package/public/favicon.ico +0 -0
  113. package/public/file.svg +1 -0
  114. package/public/globe.svg +1 -0
  115. package/public/logo.svg +32 -0
  116. package/public/next.svg +1 -0
  117. package/public/screenshots/code-generator.png +0 -0
  118. package/public/screenshots/connection-modal.png +0 -0
  119. package/public/screenshots/data-profiler.png +0 -0
  120. package/public/screenshots/erd-diagram.png +0 -0
  121. package/public/screenshots/hero-editor.png +0 -0
  122. package/public/screenshots/nl2sql.png +0 -0
  123. package/public/vercel.svg +1 -0
  124. package/public/window.svg +1 -0
  125. package/render.yaml +58 -0
  126. package/scripts/merge-lcov.mjs +239 -0
  127. package/sonar-project.properties +16 -0
  128. package/src/app/admin/error.tsx +46 -0
  129. package/src/app/admin/page.tsx +10 -0
  130. package/src/app/api/admin/audit/route.ts +52 -0
  131. package/src/app/api/admin/fleet-health/route.ts +81 -0
  132. package/src/app/api/ai/autopilot/route.ts +105 -0
  133. package/src/app/api/ai/chat/route.ts +132 -0
  134. package/src/app/api/ai/describe-schema/route.ts +52 -0
  135. package/src/app/api/ai/explain/route.ts +86 -0
  136. package/src/app/api/ai/impact/route.ts +97 -0
  137. package/src/app/api/ai/index-advisor/route.ts +98 -0
  138. package/src/app/api/ai/nl2sql/route.ts +87 -0
  139. package/src/app/api/ai/query-safety/route.ts +87 -0
  140. package/src/app/api/auth/login/route.ts +62 -0
  141. package/src/app/api/auth/logout/route.ts +25 -0
  142. package/src/app/api/auth/me/route.ts +10 -0
  143. package/src/app/api/auth/oidc/callback/route.ts +82 -0
  144. package/src/app/api/auth/oidc/login/route.ts +43 -0
  145. package/src/app/api/connections/managed/route.ts +35 -0
  146. package/src/app/api/db/cancel/route.ts +42 -0
  147. package/src/app/api/db/disconnect/route.ts +28 -0
  148. package/src/app/api/db/health/route.ts +49 -0
  149. package/src/app/api/db/maintenance/route.ts +72 -0
  150. package/src/app/api/db/monitoring/route.ts +62 -0
  151. package/src/app/api/db/multi-query/route.ts +116 -0
  152. package/src/app/api/db/pool-stats/route.ts +37 -0
  153. package/src/app/api/db/profile/route.ts +144 -0
  154. package/src/app/api/db/provider-meta/route.ts +49 -0
  155. package/src/app/api/db/query/route.ts +50 -0
  156. package/src/app/api/db/schema/route.ts +47 -0
  157. package/src/app/api/db/schema-snapshot/route.ts +42 -0
  158. package/src/app/api/db/test-connection/route.ts +55 -0
  159. package/src/app/api/db/transaction/route.ts +111 -0
  160. package/src/app/api/storage/[collection]/route.ts +67 -0
  161. package/src/app/api/storage/config/route.ts +17 -0
  162. package/src/app/api/storage/migrate/route.ts +45 -0
  163. package/src/app/api/storage/route.ts +32 -0
  164. package/src/app/error.tsx +49 -0
  165. package/src/app/global-error.tsx +55 -0
  166. package/src/app/globals.css +146 -0
  167. package/src/app/icon.svg +42 -0
  168. package/src/app/layout.tsx +34 -0
  169. package/src/app/login/login-form.tsx +301 -0
  170. package/src/app/login/page.tsx +11 -0
  171. package/src/app/monitoring/page.tsx +8 -0
  172. package/src/app/not-found.tsx +29 -0
  173. package/src/app/page.tsx +5 -0
  174. package/src/components/AIAutopilotPanel.tsx +238 -0
  175. package/src/components/CodeGenerator.tsx +271 -0
  176. package/src/components/CommandPalette.tsx +227 -0
  177. package/src/components/ConnectionModal.tsx +759 -0
  178. package/src/components/CreateTableModal.tsx +281 -0
  179. package/src/components/DataCharts.tsx +962 -0
  180. package/src/components/DataImportModal.tsx +582 -0
  181. package/src/components/DataProfiler.tsx +335 -0
  182. package/src/components/DatabaseDocs.tsx +251 -0
  183. package/src/components/MaskingSettings.tsx +414 -0
  184. package/src/components/MobileNav.tsx +50 -0
  185. package/src/components/NL2SQLPanel.tsx +281 -0
  186. package/src/components/PivotTable.tsx +257 -0
  187. package/src/components/QueryEditor.tsx +760 -0
  188. package/src/components/QueryHistory.tsx +344 -0
  189. package/src/components/QuerySafetyDialog.tsx +290 -0
  190. package/src/components/ResultsGrid.tsx +644 -0
  191. package/src/components/SaveQueryModal.tsx +104 -0
  192. package/src/components/SavedQueries.tsx +128 -0
  193. package/src/components/SchemaDiagram.tsx +473 -0
  194. package/src/components/SchemaDiff.tsx +473 -0
  195. package/src/components/SnapshotTimeline.tsx +116 -0
  196. package/src/components/Studio.tsx +639 -0
  197. package/src/components/TestDataGenerator.tsx +261 -0
  198. package/src/components/VisualExplain.tsx +820 -0
  199. package/src/components/admin/AdminDashboard.tsx +163 -0
  200. package/src/components/admin/tabs/AuditTab.tsx +531 -0
  201. package/src/components/admin/tabs/MonitoringEmbed.tsx +11 -0
  202. package/src/components/admin/tabs/OperationsTab.tsx +646 -0
  203. package/src/components/admin/tabs/OverviewTab.tsx +1328 -0
  204. package/src/components/admin/tabs/SecurityTab.tsx +284 -0
  205. package/src/components/community-section.tsx +92 -0
  206. package/src/components/icons/db-icons.tsx +84 -0
  207. package/src/components/libredb-logo.tsx +61 -0
  208. package/src/components/monitoring/MonitoringDashboard.tsx +345 -0
  209. package/src/components/monitoring/tabs/MetricChart.tsx +82 -0
  210. package/src/components/monitoring/tabs/OverviewTab.tsx +263 -0
  211. package/src/components/monitoring/tabs/PerformanceTab.tsx +254 -0
  212. package/src/components/monitoring/tabs/PoolTab.tsx +174 -0
  213. package/src/components/monitoring/tabs/QueriesTab.tsx +287 -0
  214. package/src/components/monitoring/tabs/SessionsTab.tsx +316 -0
  215. package/src/components/monitoring/tabs/StorageTab.tsx +335 -0
  216. package/src/components/monitoring/tabs/TablesTab.tsx +300 -0
  217. package/src/components/results-grid/ResultCard.tsx +111 -0
  218. package/src/components/results-grid/RowDetailSheet.tsx +178 -0
  219. package/src/components/results-grid/StatsBar.tsx +201 -0
  220. package/src/components/results-grid/index.ts +1 -0
  221. package/src/components/results-grid/utils.ts +23 -0
  222. package/src/components/schema-explorer/ColumnList.tsx +53 -0
  223. package/src/components/schema-explorer/SchemaExplorer.tsx +182 -0
  224. package/src/components/schema-explorer/TableItem.tsx +210 -0
  225. package/src/components/schema-explorer/index.ts +1 -0
  226. package/src/components/sidebar/ConnectionItem.tsx +105 -0
  227. package/src/components/sidebar/ConnectionsList.tsx +62 -0
  228. package/src/components/sidebar/Sidebar.tsx +130 -0
  229. package/src/components/sidebar/index.ts +2 -0
  230. package/src/components/studio/BottomPanel.tsx +286 -0
  231. package/src/components/studio/QueryToolbar.tsx +180 -0
  232. package/src/components/studio/StudioDesktopHeader.tsx +114 -0
  233. package/src/components/studio/StudioMobileHeader.tsx +340 -0
  234. package/src/components/studio/StudioTabBar.tsx +82 -0
  235. package/src/components/studio/index.ts +5 -0
  236. package/src/components/ui/accordion.tsx +66 -0
  237. package/src/components/ui/alert-dialog.tsx +157 -0
  238. package/src/components/ui/alert.tsx +66 -0
  239. package/src/components/ui/aspect-ratio.tsx +11 -0
  240. package/src/components/ui/avatar.tsx +53 -0
  241. package/src/components/ui/badge.tsx +46 -0
  242. package/src/components/ui/breadcrumb.tsx +109 -0
  243. package/src/components/ui/button-group.tsx +83 -0
  244. package/src/components/ui/button.tsx +60 -0
  245. package/src/components/ui/calendar.tsx +216 -0
  246. package/src/components/ui/card.tsx +92 -0
  247. package/src/components/ui/carousel.tsx +241 -0
  248. package/src/components/ui/chart.tsx +357 -0
  249. package/src/components/ui/checkbox.tsx +32 -0
  250. package/src/components/ui/collapsible.tsx +33 -0
  251. package/src/components/ui/command.tsx +184 -0
  252. package/src/components/ui/context-menu.tsx +252 -0
  253. package/src/components/ui/dialog.tsx +143 -0
  254. package/src/components/ui/drawer.tsx +135 -0
  255. package/src/components/ui/dropdown-menu.tsx +257 -0
  256. package/src/components/ui/empty.tsx +104 -0
  257. package/src/components/ui/field.tsx +248 -0
  258. package/src/components/ui/form.tsx +167 -0
  259. package/src/components/ui/hover-card.tsx +44 -0
  260. package/src/components/ui/input-group.tsx +170 -0
  261. package/src/components/ui/input-otp.tsx +77 -0
  262. package/src/components/ui/input.tsx +21 -0
  263. package/src/components/ui/item.tsx +193 -0
  264. package/src/components/ui/kbd.tsx +28 -0
  265. package/src/components/ui/label.tsx +24 -0
  266. package/src/components/ui/menubar.tsx +276 -0
  267. package/src/components/ui/navigation-menu.tsx +168 -0
  268. package/src/components/ui/pagination.tsx +127 -0
  269. package/src/components/ui/popover.tsx +48 -0
  270. package/src/components/ui/progress.tsx +31 -0
  271. package/src/components/ui/radio-group.tsx +45 -0
  272. package/src/components/ui/resizable.tsx +56 -0
  273. package/src/components/ui/scroll-area.tsx +58 -0
  274. package/src/components/ui/select.tsx +187 -0
  275. package/src/components/ui/separator.tsx +28 -0
  276. package/src/components/ui/sheet.tsx +139 -0
  277. package/src/components/ui/sidebar.tsx +726 -0
  278. package/src/components/ui/skeleton.tsx +13 -0
  279. package/src/components/ui/slider.tsx +63 -0
  280. package/src/components/ui/sonner.tsx +40 -0
  281. package/src/components/ui/spinner.tsx +16 -0
  282. package/src/components/ui/switch.tsx +31 -0
  283. package/src/components/ui/table.tsx +116 -0
  284. package/src/components/ui/tabs.tsx +66 -0
  285. package/src/components/ui/textarea.tsx +18 -0
  286. package/src/components/ui/toggle-group.tsx +83 -0
  287. package/src/components/ui/toggle.tsx +47 -0
  288. package/src/components/ui/tooltip.tsx +61 -0
  289. package/src/exports/components.ts +15 -0
  290. package/src/exports/index.ts +4 -0
  291. package/src/exports/providers.ts +4 -0
  292. package/src/exports/types.ts +26 -0
  293. package/src/hooks/use-ai-chat.ts +182 -0
  294. package/src/hooks/use-all-connections.ts +66 -0
  295. package/src/hooks/use-api-call.ts +71 -0
  296. package/src/hooks/use-auth.ts +51 -0
  297. package/src/hooks/use-connection-form.ts +349 -0
  298. package/src/hooks/use-connection-manager.ts +169 -0
  299. package/src/hooks/use-connection-payload.ts +15 -0
  300. package/src/hooks/use-inline-editing.ts +109 -0
  301. package/src/hooks/use-mobile.ts +20 -0
  302. package/src/hooks/use-monitoring-data.ts +270 -0
  303. package/src/hooks/use-provider-metadata.ts +62 -0
  304. package/src/hooks/use-query-execution.ts +478 -0
  305. package/src/hooks/use-storage-sync.ts +259 -0
  306. package/src/hooks/use-tab-manager.ts +231 -0
  307. package/src/hooks/use-toast.ts +20 -0
  308. package/src/hooks/use-transaction-control.ts +64 -0
  309. package/src/lib/api/error-codes.ts +30 -0
  310. package/src/lib/api/errors.ts +236 -0
  311. package/src/lib/api/with-error-handler.ts +41 -0
  312. package/src/lib/audit.ts +105 -0
  313. package/src/lib/auth.ts +87 -0
  314. package/src/lib/connection-string-parser.ts +172 -0
  315. package/src/lib/data-masking.ts +385 -0
  316. package/src/lib/db/base-provider.ts +325 -0
  317. package/src/lib/db/errors.ts +317 -0
  318. package/src/lib/db/factory.ts +324 -0
  319. package/src/lib/db/index.ts +123 -0
  320. package/src/lib/db/providers/document/index.ts +6 -0
  321. package/src/lib/db/providers/document/mongodb.ts +992 -0
  322. package/src/lib/db/providers/keyvalue/redis.ts +554 -0
  323. package/src/lib/db/providers/sql/index.ts +11 -0
  324. package/src/lib/db/providers/sql/mssql.ts +1065 -0
  325. package/src/lib/db/providers/sql/mysql.ts +978 -0
  326. package/src/lib/db/providers/sql/oracle.ts +1044 -0
  327. package/src/lib/db/providers/sql/postgres.ts +1179 -0
  328. package/src/lib/db/providers/sql/sql-base.ts +174 -0
  329. package/src/lib/db/providers/sql/sqlite.ts +721 -0
  330. package/src/lib/db/types.ts +437 -0
  331. package/src/lib/db/utils/pool-manager.ts +287 -0
  332. package/src/lib/db/utils/query-limiter.ts +239 -0
  333. package/src/lib/db-ui-config.ts +86 -0
  334. package/src/lib/editor/mongodb-completions.ts +172 -0
  335. package/src/lib/editor/sql-completions.ts +280 -0
  336. package/src/lib/llm/base-provider.ts +117 -0
  337. package/src/lib/llm/factory.ts +102 -0
  338. package/src/lib/llm/index.ts +90 -0
  339. package/src/lib/llm/providers/custom.ts +181 -0
  340. package/src/lib/llm/providers/gemini.ts +126 -0
  341. package/src/lib/llm/providers/ollama.ts +154 -0
  342. package/src/lib/llm/providers/openai.ts +146 -0
  343. package/src/lib/llm/types.ts +173 -0
  344. package/src/lib/llm/utils/config.ts +187 -0
  345. package/src/lib/llm/utils/retry.ts +119 -0
  346. package/src/lib/llm/utils/streaming.ts +202 -0
  347. package/src/lib/logger.ts +127 -0
  348. package/src/lib/monitoring-thresholds.ts +44 -0
  349. package/src/lib/oidc.ts +262 -0
  350. package/src/lib/query-generators.ts +61 -0
  351. package/src/lib/schema-diff/diff-engine.ts +273 -0
  352. package/src/lib/schema-diff/migration-generator.ts +208 -0
  353. package/src/lib/schema-diff/types.ts +55 -0
  354. package/src/lib/seed/config-loader.ts +79 -0
  355. package/src/lib/seed/connection-filter.ts +49 -0
  356. package/src/lib/seed/credential-resolver.ts +62 -0
  357. package/src/lib/seed/index.ts +40 -0
  358. package/src/lib/seed/resolve-connection.ts +57 -0
  359. package/src/lib/seed/types.ts +69 -0
  360. package/src/lib/sql/alias-extractor.ts +267 -0
  361. package/src/lib/sql/index.ts +8 -0
  362. package/src/lib/sql/statement-splitter.ts +167 -0
  363. package/src/lib/sql/types.ts +40 -0
  364. package/src/lib/ssh/tunnel.ts +142 -0
  365. package/src/lib/storage/factory.ts +84 -0
  366. package/src/lib/storage/index.ts +14 -0
  367. package/src/lib/storage/local-storage.ts +99 -0
  368. package/src/lib/storage/providers/postgres.ts +225 -0
  369. package/src/lib/storage/providers/sqlite.ts +153 -0
  370. package/src/lib/storage/storage-facade.ts +272 -0
  371. package/src/lib/storage/types.ts +75 -0
  372. package/src/lib/time-series-buffer.ts +58 -0
  373. package/src/lib/types.ts +173 -0
  374. package/src/lib/utils.ts +6 -0
  375. package/src/proxy.ts +104 -0
  376. package/src/types/db-drivers.d.ts +23 -0
  377. package/src/types/html2canvas.d.ts +9 -0
  378. package/tests/api/admin/audit.test.ts +178 -0
  379. package/tests/api/admin/fleet-health.test.ts +183 -0
  380. package/tests/api/ai/autopilot.test.ts +174 -0
  381. package/tests/api/ai/chat.test.ts +250 -0
  382. package/tests/api/ai/describe-schema.test.ts +266 -0
  383. package/tests/api/ai/explain.test.ts +199 -0
  384. package/tests/api/ai/impact.test.ts +168 -0
  385. package/tests/api/ai/index-advisor.test.ts +171 -0
  386. package/tests/api/ai/nl2sql.test.ts +202 -0
  387. package/tests/api/ai/query-safety.test.ts +196 -0
  388. package/tests/api/auth/login.test.ts +170 -0
  389. package/tests/api/auth/logout.test.ts +140 -0
  390. package/tests/api/auth/me.test.ts +73 -0
  391. package/tests/api/auth/oidc-callback.test.ts +215 -0
  392. package/tests/api/auth/oidc-login.test.ts +127 -0
  393. package/tests/api/db/cancel.test.ts +198 -0
  394. package/tests/api/db/disconnect.test.ts +124 -0
  395. package/tests/api/db/health.test.ts +222 -0
  396. package/tests/api/db/maintenance.test.ts +263 -0
  397. package/tests/api/db/monitoring.test.ts +221 -0
  398. package/tests/api/db/multi-query.test.ts +316 -0
  399. package/tests/api/db/pool-stats.test.ts +135 -0
  400. package/tests/api/db/profile.test.ts +330 -0
  401. package/tests/api/db/provider-meta.test.ts +193 -0
  402. package/tests/api/db/query.test.ts +314 -0
  403. package/tests/api/db/schema-snapshot.test.ts +170 -0
  404. package/tests/api/db/schema.test.ts +191 -0
  405. package/tests/api/db/test-connection.test.ts +185 -0
  406. package/tests/api/db/transaction.test.ts +314 -0
  407. package/tests/api/proxy.test.ts +191 -0
  408. package/tests/api/seed/managed-route.test.ts +113 -0
  409. package/tests/api/storage/config.test.ts +42 -0
  410. package/tests/api/storage/storage-routes.test.ts +309 -0
  411. package/tests/components/AIAutopilotPanel.test.tsx +756 -0
  412. package/tests/components/AdminPage.test.tsx +33 -0
  413. package/tests/components/CodeGenerator.test.tsx +182 -0
  414. package/tests/components/CommandPalette.test.tsx +428 -0
  415. package/tests/components/CommunitySection.test.tsx +91 -0
  416. package/tests/components/ConnectionModal.mobile.test.tsx +284 -0
  417. package/tests/components/ConnectionModal.test.tsx +570 -0
  418. package/tests/components/CreateTableModal.test.tsx +383 -0
  419. package/tests/components/DataCharts.test.tsx +739 -0
  420. package/tests/components/DataImportModal.test.tsx +751 -0
  421. package/tests/components/DataProfiler.test.tsx +589 -0
  422. package/tests/components/DatabaseDocs.test.tsx +353 -0
  423. package/tests/components/LoginPage.test.tsx +163 -0
  424. package/tests/components/LoginPageOIDC.test.tsx +92 -0
  425. package/tests/components/MaskingSettings.test.tsx +498 -0
  426. package/tests/components/MobileNav.test.tsx +30 -0
  427. package/tests/components/MonitoringPage.test.tsx +32 -0
  428. package/tests/components/NL2SQLPanel.test.tsx +621 -0
  429. package/tests/components/Page.test.tsx +33 -0
  430. package/tests/components/PivotTable.test.tsx +350 -0
  431. package/tests/components/QueryEditor.test.tsx +1730 -0
  432. package/tests/components/QueryHistory.test.tsx +572 -0
  433. package/tests/components/QuerySafetyDialog.test.tsx +586 -0
  434. package/tests/components/ResultsGrid.test.tsx +804 -0
  435. package/tests/components/RootLayout.test.tsx +83 -0
  436. package/tests/components/SaveQueryModal.test.tsx +25 -0
  437. package/tests/components/SavedQueries.test.tsx +43 -0
  438. package/tests/components/SchemaDiagram.test.tsx +1034 -0
  439. package/tests/components/SchemaDiff.test.tsx +906 -0
  440. package/tests/components/SnapshotTimeline.test.tsx +174 -0
  441. package/tests/components/Studio.test.tsx +1030 -0
  442. package/tests/components/TestDataGenerator.test.tsx +291 -0
  443. package/tests/components/VisualExplain.test.tsx +704 -0
  444. package/tests/components/admin/AdminDashboard.test.tsx +205 -0
  445. package/tests/components/admin/AuditTab.test.tsx +220 -0
  446. package/tests/components/admin/MonitoringEmbed.test.tsx +58 -0
  447. package/tests/components/admin/OperationsTab.test.tsx +975 -0
  448. package/tests/components/admin/OverviewTab.test.tsx +254 -0
  449. package/tests/components/admin/SecurityTab.test.tsx +467 -0
  450. package/tests/components/monitoring/MetricChart.test.tsx +111 -0
  451. package/tests/components/monitoring/MonitoringDashboard.test.tsx +259 -0
  452. package/tests/components/monitoring/OverviewTab.test.tsx +78 -0
  453. package/tests/components/monitoring/PerformanceTab.test.tsx +87 -0
  454. package/tests/components/monitoring/PoolTab.test.tsx +42 -0
  455. package/tests/components/monitoring/QueriesTab.test.tsx +80 -0
  456. package/tests/components/monitoring/SessionsTab.test.tsx +154 -0
  457. package/tests/components/monitoring/StorageTab.test.tsx +127 -0
  458. package/tests/components/monitoring/TablesTab.test.tsx +153 -0
  459. package/tests/components/results-grid/ResultCard.test.tsx +105 -0
  460. package/tests/components/results-grid/RowDetailSheet.test.tsx +308 -0
  461. package/tests/components/results-grid/StatsBar.test.tsx +162 -0
  462. package/tests/components/schema-explorer/ColumnList.test.tsx +151 -0
  463. package/tests/components/schema-explorer/SchemaExplorer.test.tsx +461 -0
  464. package/tests/components/schema-explorer/TableItem.test.tsx +415 -0
  465. package/tests/components/sidebar/ConnectionItem.test.tsx +201 -0
  466. package/tests/components/sidebar/ConnectionsList.test.tsx +176 -0
  467. package/tests/components/sidebar/Sidebar.test.tsx +187 -0
  468. package/tests/components/studio/BottomPanel.test.tsx +383 -0
  469. package/tests/components/studio/QueryToolbar.test.tsx +321 -0
  470. package/tests/components/studio/StudioDesktopHeader.test.tsx +377 -0
  471. package/tests/components/studio/StudioMobileHeader.test.tsx +198 -0
  472. package/tests/components/studio/StudioTabBar.test.tsx +331 -0
  473. package/tests/fixtures/connections.ts +96 -0
  474. package/tests/fixtures/masking-configs.ts +86 -0
  475. package/tests/fixtures/query-results.ts +71 -0
  476. package/tests/fixtures/schemas.ts +64 -0
  477. package/tests/fixtures/seed-connections/invalid-config.yaml +7 -0
  478. package/tests/fixtures/seed-connections/minimal-config.yaml +8 -0
  479. package/tests/fixtures/seed-connections/mixed-credentials.yaml +23 -0
  480. package/tests/fixtures/seed-connections/multi-role-config.yaml +30 -0
  481. package/tests/fixtures/seed-connections/valid-config.json +15 -0
  482. package/tests/fixtures/seed-connections/valid-config.yaml +51 -0
  483. package/tests/helpers/mock-fetch.ts +59 -0
  484. package/tests/helpers/mock-monaco.ts +112 -0
  485. package/tests/helpers/mock-navigation.ts +28 -0
  486. package/tests/helpers/mock-next.ts +80 -0
  487. package/tests/helpers/mock-provider.ts +133 -0
  488. package/tests/helpers/mock-sonner.ts +29 -0
  489. package/tests/helpers/render-with-providers.tsx +19 -0
  490. package/tests/hooks/use-ai-chat.test.ts +600 -0
  491. package/tests/hooks/use-auth.test.ts +371 -0
  492. package/tests/hooks/use-connection-form.test.ts +743 -0
  493. package/tests/hooks/use-connection-manager.test.ts +466 -0
  494. package/tests/hooks/use-inline-editing.test.ts +321 -0
  495. package/tests/hooks/use-mobile.test.ts +177 -0
  496. package/tests/hooks/use-monitoring-data.test.ts +819 -0
  497. package/tests/hooks/use-provider-metadata.test.ts +228 -0
  498. package/tests/hooks/use-query-execution.test.ts +1212 -0
  499. package/tests/hooks/use-tab-manager.test.ts +756 -0
  500. package/tests/hooks/use-toast.test.ts +74 -0
  501. package/tests/hooks/use-transaction-control.test.ts +211 -0
  502. package/tests/integration/db/mongodb-provider.test.ts +698 -0
  503. package/tests/integration/db/mssql-provider.test.ts +840 -0
  504. package/tests/integration/db/mysql-provider.test.ts +872 -0
  505. package/tests/integration/db/oracle-provider.test.ts +843 -0
  506. package/tests/integration/db/postgres-provider.test.ts +1382 -0
  507. package/tests/integration/db/redis-provider.test.ts +526 -0
  508. package/tests/integration/db/sqlite-provider.test.ts +480 -0
  509. package/tests/integration/seed/seed-pipeline.test.ts +102 -0
  510. package/tests/isolated/factory-singleton.test.ts +150 -0
  511. package/tests/isolated/use-storage-sync.test.ts +389 -0
  512. package/tests/run-components.sh +196 -0
  513. package/tests/setup-dom.ts +58 -0
  514. package/tests/setup.ts +40 -0
  515. package/tests/unit/api-errors.test.ts +210 -0
  516. package/tests/unit/code-generator-functions.test.ts +271 -0
  517. package/tests/unit/components/column-list.test.tsx +190 -0
  518. package/tests/unit/components/data-import-modal.test.tsx +441 -0
  519. package/tests/unit/components/studio-mobile-header.test.tsx +327 -0
  520. package/tests/unit/data-charts-functions.test.ts +496 -0
  521. package/tests/unit/data-import-functions.test.ts +320 -0
  522. package/tests/unit/data-import-utils.test.ts +125 -0
  523. package/tests/unit/db/base-provider.test.ts +517 -0
  524. package/tests/unit/db/errors.test.ts +403 -0
  525. package/tests/unit/db/factory.test.ts +436 -0
  526. package/tests/unit/db/pool-manager.test.ts +440 -0
  527. package/tests/unit/db/query-limiter.test.ts +387 -0
  528. package/tests/unit/db/sql-base.test.ts +438 -0
  529. package/tests/unit/lib/api/error-codes.test.ts +39 -0
  530. package/tests/unit/lib/audit.test.ts +326 -0
  531. package/tests/unit/lib/auth.test.ts +146 -0
  532. package/tests/unit/lib/connection-string-parser.test.ts +424 -0
  533. package/tests/unit/lib/data-masking.test.ts +583 -0
  534. package/tests/unit/lib/db-icons.test.tsx +41 -0
  535. package/tests/unit/lib/monitoring-thresholds.test.ts +133 -0
  536. package/tests/unit/lib/oidc.test.ts +509 -0
  537. package/tests/unit/lib/query-generators.test.ts +127 -0
  538. package/tests/unit/lib/storage/factory.test.ts +71 -0
  539. package/tests/unit/lib/storage/local-storage.test.ts +114 -0
  540. package/tests/unit/lib/storage/providers/postgres.test.ts +312 -0
  541. package/tests/unit/lib/storage/providers/sqlite.test.ts +232 -0
  542. package/tests/unit/lib/storage/storage-facade-extended.test.ts +331 -0
  543. package/tests/unit/lib/storage/storage-facade.test.ts +184 -0
  544. package/tests/unit/lib/storage.test.ts +317 -0
  545. package/tests/unit/lib/time-series-buffer.test.ts +212 -0
  546. package/tests/unit/lib/utils.test.ts +24 -0
  547. package/tests/unit/llm/base-provider.test.ts +238 -0
  548. package/tests/unit/llm/config.test.ts +262 -0
  549. package/tests/unit/llm/custom-provider.test.ts +281 -0
  550. package/tests/unit/llm/gemini-provider.test.ts +248 -0
  551. package/tests/unit/llm/llm-factory.test.ts +155 -0
  552. package/tests/unit/llm/ollama-provider.test.ts +288 -0
  553. package/tests/unit/llm/openai-provider.test.ts +324 -0
  554. package/tests/unit/llm/retry.test.ts +180 -0
  555. package/tests/unit/llm/streaming.test.ts +355 -0
  556. package/tests/unit/logger.test.ts +198 -0
  557. package/tests/unit/mongodb-completions.test.ts +516 -0
  558. package/tests/unit/pivot-table-functions.test.ts +76 -0
  559. package/tests/unit/query-cancelled-error.test.ts +81 -0
  560. package/tests/unit/schema-diff/diff-engine.test.ts +367 -0
  561. package/tests/unit/schema-diff/migration-generator.test.ts +513 -0
  562. package/tests/unit/seed/config-loader.test.ts +73 -0
  563. package/tests/unit/seed/connection-filter.test.ts +91 -0
  564. package/tests/unit/seed/credential-resolver.test.ts +85 -0
  565. package/tests/unit/seed/index.test.ts +72 -0
  566. package/tests/unit/seed/resolve-connection.test.ts +74 -0
  567. package/tests/unit/seed/types.test.ts +129 -0
  568. package/tests/unit/sql/alias-extractor.test.ts +444 -0
  569. package/tests/unit/sql/statement-splitter.test.ts +348 -0
  570. package/tests/unit/sql-completions.test.ts +463 -0
  571. package/tests/unit/ssh-tunnel.test.ts +465 -0
  572. package/tsconfig.json +42 -0
@@ -0,0 +1,646 @@
1
+ 'use client';
2
+
3
+ import React, { useState, useEffect, useMemo, useCallback } from 'react';
4
+ import { Button } from '@/components/ui/button';
5
+ import { Badge } from '@/components/ui/badge';
6
+ import { Input } from '@/components/ui/input';
7
+ import { Skeleton } from '@/components/ui/skeleton';
8
+ import {
9
+ Select,
10
+ SelectContent,
11
+ SelectItem,
12
+ SelectTrigger,
13
+ SelectValue,
14
+ } from '@/components/ui/select';
15
+ import {
16
+ AlertDialog,
17
+ AlertDialogAction,
18
+ AlertDialogCancel,
19
+ AlertDialogContent,
20
+ AlertDialogDescription,
21
+ AlertDialogFooter,
22
+ AlertDialogHeader,
23
+ AlertDialogTitle,
24
+ } from '@/components/ui/alert-dialog';
25
+ import {
26
+ Table,
27
+ TableBody,
28
+ TableCell,
29
+ TableHead,
30
+ TableHeader,
31
+ TableRow,
32
+ } from '@/components/ui/table';
33
+ import {
34
+ Tooltip,
35
+ TooltipContent,
36
+ TooltipProvider,
37
+ TooltipTrigger,
38
+ } from '@/components/ui/tooltip';
39
+ import {
40
+ RefreshCw,
41
+ Zap,
42
+ HardDrive,
43
+ Search,
44
+ Clock,
45
+ Users,
46
+ Skull,
47
+ Database,
48
+ ShieldAlert,
49
+ Loader2,
50
+ CheckCircle2,
51
+ XCircle,
52
+ Table2,
53
+ } from 'lucide-react';
54
+ import { useMonitoringData } from '@/hooks/use-monitoring-data';
55
+ import { storage } from '@/lib/storage';
56
+ import { useAllConnections } from '@/hooks/use-all-connections';
57
+ import type { DatabaseConnection } from '@/lib/types';
58
+ import type { ActiveSessionDetails } from '@/lib/db/types';
59
+
60
+ interface OperationLogEntry {
61
+ id: string;
62
+ timestamp: Date;
63
+ type: string;
64
+ target: string;
65
+ result: 'success' | 'failure';
66
+ duration: number;
67
+ error?: string;
68
+ }
69
+
70
+ export function OperationsTab() {
71
+ const [connections, setConnections] = useState<DatabaseConnection[]>([]);
72
+ const [selectedConnection, setSelectedConnection] = useState<DatabaseConnection | null>(null);
73
+ const [operationLog, setOperationLog] = useState<OperationLogEntry[]>([]);
74
+ const [confirmKill, setConfirmKill] = useState<ActiveSessionDetails | null>(null);
75
+ const [killingPid, setKillingPid] = useState<number | string | null>(null);
76
+ const [actionLoading, setActionLoading] = useState<string | null>(null);
77
+
78
+ const monitoringOptions = useMemo(
79
+ () => ({ includeTables: true, includeIndexes: false, includeStorage: false }),
80
+ []
81
+ );
82
+
83
+ const {
84
+ data,
85
+ loading,
86
+ error,
87
+ refresh,
88
+ killSession,
89
+ runMaintenance,
90
+ } = useMonitoringData(selectedConnection, monitoringOptions);
91
+
92
+ const { connections: allConns } = useAllConnections();
93
+ useEffect(() => {
94
+ if (allConns.length === 0) return;
95
+ setConnections(allConns);
96
+ const savedId = storage.getActiveConnectionId();
97
+ const saved = savedId ? allConns.find((c) => c.id === savedId) : null;
98
+ setSelectedConnection(saved ?? allConns[0]);
99
+ }, [allConns]);
100
+
101
+ const handleConnectionChange = (id: string) => {
102
+ const conn = connections.find((c) => c.id === id);
103
+ if (conn) setSelectedConnection(conn);
104
+ };
105
+
106
+ const addLogEntry = useCallback((type: string, target: string, result: 'success' | 'failure', duration: number, error?: string) => {
107
+ setOperationLog((prev) => [
108
+ {
109
+ id: `${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
110
+ timestamp: new Date(),
111
+ type,
112
+ target,
113
+ result,
114
+ duration,
115
+ error,
116
+ },
117
+ ...prev,
118
+ ].slice(0, 50));
119
+ }, []);
120
+
121
+ const handleRunMaintenance = async (type: string, target?: string) => {
122
+ const actionId = `${type}-${target || 'global'}`;
123
+ setActionLoading(actionId);
124
+ const start = Date.now();
125
+ try {
126
+ const success = await runMaintenance(type, target);
127
+ const duration = Date.now() - start;
128
+ addLogEntry(type.toUpperCase(), target || 'all', success ? 'success' : 'failure', duration);
129
+ } catch {
130
+ const duration = Date.now() - start;
131
+ addLogEntry(type.toUpperCase(), target || 'all', 'failure', duration);
132
+ } finally {
133
+ setActionLoading(null);
134
+ }
135
+ };
136
+
137
+ const handleKillClick = (session: ActiveSessionDetails) => {
138
+ setConfirmKill(session);
139
+ };
140
+
141
+ const handleConfirmKill = async () => {
142
+ if (!confirmKill) return;
143
+ setKillingPid(confirmKill.pid);
144
+ setConfirmKill(null);
145
+ const start = Date.now();
146
+ const success = await killSession(confirmKill.pid);
147
+ const duration = Date.now() - start;
148
+ addLogEntry('KILL', `PID:${confirmKill.pid}`, success ? 'success' : 'failure', duration);
149
+ setKillingPid(null);
150
+ };
151
+
152
+ const sessions = data?.activeSessions ?? [];
153
+ const tables = data?.tables ?? [];
154
+ const [tableSearch, setTableSearch] = useState('');
155
+ const filteredTables = tables.filter((t) =>
156
+ t.tableName.toLowerCase().includes(tableSearch.toLowerCase())
157
+ );
158
+
159
+ const activeCount = sessions.filter((s) => s.state === 'active').length;
160
+ const idleCount = sessions.filter((s) => s.state === 'idle').length;
161
+ const idleInTxCount = sessions.filter((s) => s.state?.includes('idle in transaction')).length;
162
+ const waitingCount = sessions.filter((s) => s.waitEventType).length;
163
+
164
+ const getStateBadge = (state: string) => {
165
+ switch (state) {
166
+ case 'active':
167
+ return <Badge className="bg-green-500/10 text-green-400 border border-green-500/20 text-[9px]">Active</Badge>;
168
+ case 'idle':
169
+ return <Badge variant="secondary" className="text-[9px]">Idle</Badge>;
170
+ case 'idle in transaction':
171
+ return <Badge className="bg-yellow-500/10 text-yellow-400 border border-yellow-500/20 text-[9px]">Idle TX</Badge>;
172
+ case 'idle in transaction (aborted)':
173
+ return <Badge className="bg-red-500/10 text-red-400 border border-red-500/20 text-[9px]">Abort</Badge>;
174
+ default:
175
+ return <Badge variant="outline" className="text-[9px]">{state}</Badge>;
176
+ }
177
+ };
178
+
179
+ if (connections.length === 0) {
180
+ return (
181
+ <div className="flex flex-col items-center justify-center p-12 text-center">
182
+ <Database className="h-12 w-12 text-zinc-700 mb-4" />
183
+ <h3 className="text-lg font-semibold text-zinc-300 mb-2">
184
+ No Database Connections
185
+ </h3>
186
+ <p className="text-zinc-500 text-sm">
187
+ Please add a database connection from the editor first.
188
+ </p>
189
+ </div>
190
+ );
191
+ }
192
+
193
+ return (
194
+ <div className="space-y-6">
195
+ {/* Connection Selector */}
196
+ <div className="flex items-center justify-between">
197
+ <Select
198
+ value={selectedConnection?.id || ''}
199
+ onValueChange={handleConnectionChange}
200
+ >
201
+ <SelectTrigger className="w-full sm:w-[280px] bg-zinc-900/50 border-white/10 text-zinc-300">
202
+ <SelectValue placeholder="Select connection">
203
+ {selectedConnection ? (
204
+ <div className="flex items-center gap-2">
205
+ <Database className="h-4 w-4 flex-shrink-0" />
206
+ <span className="truncate">{selectedConnection.name}</span>
207
+ <span className="text-xs text-zinc-500 hidden sm:inline">
208
+ ({selectedConnection.type})
209
+ </span>
210
+ </div>
211
+ ) : (
212
+ 'Select connection'
213
+ )}
214
+ </SelectValue>
215
+ </SelectTrigger>
216
+ <SelectContent>
217
+ {connections.map((conn) => (
218
+ <SelectItem key={conn.id} value={conn.id}>
219
+ <div className="flex items-center gap-2">
220
+ <Database className="h-4 w-4" />
221
+ <span>{conn.name}</span>
222
+ <span className="text-xs text-muted-foreground">
223
+ ({conn.type})
224
+ </span>
225
+ </div>
226
+ </SelectItem>
227
+ ))}
228
+ </SelectContent>
229
+ </Select>
230
+ <Button
231
+ variant="ghost"
232
+ size="sm"
233
+ className="h-8 text-zinc-500 hover:text-zinc-300"
234
+ onClick={refresh}
235
+ disabled={loading}
236
+ >
237
+ <RefreshCw className={`w-3.5 h-3.5 mr-1.5 ${loading ? 'animate-spin' : ''}`} />
238
+ Refresh
239
+ </Button>
240
+ </div>
241
+
242
+ {error && !data && (
243
+ <div className="rounded-xl border border-red-500/20 bg-red-500/5 p-4 text-red-400 text-sm">
244
+ {error}
245
+ </div>
246
+ )}
247
+
248
+ {/* Global Operations */}
249
+ <div>
250
+ <div className="flex items-center gap-2 mb-3">
251
+ <ShieldAlert className="h-4 w-4 text-blue-400" />
252
+ <h3 className="text-sm font-bold text-zinc-300">
253
+ Global Operations
254
+ </h3>
255
+ </div>
256
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
257
+ {/* Analyze */}
258
+ <div className="p-4 rounded-xl border border-white/5 bg-white/[0.02] hover:bg-white/[0.04] transition-colors">
259
+ <div className="flex items-start justify-between mb-3">
260
+ <div className="w-8 h-8 rounded-lg bg-yellow-500/10 border border-yellow-500/20 flex items-center justify-center">
261
+ <Zap className="w-4 h-4 text-yellow-500" />
262
+ </div>
263
+ <Button
264
+ size="sm"
265
+ variant="outline"
266
+ className="h-7 text-[10px] border-white/10 hover:bg-yellow-500/10 hover:text-yellow-500"
267
+ onClick={() => handleRunMaintenance('analyze')}
268
+ disabled={!!actionLoading || !selectedConnection}
269
+ >
270
+ {actionLoading === 'analyze-global' ? (
271
+ <RefreshCw className="w-3 h-3 animate-spin mr-1" />
272
+ ) : null}
273
+ Run Analyze
274
+ </Button>
275
+ </div>
276
+ <h4 className="text-sm font-bold text-zinc-200 mb-1">Update Statistics</h4>
277
+ <p className="text-[11px] text-zinc-500 leading-relaxed">
278
+ Updates query planner statistics for all tables.
279
+ </p>
280
+ </div>
281
+
282
+ {/* Vacuum */}
283
+ <div className="p-4 rounded-xl border border-white/5 bg-white/[0.02] hover:bg-white/[0.04] transition-colors">
284
+ <div className="flex items-start justify-between mb-3">
285
+ <div className="w-8 h-8 rounded-lg bg-blue-500/10 border border-blue-500/20 flex items-center justify-center">
286
+ <HardDrive className="w-4 h-4 text-blue-500" />
287
+ </div>
288
+ <Button
289
+ size="sm"
290
+ variant="outline"
291
+ className="h-7 text-[10px] border-white/10 hover:bg-blue-500/10 hover:text-blue-500"
292
+ onClick={() => handleRunMaintenance('vacuum')}
293
+ disabled={!!actionLoading || !selectedConnection}
294
+ >
295
+ {actionLoading === 'vacuum-global' ? (
296
+ <RefreshCw className="w-3 h-3 animate-spin mr-1" />
297
+ ) : null}
298
+ Run Vacuum
299
+ </Button>
300
+ </div>
301
+ <h4 className="text-sm font-bold text-zinc-200 mb-1">Reclaim Space</h4>
302
+ <p className="text-[11px] text-zinc-500 leading-relaxed">
303
+ Removes dead rows and returns space to the OS.
304
+ </p>
305
+ </div>
306
+
307
+ {/* Reindex */}
308
+ <div className="p-4 rounded-xl border border-white/5 bg-white/[0.02] hover:bg-white/[0.04] transition-colors">
309
+ <div className="flex items-start justify-between mb-3">
310
+ <div className="w-8 h-8 rounded-lg bg-purple-500/10 border border-purple-500/20 flex items-center justify-center">
311
+ <RefreshCw className="w-4 h-4 text-purple-500" />
312
+ </div>
313
+ <Button
314
+ size="sm"
315
+ variant="outline"
316
+ className="h-7 text-[10px] border-white/10 hover:bg-purple-500/10 hover:text-purple-500"
317
+ onClick={() => handleRunMaintenance('reindex')}
318
+ disabled={!!actionLoading || !selectedConnection}
319
+ >
320
+ {actionLoading === 'reindex-global' ? (
321
+ <RefreshCw className="w-3 h-3 animate-spin mr-1" />
322
+ ) : null}
323
+ Run Reindex
324
+ </Button>
325
+ </div>
326
+ <h4 className="text-sm font-bold text-zinc-200 mb-1">Rebuild Indexes</h4>
327
+ <p className="text-[11px] text-zinc-500 leading-relaxed">
328
+ Reconstructs all indexes in the database.
329
+ </p>
330
+ </div>
331
+
332
+ {/* Warning Card */}
333
+ <div className="p-4 rounded-xl border border-red-500/10 bg-red-500/5 flex flex-col justify-center">
334
+ <div className="flex items-center gap-2 text-red-400 mb-2">
335
+ <ShieldAlert className="w-4 h-4" />
336
+ <span className="text-[11px] font-bold uppercase tracking-wider">
337
+ Warning
338
+ </span>
339
+ </div>
340
+ <p className="text-[11px] text-red-400/70 leading-relaxed italic">
341
+ These operations can be resource-intensive. Avoid running them
342
+ during peak traffic hours.
343
+ </p>
344
+ </div>
345
+ </div>
346
+ </div>
347
+
348
+ {/* Tables + Sessions Split */}
349
+ <div className="grid gap-6 md:grid-cols-2">
350
+ {/* Table Operations */}
351
+ <div className="rounded-xl border border-white/5 bg-zinc-900/50">
352
+ <div className="p-4 border-b border-white/5 flex items-center justify-between">
353
+ <div className="flex items-center gap-2">
354
+ <Table2 className="w-4 h-4 text-blue-400" />
355
+ <span className="text-xs font-bold text-zinc-300">
356
+ Tables ({tables.length})
357
+ </span>
358
+ </div>
359
+ <Input
360
+ placeholder="Filter..."
361
+ value={tableSearch}
362
+ onChange={(e) => setTableSearch(e.target.value)}
363
+ className="w-[140px] h-7 text-xs bg-zinc-900 border-white/10"
364
+ />
365
+ </div>
366
+ <div className="max-h-[350px] overflow-y-auto">
367
+ {loading && tables.length === 0 ? (
368
+ <div className="p-4 space-y-2">
369
+ {[...Array(5)].map((_, i) => (
370
+ <Skeleton key={i} className="h-10 w-full bg-zinc-800" />
371
+ ))}
372
+ </div>
373
+ ) : filteredTables.length === 0 ? (
374
+ <div className="p-8 text-center text-zinc-600 text-sm">
375
+ No tables found.
376
+ </div>
377
+ ) : (
378
+ <div className="divide-y divide-white/5">
379
+ {filteredTables.map((table) => (
380
+ <div
381
+ key={`${table.schemaName}.${table.tableName}`}
382
+ className="group flex items-center justify-between px-4 py-2 hover:bg-white/[0.03] transition-colors"
383
+ >
384
+ <div className="min-w-0">
385
+ <div className="text-sm font-medium text-zinc-300 truncate max-w-[160px]">
386
+ {table.tableName}
387
+ </div>
388
+ <div className="flex items-center gap-2 text-[10px] text-zinc-500">
389
+ <span className="font-mono">
390
+ {table.rowCount.toLocaleString()} rows
391
+ </span>
392
+ <span>-</span>
393
+ <span className="font-mono">{table.tableSize}</span>
394
+ {(table.bloatRatio ?? 0) > 10 && (
395
+ <Badge variant="outline" className="text-[9px] text-yellow-400 border-yellow-500/20 h-4">
396
+ {(table.bloatRatio ?? 0).toFixed(0)}% bloat
397
+ </Badge>
398
+ )}
399
+ </div>
400
+ </div>
401
+ <div className="flex items-center gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
402
+ <Button
403
+ size="icon"
404
+ variant="ghost"
405
+ className="w-7 h-7 text-zinc-500 hover:text-yellow-500"
406
+ title="Analyze"
407
+ onClick={() => handleRunMaintenance('analyze', table.tableName)}
408
+ disabled={!!actionLoading}
409
+ >
410
+ {actionLoading === `analyze-${table.tableName}` ? (
411
+ <Loader2 className="w-3 h-3 animate-spin" />
412
+ ) : (
413
+ <Search className="w-3 h-3" />
414
+ )}
415
+ </Button>
416
+ <Button
417
+ size="icon"
418
+ variant="ghost"
419
+ className="w-7 h-7 text-zinc-500 hover:text-blue-500"
420
+ title="Vacuum"
421
+ onClick={() => handleRunMaintenance('vacuum', table.tableName)}
422
+ disabled={!!actionLoading}
423
+ >
424
+ {actionLoading === `vacuum-${table.tableName}` ? (
425
+ <Loader2 className="w-3 h-3 animate-spin" />
426
+ ) : (
427
+ <HardDrive className="w-3 h-3" />
428
+ )}
429
+ </Button>
430
+ </div>
431
+ </div>
432
+ ))}
433
+ </div>
434
+ )}
435
+ </div>
436
+ </div>
437
+
438
+ {/* Session Manager */}
439
+ <div className="rounded-xl border border-white/5 bg-zinc-900/50">
440
+ <div className="p-4 border-b border-white/5">
441
+ <div className="flex items-center gap-2 mb-3">
442
+ <Users className="w-4 h-4 text-green-400" />
443
+ <span className="text-xs font-bold text-zinc-300">
444
+ Sessions ({sessions.length})
445
+ </span>
446
+ </div>
447
+ <div className="grid grid-cols-4 gap-2">
448
+ <div className="rounded-lg bg-white/[0.03] p-2 text-center">
449
+ <div className="text-lg font-bold text-zinc-200 tabular-nums">{activeCount}</div>
450
+ <div className="text-[9px] text-zinc-500 uppercase font-bold">Active</div>
451
+ </div>
452
+ <div className="rounded-lg bg-white/[0.03] p-2 text-center">
453
+ <div className="text-lg font-bold text-zinc-200 tabular-nums">{idleCount}</div>
454
+ <div className="text-[9px] text-zinc-500 uppercase font-bold">Idle</div>
455
+ </div>
456
+ <div className="rounded-lg bg-white/[0.03] p-2 text-center">
457
+ <div className={`text-lg font-bold tabular-nums ${idleInTxCount > 0 ? 'text-yellow-400' : 'text-zinc-200'}`}>{idleInTxCount}</div>
458
+ <div className="text-[9px] text-zinc-500 uppercase font-bold">In TX</div>
459
+ </div>
460
+ <div className="rounded-lg bg-white/[0.03] p-2 text-center">
461
+ <div className={`text-lg font-bold tabular-nums ${waitingCount > 0 ? 'text-orange-400' : 'text-zinc-200'}`}>{waitingCount}</div>
462
+ <div className="text-[9px] text-zinc-500 uppercase font-bold">Wait</div>
463
+ </div>
464
+ </div>
465
+ </div>
466
+ <div className="max-h-[280px] overflow-y-auto">
467
+ {loading && sessions.length === 0 ? (
468
+ <div className="p-4 space-y-2">
469
+ {[...Array(4)].map((_, i) => (
470
+ <Skeleton key={i} className="h-10 w-full bg-zinc-800" />
471
+ ))}
472
+ </div>
473
+ ) : sessions.length === 0 ? (
474
+ <div className="p-8 text-center text-zinc-600 text-sm">
475
+ No active sessions found.
476
+ </div>
477
+ ) : (
478
+ <Table>
479
+ <TableHeader>
480
+ <TableRow className="border-white/5 hover:bg-transparent">
481
+ <TableHead className="text-[10px] text-zinc-500 font-bold uppercase w-[60px]">PID</TableHead>
482
+ <TableHead className="text-[10px] text-zinc-500 font-bold uppercase">User</TableHead>
483
+ <TableHead className="text-[10px] text-zinc-500 font-bold uppercase">State</TableHead>
484
+ <TableHead className="text-[10px] text-zinc-500 font-bold uppercase hidden md:table-cell">Query</TableHead>
485
+ <TableHead className="text-[10px] text-zinc-500 font-bold uppercase">Time</TableHead>
486
+ <TableHead className="text-right text-[10px] text-zinc-500 font-bold uppercase w-10">Act</TableHead>
487
+ </TableRow>
488
+ </TableHeader>
489
+ <TableBody>
490
+ {sessions.map((session) => (
491
+ <TableRow key={session.pid} className="group border-white/5 hover:bg-white/[0.03]">
492
+ <TableCell className="font-mono text-[10px] text-zinc-400 py-2">
493
+ {session.pid}
494
+ </TableCell>
495
+ <TableCell className="py-2">
496
+ <span className="text-xs text-zinc-300 truncate max-w-[80px] block">
497
+ {session.user}
498
+ </span>
499
+ </TableCell>
500
+ <TableCell className="py-2">{getStateBadge(session.state)}</TableCell>
501
+ <TableCell className="font-mono text-[10px] text-zinc-500 hidden md:table-cell py-2">
502
+ <TooltipProvider>
503
+ <Tooltip>
504
+ <TooltipTrigger asChild>
505
+ <div className="max-w-[120px] truncate cursor-help">
506
+ {session.query || '-'}
507
+ </div>
508
+ </TooltipTrigger>
509
+ <TooltipContent side="bottom" className="max-w-lg">
510
+ <pre className="text-xs whitespace-pre-wrap">
511
+ {session.query || 'No query'}
512
+ </pre>
513
+ </TooltipContent>
514
+ </Tooltip>
515
+ </TooltipProvider>
516
+ </TableCell>
517
+ <TableCell className="py-2">
518
+ <Badge
519
+ variant={
520
+ session.durationMs > 60000
521
+ ? 'destructive'
522
+ : session.durationMs > 10000
523
+ ? 'outline'
524
+ : 'secondary'
525
+ }
526
+ className="text-[9px]"
527
+ >
528
+ {session.duration}
529
+ </Badge>
530
+ </TableCell>
531
+ <TableCell className="text-right py-2">
532
+ <Button
533
+ variant="ghost"
534
+ size="icon"
535
+ className="h-6 w-6 text-zinc-600 hover:text-red-500 hover:bg-red-500/10 opacity-0 group-hover:opacity-100 transition-all"
536
+ onClick={() => handleKillClick(session)}
537
+ disabled={killingPid === session.pid}
538
+ >
539
+ {killingPid === session.pid ? (
540
+ <Loader2 className="h-3 w-3 animate-spin" />
541
+ ) : (
542
+ <Skull className="h-3 w-3" />
543
+ )}
544
+ </Button>
545
+ </TableCell>
546
+ </TableRow>
547
+ ))}
548
+ </TableBody>
549
+ </Table>
550
+ )}
551
+ </div>
552
+ </div>
553
+ </div>
554
+
555
+ {/* Operation Log */}
556
+ {operationLog.length > 0 && (
557
+ <div className="rounded-xl border border-white/5 bg-zinc-900/50">
558
+ <div className="p-4 border-b border-white/5 flex items-center gap-2">
559
+ <Clock className="w-4 h-4 text-zinc-500" />
560
+ <span className="text-xs font-bold text-zinc-300">
561
+ Operation Log (this session)
562
+ </span>
563
+ </div>
564
+ <div className="max-h-[200px] overflow-y-auto divide-y divide-white/5">
565
+ {operationLog.map((entry) => (
566
+ <div
567
+ key={entry.id}
568
+ className="flex items-center gap-3 px-4 py-2 text-xs hover:bg-white/[0.03] transition-colors"
569
+ >
570
+ <span className="text-zinc-600 font-mono text-[10px] w-[50px] shrink-0">
571
+ {entry.timestamp.toLocaleTimeString([], {
572
+ hour: '2-digit',
573
+ minute: '2-digit',
574
+ })}
575
+ </span>
576
+ <Badge
577
+ variant="outline"
578
+ className="text-[9px] font-bold w-[70px] justify-center shrink-0 border-white/10"
579
+ >
580
+ {entry.type}
581
+ </Badge>
582
+ <span className="text-zinc-400 font-mono truncate">
583
+ {entry.target}
584
+ </span>
585
+ <div className="ml-auto flex items-center gap-2 shrink-0">
586
+ {entry.result === 'success' ? (
587
+ <CheckCircle2 className="w-3 h-3 text-emerald-500" />
588
+ ) : (
589
+ <XCircle className="w-3 h-3 text-red-500" />
590
+ )}
591
+ <span className="text-zinc-600 font-mono text-[10px]">
592
+ {entry.duration}ms
593
+ </span>
594
+ </div>
595
+ </div>
596
+ ))}
597
+ </div>
598
+ </div>
599
+ )}
600
+
601
+ {/* Kill Session Confirmation Dialog */}
602
+ <AlertDialog open={!!confirmKill} onOpenChange={() => setConfirmKill(null)}>
603
+ <AlertDialogContent className="bg-zinc-950 border-white/10">
604
+ <AlertDialogHeader>
605
+ <AlertDialogTitle className="text-zinc-100">
606
+ Terminate Session?
607
+ </AlertDialogTitle>
608
+ <AlertDialogDescription className="text-zinc-400">
609
+ Are you sure you want to terminate session{' '}
610
+ <span className="font-mono font-bold text-zinc-200">
611
+ {confirmKill?.pid}
612
+ </span>
613
+ ?
614
+ <br />
615
+ <br />
616
+ User:{' '}
617
+ <span className="font-medium text-zinc-300">
618
+ {confirmKill?.user}
619
+ </span>
620
+ <br />
621
+ State:{' '}
622
+ <span className="font-medium text-zinc-300">
623
+ {confirmKill?.state}
624
+ </span>
625
+ <br />
626
+ <br />
627
+ This action will forcefully end the connection and may cause data
628
+ loss if the session has uncommitted transactions.
629
+ </AlertDialogDescription>
630
+ </AlertDialogHeader>
631
+ <AlertDialogFooter>
632
+ <AlertDialogCancel className="border-white/10 text-zinc-400">
633
+ Cancel
634
+ </AlertDialogCancel>
635
+ <AlertDialogAction
636
+ onClick={handleConfirmKill}
637
+ className="bg-red-600 text-white hover:bg-red-500"
638
+ >
639
+ Terminate
640
+ </AlertDialogAction>
641
+ </AlertDialogFooter>
642
+ </AlertDialogContent>
643
+ </AlertDialog>
644
+ </div>
645
+ );
646
+ }