@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,316 @@
1
+ 'use client';
2
+
3
+ import React, { useState } from 'react';
4
+ import { Users, Skull, Activity, Clock, Loader2 } from 'lucide-react';
5
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
6
+ import { Badge } from '@/components/ui/badge';
7
+ import { Button } from '@/components/ui/button';
8
+ import { Skeleton } from '@/components/ui/skeleton';
9
+ import {
10
+ Table,
11
+ TableBody,
12
+ TableCell,
13
+ TableHead,
14
+ TableHeader,
15
+ TableRow,
16
+ } from '@/components/ui/table';
17
+ import {
18
+ Tooltip,
19
+ TooltipContent,
20
+ TooltipProvider,
21
+ TooltipTrigger,
22
+ } from '@/components/ui/tooltip';
23
+ import {
24
+ AlertDialog,
25
+ AlertDialogAction,
26
+ AlertDialogCancel,
27
+ AlertDialogContent,
28
+ AlertDialogDescription,
29
+ AlertDialogFooter,
30
+ AlertDialogHeader,
31
+ AlertDialogTitle,
32
+ } from '@/components/ui/alert-dialog';
33
+ import type { MonitoringData, ActiveSessionDetails } from '@/lib/db/types';
34
+
35
+ interface SessionsTabProps {
36
+ data: MonitoringData | null;
37
+ loading: boolean;
38
+ onKillSession: (pid: number | string) => Promise<boolean>;
39
+ isAdmin?: boolean;
40
+ }
41
+
42
+ export function SessionsTab({ data, loading, onKillSession, isAdmin = true }: SessionsTabProps) {
43
+ const [killingPid, setKillingPid] = useState<number | string | null>(null);
44
+ const [confirmKill, setConfirmKill] = useState<ActiveSessionDetails | null>(null);
45
+
46
+ if (loading && !data) {
47
+ return <SessionsSkeleton />;
48
+ }
49
+
50
+ const sessions = data?.activeSessions ?? [];
51
+
52
+ const activeCount = sessions.filter((s) => s.state === 'active').length;
53
+ const idleCount = sessions.filter((s) => s.state === 'idle').length;
54
+ const idleInTxCount = sessions.filter((s) =>
55
+ s.state?.includes('idle in transaction')
56
+ ).length;
57
+ const waitingCount = sessions.filter((s) => s.waitEventType).length;
58
+
59
+ const handleKillClick = (session: ActiveSessionDetails) => {
60
+ setConfirmKill(session);
61
+ };
62
+
63
+ const handleConfirmKill = async () => {
64
+ if (!confirmKill) return;
65
+
66
+ setKillingPid(confirmKill.pid);
67
+ setConfirmKill(null);
68
+
69
+ await onKillSession(confirmKill.pid);
70
+
71
+ setKillingPid(null);
72
+ };
73
+
74
+ const getStateBadge = (state: string) => {
75
+ switch (state) {
76
+ case 'active':
77
+ return <Badge className="bg-green-500">Active</Badge>;
78
+ case 'idle':
79
+ return <Badge variant="secondary">Idle</Badge>;
80
+ case 'idle in transaction':
81
+ return <Badge className="bg-yellow-500">Idle in TX</Badge>;
82
+ case 'idle in transaction (aborted)':
83
+ return <Badge variant="destructive">Aborted TX</Badge>;
84
+ default:
85
+ return <Badge variant="outline">{state}</Badge>;
86
+ }
87
+ };
88
+
89
+ return (
90
+ <div className="p-3 sm:p-6 space-y-4 sm:space-y-6">
91
+ {/* Stats Cards */}
92
+ <div className="grid grid-cols-4 gap-2 sm:gap-4">
93
+ <Card className="p-0">
94
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 p-2 sm:p-4 pb-1 sm:pb-2">
95
+ <CardTitle className="text-[10px] sm:text-sm font-medium text-muted-foreground">
96
+ Active
97
+ </CardTitle>
98
+ <Activity className="h-3 w-3 sm:h-4 sm:w-4 text-green-500" />
99
+ </CardHeader>
100
+ <CardContent className="p-2 sm:p-4 pt-0">
101
+ <div className="text-lg sm:text-2xl font-bold">{activeCount}</div>
102
+ </CardContent>
103
+ </Card>
104
+
105
+ <Card className="p-0">
106
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 p-2 sm:p-4 pb-1 sm:pb-2">
107
+ <CardTitle className="text-[10px] sm:text-sm font-medium text-muted-foreground">
108
+ Idle
109
+ </CardTitle>
110
+ <Clock className="h-3 w-3 sm:h-4 sm:w-4 text-muted-foreground" />
111
+ </CardHeader>
112
+ <CardContent className="p-2 sm:p-4 pt-0">
113
+ <div className="text-lg sm:text-2xl font-bold">{idleCount}</div>
114
+ </CardContent>
115
+ </Card>
116
+
117
+ <Card className="p-0">
118
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 p-2 sm:p-4 pb-1 sm:pb-2">
119
+ <CardTitle className="text-[10px] sm:text-sm font-medium text-muted-foreground">
120
+ In TX
121
+ </CardTitle>
122
+ <Clock className={`h-3 w-3 sm:h-4 sm:w-4 ${idleInTxCount > 0 ? 'text-yellow-500' : 'text-muted-foreground'}`} />
123
+ </CardHeader>
124
+ <CardContent className="p-2 sm:p-4 pt-0">
125
+ <div className="text-lg sm:text-2xl font-bold">{idleInTxCount}</div>
126
+ </CardContent>
127
+ </Card>
128
+
129
+ <Card className="p-0">
130
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 p-2 sm:p-4 pb-1 sm:pb-2">
131
+ <CardTitle className="text-[10px] sm:text-sm font-medium text-muted-foreground">
132
+ Wait
133
+ </CardTitle>
134
+ <Users className={`h-3 w-3 sm:h-4 sm:w-4 ${waitingCount > 0 ? 'text-orange-500' : 'text-muted-foreground'}`} />
135
+ </CardHeader>
136
+ <CardContent className="p-2 sm:p-4 pt-0">
137
+ <div className="text-lg sm:text-2xl font-bold">{waitingCount}</div>
138
+ </CardContent>
139
+ </Card>
140
+ </div>
141
+
142
+ {/* Sessions Table */}
143
+ <Card className="p-0">
144
+ <CardHeader className="p-3 sm:p-4">
145
+ <CardTitle className="text-xs sm:text-sm font-medium flex items-center gap-2">
146
+ <Users className="h-3 w-3 sm:h-4 sm:w-4" />
147
+ Sessions ({sessions.length})
148
+ </CardTitle>
149
+ </CardHeader>
150
+ <CardContent className="p-0 sm:p-4 sm:pt-0">
151
+ {sessions.length === 0 ? (
152
+ <div className="text-center py-8 text-muted-foreground">
153
+ <Users className="h-8 w-8 mx-auto mb-2 opacity-50" />
154
+ <p className="text-sm">No active sessions found.</p>
155
+ </div>
156
+ ) : (
157
+ <div className="overflow-x-auto">
158
+ <Table>
159
+ <TableHeader>
160
+ <TableRow>
161
+ <TableHead className="text-xs w-[60px]">PID</TableHead>
162
+ <TableHead className="text-xs">User</TableHead>
163
+ <TableHead className="text-xs">State</TableHead>
164
+ <TableHead className="text-xs hidden md:table-cell">Query</TableHead>
165
+ <TableHead className="text-xs">Time</TableHead>
166
+ <TableHead className="text-xs hidden lg:table-cell">Wait</TableHead>
167
+ <TableHead className="text-right text-xs w-12">Act</TableHead>
168
+ </TableRow>
169
+ </TableHeader>
170
+ <TableBody>
171
+ {sessions.map((session) => (
172
+ <TableRow key={session.pid}>
173
+ <TableCell className="font-mono text-[10px] sm:text-xs py-2">
174
+ {session.pid}
175
+ </TableCell>
176
+ <TableCell className="py-2">
177
+ <div className="flex flex-col">
178
+ <span className="font-medium text-xs truncate max-w-[60px] sm:max-w-[100px]">{session.user}</span>
179
+ {session.applicationName && (
180
+ <span className="text-[10px] text-muted-foreground truncate max-w-[60px] sm:max-w-[100px] hidden sm:block">
181
+ {session.applicationName}
182
+ </span>
183
+ )}
184
+ </div>
185
+ </TableCell>
186
+ <TableCell className="py-2">{getStateBadge(session.state)}</TableCell>
187
+ <TableCell className="font-mono text-[10px] hidden md:table-cell py-2">
188
+ <TooltipProvider>
189
+ <Tooltip>
190
+ <TooltipTrigger asChild>
191
+ <div className="max-w-[150px] lg:max-w-[250px] truncate cursor-help">
192
+ {session.query || '-'}
193
+ </div>
194
+ </TooltipTrigger>
195
+ <TooltipContent
196
+ side="bottom"
197
+ className="max-w-lg"
198
+ >
199
+ <pre className="text-xs whitespace-pre-wrap">
200
+ {session.query || 'No query'}
201
+ </pre>
202
+ </TooltipContent>
203
+ </Tooltip>
204
+ </TooltipProvider>
205
+ </TableCell>
206
+ <TableCell className="py-2">
207
+ <Badge
208
+ variant={
209
+ session.durationMs > 60000
210
+ ? 'destructive'
211
+ : session.durationMs > 10000
212
+ ? 'outline'
213
+ : 'secondary'
214
+ }
215
+ className="text-[10px] sm:text-xs"
216
+ >
217
+ {session.duration}
218
+ </Badge>
219
+ </TableCell>
220
+ <TableCell className="text-[10px] text-muted-foreground hidden lg:table-cell py-2">
221
+ {session.waitEventType
222
+ ? `${session.waitEventType}`
223
+ : '-'}
224
+ </TableCell>
225
+ <TableCell className="text-right py-2">
226
+ {isAdmin ? (
227
+ <Button
228
+ variant="ghost"
229
+ size="icon"
230
+ className="h-6 w-6 sm:h-8 sm:w-8 text-destructive hover:text-destructive hover:bg-destructive/10"
231
+ onClick={() => handleKillClick(session)}
232
+ disabled={killingPid === session.pid}
233
+ >
234
+ {killingPid === session.pid ? (
235
+ <Loader2 className="h-3 w-3 sm:h-4 sm:w-4 animate-spin" />
236
+ ) : (
237
+ <Skull className="h-3 w-3 sm:h-4 sm:w-4" />
238
+ )}
239
+ </Button>
240
+ ) : (
241
+ <span className="text-[10px] text-muted-foreground">-</span>
242
+ )}
243
+ </TableCell>
244
+ </TableRow>
245
+ ))}
246
+ </TableBody>
247
+ </Table>
248
+ </div>
249
+ )}
250
+ </CardContent>
251
+ </Card>
252
+
253
+ {/* Confirm Kill Dialog */}
254
+ <AlertDialog open={!!confirmKill} onOpenChange={() => setConfirmKill(null)}>
255
+ <AlertDialogContent>
256
+ <AlertDialogHeader>
257
+ <AlertDialogTitle>Terminate Session?</AlertDialogTitle>
258
+ <AlertDialogDescription>
259
+ Are you sure you want to terminate session{' '}
260
+ <span className="font-mono font-bold">{confirmKill?.pid}</span>?
261
+ <br />
262
+ <br />
263
+ User: <span className="font-medium">{confirmKill?.user}</span>
264
+ <br />
265
+ State: <span className="font-medium">{confirmKill?.state}</span>
266
+ <br />
267
+ <br />
268
+ This action will forcefully end the connection and may cause data
269
+ loss if the session has uncommitted transactions.
270
+ </AlertDialogDescription>
271
+ </AlertDialogHeader>
272
+ <AlertDialogFooter>
273
+ <AlertDialogCancel>Cancel</AlertDialogCancel>
274
+ <AlertDialogAction
275
+ onClick={handleConfirmKill}
276
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
277
+ >
278
+ Terminate
279
+ </AlertDialogAction>
280
+ </AlertDialogFooter>
281
+ </AlertDialogContent>
282
+ </AlertDialog>
283
+ </div>
284
+ );
285
+ }
286
+
287
+ function SessionsSkeleton() {
288
+ return (
289
+ <div className="p-6 space-y-6">
290
+ <div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
291
+ {[...Array(4)].map((_, i) => (
292
+ <Card key={i}>
293
+ <CardHeader className="pb-2">
294
+ <Skeleton className="h-4 w-16" />
295
+ </CardHeader>
296
+ <CardContent>
297
+ <Skeleton className="h-8 w-12" />
298
+ </CardContent>
299
+ </Card>
300
+ ))}
301
+ </div>
302
+ <Card>
303
+ <CardHeader>
304
+ <Skeleton className="h-5 w-32" />
305
+ </CardHeader>
306
+ <CardContent>
307
+ <div className="space-y-2">
308
+ {[...Array(5)].map((_, i) => (
309
+ <Skeleton key={i} className="h-12 w-full" />
310
+ ))}
311
+ </div>
312
+ </CardContent>
313
+ </Card>
314
+ </div>
315
+ );
316
+ }
@@ -0,0 +1,335 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import { HardDrive, Database, Archive, FolderOpen } from 'lucide-react';
5
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
6
+ import { Badge } from '@/components/ui/badge';
7
+ import { Skeleton } from '@/components/ui/skeleton';
8
+ import { Progress } from '@/components/ui/progress';
9
+ import {
10
+ Table,
11
+ TableBody,
12
+ TableCell,
13
+ TableHead,
14
+ TableHeader,
15
+ TableRow,
16
+ } from '@/components/ui/table';
17
+ import type { MonitoringData } from '@/lib/db/types';
18
+
19
+ interface StorageTabProps {
20
+ data: MonitoringData | null;
21
+ loading: boolean;
22
+ }
23
+
24
+ export function StorageTab({ data, loading }: StorageTabProps) {
25
+ if (loading && !data) {
26
+ return <StorageSkeleton />;
27
+ }
28
+
29
+ const overview = data?.overview;
30
+ const storage = data?.storage ?? [];
31
+ const tables = data?.tables ?? [];
32
+ const indexes = data?.indexes ?? [];
33
+
34
+ // Calculate totals
35
+ const totalTableSize = tables.reduce((sum, t) => sum + t.tableSizeBytes, 0);
36
+ const totalIndexSize = indexes.reduce((sum, i) => sum + i.indexSizeBytes, 0);
37
+ const walStorage = storage.find((s) => s.name === 'WAL');
38
+
39
+ const formatBytes = (bytes: number) => {
40
+ if (bytes >= 1073741824) return `${(bytes / 1073741824).toFixed(2)} GB`;
41
+ if (bytes >= 1048576) return `${(bytes / 1048576).toFixed(2)} MB`;
42
+ if (bytes >= 1024) return `${(bytes / 1024).toFixed(2)} KB`;
43
+ return `${bytes} B`;
44
+ };
45
+
46
+ // Calculate storage breakdown
47
+ const totalSize = overview?.databaseSizeBytes ?? 0;
48
+ const tablePercent = totalSize > 0 ? (totalTableSize / totalSize) * 100 : 0;
49
+ const indexPercent = totalSize > 0 ? (totalIndexSize / totalSize) * 100 : 0;
50
+ const otherPercent = Math.max(0, 100 - tablePercent - indexPercent);
51
+
52
+ return (
53
+ <div className="p-3 sm:p-6 space-y-4 sm:space-y-6">
54
+ {/* Stats Cards */}
55
+ <div className="grid grid-cols-2 lg:grid-cols-4 gap-2 sm:gap-4">
56
+ <Card className="p-0">
57
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 p-2 sm:p-4 pb-1 sm:pb-2">
58
+ <CardTitle className="text-[10px] sm:text-sm font-medium text-muted-foreground">
59
+ DB Size
60
+ </CardTitle>
61
+ <Database className="h-3 w-3 sm:h-4 sm:w-4 text-blue-500" />
62
+ </CardHeader>
63
+ <CardContent className="p-2 sm:p-4 pt-0">
64
+ <div className="text-lg sm:text-2xl font-bold truncate">
65
+ {overview?.databaseSize || 'N/A'}
66
+ </div>
67
+ </CardContent>
68
+ </Card>
69
+
70
+ <Card className="p-0">
71
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 p-2 sm:p-4 pb-1 sm:pb-2">
72
+ <CardTitle className="text-[10px] sm:text-sm font-medium text-muted-foreground">
73
+ Tables
74
+ </CardTitle>
75
+ <HardDrive className="h-3 w-3 sm:h-4 sm:w-4 text-green-500" />
76
+ </CardHeader>
77
+ <CardContent className="p-2 sm:p-4 pt-0">
78
+ <div className="text-lg sm:text-2xl font-bold truncate">{formatBytes(totalTableSize)}</div>
79
+ <p className="text-[10px] sm:text-xs text-muted-foreground mt-1">
80
+ {tablePercent.toFixed(1)}%
81
+ </p>
82
+ </CardContent>
83
+ </Card>
84
+
85
+ <Card className="p-0">
86
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 p-2 sm:p-4 pb-1 sm:pb-2">
87
+ <CardTitle className="text-[10px] sm:text-sm font-medium text-muted-foreground">
88
+ Indexes
89
+ </CardTitle>
90
+ <Archive className="h-3 w-3 sm:h-4 sm:w-4 text-purple-500" />
91
+ </CardHeader>
92
+ <CardContent className="p-2 sm:p-4 pt-0">
93
+ <div className="text-lg sm:text-2xl font-bold truncate">{formatBytes(totalIndexSize)}</div>
94
+ <p className="text-[10px] sm:text-xs text-muted-foreground mt-1">
95
+ {indexPercent.toFixed(1)}%
96
+ </p>
97
+ </CardContent>
98
+ </Card>
99
+
100
+ <Card className="p-0">
101
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 p-2 sm:p-4 pb-1 sm:pb-2">
102
+ <CardTitle className="text-[10px] sm:text-sm font-medium text-muted-foreground">
103
+ WAL
104
+ </CardTitle>
105
+ <FolderOpen className="h-3 w-3 sm:h-4 sm:w-4 text-orange-500" />
106
+ </CardHeader>
107
+ <CardContent className="p-2 sm:p-4 pt-0">
108
+ <div className="text-lg sm:text-2xl font-bold truncate">
109
+ {walStorage?.walSize || walStorage?.size || 'N/A'}
110
+ </div>
111
+ </CardContent>
112
+ </Card>
113
+ </div>
114
+
115
+ {/* Storage Breakdown */}
116
+ <Card className="p-0">
117
+ <CardHeader className="p-3 sm:p-4 pb-2">
118
+ <CardTitle className="text-xs sm:text-sm font-medium flex items-center gap-2">
119
+ <HardDrive className="h-3 w-3 sm:h-4 sm:w-4" />
120
+ Storage Breakdown
121
+ </CardTitle>
122
+ </CardHeader>
123
+ <CardContent className="p-3 sm:p-4 pt-0 space-y-3 sm:space-y-4">
124
+ <div className="space-y-2 sm:space-y-3">
125
+ <div>
126
+ <div className="flex items-center justify-between text-xs sm:text-sm mb-1">
127
+ <span className="flex items-center gap-1 sm:gap-2">
128
+ <div className="w-2 h-2 sm:w-3 sm:h-3 rounded-sm bg-green-500" />
129
+ Tables
130
+ </span>
131
+ <span className="font-medium">{formatBytes(totalTableSize)}</span>
132
+ </div>
133
+ <Progress value={tablePercent} className="h-1.5 sm:h-2" />
134
+ </div>
135
+
136
+ <div>
137
+ <div className="flex items-center justify-between text-xs sm:text-sm mb-1">
138
+ <span className="flex items-center gap-1 sm:gap-2">
139
+ <div className="w-2 h-2 sm:w-3 sm:h-3 rounded-sm bg-purple-500" />
140
+ Indexes
141
+ </span>
142
+ <span className="font-medium">{formatBytes(totalIndexSize)}</span>
143
+ </div>
144
+ <Progress value={indexPercent} className="h-1.5 sm:h-2 [&>div]:bg-purple-500" />
145
+ </div>
146
+
147
+ <div>
148
+ <div className="flex items-center justify-between text-xs sm:text-sm mb-1">
149
+ <span className="flex items-center gap-1 sm:gap-2">
150
+ <div className="w-2 h-2 sm:w-3 sm:h-3 rounded-sm bg-muted-foreground" />
151
+ <span className="hidden sm:inline">Other (TOAST, FSM)</span>
152
+ <span className="sm:hidden">Other</span>
153
+ </span>
154
+ <span className="font-medium">
155
+ {formatBytes(totalSize - totalTableSize - totalIndexSize)}
156
+ </span>
157
+ </div>
158
+ <Progress value={otherPercent} className="h-1.5 sm:h-2 [&>div]:bg-muted-foreground" />
159
+ </div>
160
+ </div>
161
+ </CardContent>
162
+ </Card>
163
+
164
+ {/* Tablespaces */}
165
+ <Card className="p-0">
166
+ <CardHeader className="p-3 sm:p-4 pb-2">
167
+ <CardTitle className="text-xs sm:text-sm font-medium flex items-center gap-2">
168
+ <FolderOpen className="h-3 w-3 sm:h-4 sm:w-4" />
169
+ Tablespaces
170
+ </CardTitle>
171
+ </CardHeader>
172
+ <CardContent className="p-0 sm:p-4 sm:pt-0">
173
+ {storage.length === 0 ? (
174
+ <div className="text-center py-8 text-muted-foreground">
175
+ <FolderOpen className="h-8 w-8 mx-auto mb-2 opacity-50" />
176
+ <p className="text-sm">No tablespace information available.</p>
177
+ </div>
178
+ ) : (
179
+ <div className="overflow-x-auto">
180
+ <Table>
181
+ <TableHeader>
182
+ <TableRow>
183
+ <TableHead className="text-xs">Name</TableHead>
184
+ <TableHead className="text-xs hidden md:table-cell">Location</TableHead>
185
+ <TableHead className="text-right text-xs">Size</TableHead>
186
+ <TableHead className="text-right text-xs hidden sm:table-cell">Usage</TableHead>
187
+ </TableRow>
188
+ </TableHeader>
189
+ <TableBody>
190
+ {storage.map((ts) => (
191
+ <TableRow key={ts.name}>
192
+ <TableCell className="py-2">
193
+ <div className="flex items-center gap-1 sm:gap-2">
194
+ <FolderOpen className="h-3 w-3 sm:h-4 sm:w-4 text-muted-foreground flex-shrink-0" />
195
+ <span className="font-medium text-xs sm:text-sm truncate max-w-[80px] sm:max-w-none">{ts.name}</span>
196
+ {ts.name === 'pg_default' && (
197
+ <Badge variant="secondary" className="text-[10px] sm:text-xs hidden sm:inline-flex">Default</Badge>
198
+ )}
199
+ {ts.name === 'WAL' && (
200
+ <Badge variant="outline" className="text-[10px] sm:text-xs hidden sm:inline-flex">WAL</Badge>
201
+ )}
202
+ </div>
203
+ </TableCell>
204
+ <TableCell className="font-mono text-[10px] sm:text-xs text-muted-foreground hidden md:table-cell py-2">
205
+ {ts.location || 'default'}
206
+ </TableCell>
207
+ <TableCell className="text-right text-xs py-2">{ts.size}</TableCell>
208
+ <TableCell className="text-right hidden sm:table-cell py-2">
209
+ {ts.usagePercent !== undefined ? (
210
+ <div className="flex items-center justify-end gap-1 sm:gap-2">
211
+ <Progress
212
+ value={ts.usagePercent}
213
+ className="w-12 sm:w-16 h-1.5 sm:h-2"
214
+ />
215
+ <span className="text-xs w-10 sm:w-12">
216
+ {ts.usagePercent.toFixed(0)}%
217
+ </span>
218
+ </div>
219
+ ) : (
220
+ '-'
221
+ )}
222
+ </TableCell>
223
+ </TableRow>
224
+ ))}
225
+ </TableBody>
226
+ </Table>
227
+ </div>
228
+ )}
229
+ </CardContent>
230
+ </Card>
231
+
232
+ {/* Top Tables by Size */}
233
+ <Card className="p-0">
234
+ <CardHeader className="p-3 sm:p-4 pb-2">
235
+ <CardTitle className="text-xs sm:text-sm font-medium flex items-center gap-2">
236
+ <Database className="h-3 w-3 sm:h-4 sm:w-4" />
237
+ Largest Tables
238
+ </CardTitle>
239
+ </CardHeader>
240
+ <CardContent className="p-0 sm:p-4 sm:pt-0">
241
+ {tables.length === 0 ? (
242
+ <div className="text-center py-8 text-muted-foreground">
243
+ <Database className="h-8 w-8 mx-auto mb-2 opacity-50" />
244
+ <p className="text-sm">No table information available.</p>
245
+ </div>
246
+ ) : (
247
+ <div className="overflow-x-auto">
248
+ <Table>
249
+ <TableHeader>
250
+ <TableRow>
251
+ <TableHead className="text-xs">Table</TableHead>
252
+ <TableHead className="text-right text-xs">Size</TableHead>
253
+ <TableHead className="text-right text-xs hidden sm:table-cell">% of DB</TableHead>
254
+ </TableRow>
255
+ </TableHeader>
256
+ <TableBody>
257
+ {tables
258
+ .slice()
259
+ .sort((a, b) => b.totalSizeBytes - a.totalSizeBytes)
260
+ .slice(0, 10)
261
+ .map((table) => {
262
+ const percent =
263
+ totalSize > 0
264
+ ? (table.totalSizeBytes / totalSize) * 100
265
+ : 0;
266
+ return (
267
+ <TableRow key={`${table.schemaName}.${table.tableName}`}>
268
+ <TableCell className="py-2">
269
+ <div className="flex flex-col">
270
+ <span className="font-medium text-xs sm:text-sm truncate max-w-[100px] sm:max-w-[200px]">{table.tableName}</span>
271
+ <span className="text-[10px] sm:text-xs text-muted-foreground">
272
+ {table.schemaName}
273
+ </span>
274
+ </div>
275
+ </TableCell>
276
+ <TableCell className="text-right text-xs py-2">
277
+ {table.totalSize}
278
+ </TableCell>
279
+ <TableCell className="text-right hidden sm:table-cell py-2">
280
+ <div className="flex items-center justify-end gap-1 sm:gap-2">
281
+ <Progress
282
+ value={percent}
283
+ className="w-12 sm:w-16 h-1.5 sm:h-2"
284
+ />
285
+ <span className="text-xs w-10 sm:w-12">
286
+ {percent.toFixed(1)}%
287
+ </span>
288
+ </div>
289
+ </TableCell>
290
+ </TableRow>
291
+ );
292
+ })}
293
+ </TableBody>
294
+ </Table>
295
+ </div>
296
+ )}
297
+ </CardContent>
298
+ </Card>
299
+ </div>
300
+ );
301
+ }
302
+
303
+ function StorageSkeleton() {
304
+ return (
305
+ <div className="p-3 sm:p-6 space-y-4 sm:space-y-6">
306
+ <div className="grid grid-cols-2 lg:grid-cols-4 gap-2 sm:gap-4">
307
+ {[...Array(4)].map((_, i) => (
308
+ <Card key={i} className="p-0">
309
+ <CardHeader className="p-2 sm:p-4 pb-1 sm:pb-2">
310
+ <Skeleton className="h-3 sm:h-4 w-12 sm:w-20" />
311
+ </CardHeader>
312
+ <CardContent className="p-2 sm:p-4 pt-0">
313
+ <Skeleton className="h-5 sm:h-8 w-16 sm:w-24" />
314
+ </CardContent>
315
+ </Card>
316
+ ))}
317
+ </div>
318
+ <Card className="p-0">
319
+ <CardHeader className="p-3 sm:p-4">
320
+ <Skeleton className="h-4 sm:h-5 w-24 sm:w-32" />
321
+ </CardHeader>
322
+ <CardContent className="p-3 sm:p-4 pt-0">
323
+ <div className="space-y-2 sm:space-y-3">
324
+ {[...Array(3)].map((_, i) => (
325
+ <div key={i}>
326
+ <Skeleton className="h-3 sm:h-4 w-full mb-1 sm:mb-2" />
327
+ <Skeleton className="h-1.5 sm:h-2 w-full" />
328
+ </div>
329
+ ))}
330
+ </div>
331
+ </CardContent>
332
+ </Card>
333
+ </div>
334
+ );
335
+ }