@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.
- package/.claude/settings.local.json +127 -0
- package/.cursorrules +426 -0
- package/.devin/wiki.json +143 -0
- package/.dockerignore +80 -0
- package/.env.example +159 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +49 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +29 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +57 -0
- package/.github/workflows/ci.yml +185 -0
- package/.github/workflows/codeql.yml +57 -0
- package/.github/workflows/docker-build-push.yml +118 -0
- package/.github/workflows/helm-release.yml +113 -0
- package/CLAUDE.md +265 -0
- package/CODE_OF_CONDUCT.md +124 -0
- package/CONTRIBUTING.md +154 -0
- package/Dockerfile +73 -0
- package/LICENSE +21 -0
- package/README.md +614 -0
- package/SECURITY.md +107 -0
- package/artifacthub-repo.yml +4 -0
- package/bun.lock +1714 -0
- package/bunfig.toml +3 -0
- package/charts/libredb-studio/.helmignore +11 -0
- package/charts/libredb-studio/Chart.lock +6 -0
- package/charts/libredb-studio/Chart.yaml +50 -0
- package/charts/libredb-studio/README.md +206 -0
- package/charts/libredb-studio/templates/NOTES.txt +59 -0
- package/charts/libredb-studio/templates/_helpers.tpl +135 -0
- package/charts/libredb-studio/templates/configmap.yaml +37 -0
- package/charts/libredb-studio/templates/deployment.yaml +184 -0
- package/charts/libredb-studio/templates/hpa.yaml +32 -0
- package/charts/libredb-studio/templates/ingress.yaml +41 -0
- package/charts/libredb-studio/templates/networkpolicy.yaml +50 -0
- package/charts/libredb-studio/templates/pdb.yaml +18 -0
- package/charts/libredb-studio/templates/pvc.yaml +23 -0
- package/charts/libredb-studio/templates/secret.yaml +30 -0
- package/charts/libredb-studio/templates/seed-configmap.yaml +11 -0
- package/charts/libredb-studio/templates/service.yaml +22 -0
- package/charts/libredb-studio/templates/serviceaccount.yaml +13 -0
- package/charts/libredb-studio/values.schema.json +246 -0
- package/charts/libredb-studio/values.yaml +286 -0
- package/components.json +22 -0
- package/conductor/code_styleguides/typescript.md +43 -0
- package/conductor/product-guidelines.md +43 -0
- package/conductor/product.md +3 -0
- package/conductor/setup_state.json +1 -0
- package/conductor/tech-stack.md +39 -0
- package/conductor/tracks/enhance_postgres_monitoring_20251227/metadata.json +8 -0
- package/conductor/tracks/enhance_postgres_monitoring_20251227/plan.md +44 -0
- package/conductor/tracks/enhance_postgres_monitoring_20251227/spec.md +31 -0
- package/conductor/tracks.md +8 -0
- package/conductor/workflow.md +333 -0
- package/database-compose.yml +55 -0
- package/docker/postgres-init/01-extensions.sql +10 -0
- package/docker/postgres-init/02-sample-data.sql +585 -0
- package/docker/postgres.yml +68 -0
- package/docker-compose.yml +38 -0
- package/docs/AI_PLAN.md +74 -0
- package/docs/API_DOCS.md +875 -0
- package/docs/ARCHITECTURE.md +218 -0
- package/docs/DATABASE_PROVIDERS.md +358 -0
- package/docs/FEATURES.md +116 -0
- package/docs/HELM_CHART.md +252 -0
- package/docs/LOGIN_PAGE.md +178 -0
- package/docs/MONACO_EDITOR_PERFORMANCE.md +315 -0
- package/docs/OIDC_ARCH.md +681 -0
- package/docs/OIDC_SETUP.md +322 -0
- package/docs/POSTGRES_METRICS.md +516 -0
- package/docs/QUERY_OPTIMIZATION.md +370 -0
- package/docs/SEED_CONNECTIONS.md +468 -0
- package/docs/SQL_ALIAS_COMPLETION.md +190 -0
- package/docs/STORAGE_ARCHITECTURE.md +565 -0
- package/docs/STORAGE_QUICK_SETUP.md +419 -0
- package/docs/TECHNICAL_PLAN.md +36 -0
- package/docs/THEMING.md +345 -0
- package/docs/adding-a-new-database-provider.md +642 -0
- package/docs/backlogs/000-PLATFORM_DATA_SYNC_DATABASE.md +360 -0
- package/docs/backlogs/001-INLINE_DATA_EDITING.md +118 -0
- package/docs/backlogs/002-DATA_IMPORT.md +215 -0
- package/docs/backlogs/003-QUERY_TIME_MACHINE.md +183 -0
- package/docs/backlogs/004-AI_DATA_STORYTELLER.md +292 -0
- package/docs/backlogs/005-QUERY_PLAYGROUND.md +352 -0
- package/docs/backlogs/006-DATA_MASKING.md +418 -0
- package/docs/enterprise-features.md +718 -0
- package/docs/kubernetes-helm-chart-artifacthub-plan.md +803 -0
- package/docs/medium-koyeb-article-en.md +215 -0
- package/docs/plans/test-plans.md +445 -0
- package/docs/releases/RELEASE.V0.3.0.md +22 -0
- package/docs/releases/RELEASE.V0.4.0.md +154 -0
- package/docs/releases/RELEASE.V0.5.0.md +252 -0
- package/docs/releases/RELEASE_v0.5.6.md +145 -0
- package/docs/releases/RELEASE_v0.6.1.md +303 -0
- package/docs/releases/RELEASE_v0.6.7.md +292 -0
- package/docs/releases/RELEASE_v0.7.0.md +332 -0
- package/docs/releases/RELEASE_v0.8.0.md +521 -0
- package/docs/sampledb/titanic.sql +1379 -0
- package/docs/superpowers/plans/2026-03-25-seed-connections.md +1362 -0
- package/docs/superpowers/specs/2026-03-25-seed-connections-design.md +590 -0
- package/e2e/admin-dashboard.spec.ts +64 -0
- package/e2e/connection-management.spec.ts +58 -0
- package/e2e/export.spec.ts +34 -0
- package/e2e/login.spec.ts +85 -0
- package/e2e/query-execution.spec.ts +35 -0
- package/e2e/tab-management.spec.ts +64 -0
- package/eslint.config.mjs +28 -0
- package/fly.toml +43 -0
- package/next.config.ts +32 -0
- package/package.json +130 -0
- package/playwright.config.ts +34 -0
- package/postcss.config.mjs +7 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/logo.svg +32 -0
- package/public/next.svg +1 -0
- package/public/screenshots/code-generator.png +0 -0
- package/public/screenshots/connection-modal.png +0 -0
- package/public/screenshots/data-profiler.png +0 -0
- package/public/screenshots/erd-diagram.png +0 -0
- package/public/screenshots/hero-editor.png +0 -0
- package/public/screenshots/nl2sql.png +0 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/render.yaml +58 -0
- package/scripts/merge-lcov.mjs +239 -0
- package/sonar-project.properties +16 -0
- package/src/app/admin/error.tsx +46 -0
- package/src/app/admin/page.tsx +10 -0
- package/src/app/api/admin/audit/route.ts +52 -0
- package/src/app/api/admin/fleet-health/route.ts +81 -0
- package/src/app/api/ai/autopilot/route.ts +105 -0
- package/src/app/api/ai/chat/route.ts +132 -0
- package/src/app/api/ai/describe-schema/route.ts +52 -0
- package/src/app/api/ai/explain/route.ts +86 -0
- package/src/app/api/ai/impact/route.ts +97 -0
- package/src/app/api/ai/index-advisor/route.ts +98 -0
- package/src/app/api/ai/nl2sql/route.ts +87 -0
- package/src/app/api/ai/query-safety/route.ts +87 -0
- package/src/app/api/auth/login/route.ts +62 -0
- package/src/app/api/auth/logout/route.ts +25 -0
- package/src/app/api/auth/me/route.ts +10 -0
- package/src/app/api/auth/oidc/callback/route.ts +82 -0
- package/src/app/api/auth/oidc/login/route.ts +43 -0
- package/src/app/api/connections/managed/route.ts +35 -0
- package/src/app/api/db/cancel/route.ts +42 -0
- package/src/app/api/db/disconnect/route.ts +28 -0
- package/src/app/api/db/health/route.ts +49 -0
- package/src/app/api/db/maintenance/route.ts +72 -0
- package/src/app/api/db/monitoring/route.ts +62 -0
- package/src/app/api/db/multi-query/route.ts +116 -0
- package/src/app/api/db/pool-stats/route.ts +37 -0
- package/src/app/api/db/profile/route.ts +144 -0
- package/src/app/api/db/provider-meta/route.ts +49 -0
- package/src/app/api/db/query/route.ts +50 -0
- package/src/app/api/db/schema/route.ts +47 -0
- package/src/app/api/db/schema-snapshot/route.ts +42 -0
- package/src/app/api/db/test-connection/route.ts +55 -0
- package/src/app/api/db/transaction/route.ts +111 -0
- package/src/app/api/storage/[collection]/route.ts +67 -0
- package/src/app/api/storage/config/route.ts +17 -0
- package/src/app/api/storage/migrate/route.ts +45 -0
- package/src/app/api/storage/route.ts +32 -0
- package/src/app/error.tsx +49 -0
- package/src/app/global-error.tsx +55 -0
- package/src/app/globals.css +146 -0
- package/src/app/icon.svg +42 -0
- package/src/app/layout.tsx +34 -0
- package/src/app/login/login-form.tsx +301 -0
- package/src/app/login/page.tsx +11 -0
- package/src/app/monitoring/page.tsx +8 -0
- package/src/app/not-found.tsx +29 -0
- package/src/app/page.tsx +5 -0
- package/src/components/AIAutopilotPanel.tsx +238 -0
- package/src/components/CodeGenerator.tsx +271 -0
- package/src/components/CommandPalette.tsx +227 -0
- package/src/components/ConnectionModal.tsx +759 -0
- package/src/components/CreateTableModal.tsx +281 -0
- package/src/components/DataCharts.tsx +962 -0
- package/src/components/DataImportModal.tsx +582 -0
- package/src/components/DataProfiler.tsx +335 -0
- package/src/components/DatabaseDocs.tsx +251 -0
- package/src/components/MaskingSettings.tsx +414 -0
- package/src/components/MobileNav.tsx +50 -0
- package/src/components/NL2SQLPanel.tsx +281 -0
- package/src/components/PivotTable.tsx +257 -0
- package/src/components/QueryEditor.tsx +760 -0
- package/src/components/QueryHistory.tsx +344 -0
- package/src/components/QuerySafetyDialog.tsx +290 -0
- package/src/components/ResultsGrid.tsx +644 -0
- package/src/components/SaveQueryModal.tsx +104 -0
- package/src/components/SavedQueries.tsx +128 -0
- package/src/components/SchemaDiagram.tsx +473 -0
- package/src/components/SchemaDiff.tsx +473 -0
- package/src/components/SnapshotTimeline.tsx +116 -0
- package/src/components/Studio.tsx +639 -0
- package/src/components/TestDataGenerator.tsx +261 -0
- package/src/components/VisualExplain.tsx +820 -0
- package/src/components/admin/AdminDashboard.tsx +163 -0
- package/src/components/admin/tabs/AuditTab.tsx +531 -0
- package/src/components/admin/tabs/MonitoringEmbed.tsx +11 -0
- package/src/components/admin/tabs/OperationsTab.tsx +646 -0
- package/src/components/admin/tabs/OverviewTab.tsx +1328 -0
- package/src/components/admin/tabs/SecurityTab.tsx +284 -0
- package/src/components/community-section.tsx +92 -0
- package/src/components/icons/db-icons.tsx +84 -0
- package/src/components/libredb-logo.tsx +61 -0
- package/src/components/monitoring/MonitoringDashboard.tsx +345 -0
- package/src/components/monitoring/tabs/MetricChart.tsx +82 -0
- package/src/components/monitoring/tabs/OverviewTab.tsx +263 -0
- package/src/components/monitoring/tabs/PerformanceTab.tsx +254 -0
- package/src/components/monitoring/tabs/PoolTab.tsx +174 -0
- package/src/components/monitoring/tabs/QueriesTab.tsx +287 -0
- package/src/components/monitoring/tabs/SessionsTab.tsx +316 -0
- package/src/components/monitoring/tabs/StorageTab.tsx +335 -0
- package/src/components/monitoring/tabs/TablesTab.tsx +300 -0
- package/src/components/results-grid/ResultCard.tsx +111 -0
- package/src/components/results-grid/RowDetailSheet.tsx +178 -0
- package/src/components/results-grid/StatsBar.tsx +201 -0
- package/src/components/results-grid/index.ts +1 -0
- package/src/components/results-grid/utils.ts +23 -0
- package/src/components/schema-explorer/ColumnList.tsx +53 -0
- package/src/components/schema-explorer/SchemaExplorer.tsx +182 -0
- package/src/components/schema-explorer/TableItem.tsx +210 -0
- package/src/components/schema-explorer/index.ts +1 -0
- package/src/components/sidebar/ConnectionItem.tsx +105 -0
- package/src/components/sidebar/ConnectionsList.tsx +62 -0
- package/src/components/sidebar/Sidebar.tsx +130 -0
- package/src/components/sidebar/index.ts +2 -0
- package/src/components/studio/BottomPanel.tsx +286 -0
- package/src/components/studio/QueryToolbar.tsx +180 -0
- package/src/components/studio/StudioDesktopHeader.tsx +114 -0
- package/src/components/studio/StudioMobileHeader.tsx +340 -0
- package/src/components/studio/StudioTabBar.tsx +82 -0
- package/src/components/studio/index.ts +5 -0
- package/src/components/ui/accordion.tsx +66 -0
- package/src/components/ui/alert-dialog.tsx +157 -0
- package/src/components/ui/alert.tsx +66 -0
- package/src/components/ui/aspect-ratio.tsx +11 -0
- package/src/components/ui/avatar.tsx +53 -0
- package/src/components/ui/badge.tsx +46 -0
- package/src/components/ui/breadcrumb.tsx +109 -0
- package/src/components/ui/button-group.tsx +83 -0
- package/src/components/ui/button.tsx +60 -0
- package/src/components/ui/calendar.tsx +216 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/carousel.tsx +241 -0
- package/src/components/ui/chart.tsx +357 -0
- package/src/components/ui/checkbox.tsx +32 -0
- package/src/components/ui/collapsible.tsx +33 -0
- package/src/components/ui/command.tsx +184 -0
- package/src/components/ui/context-menu.tsx +252 -0
- package/src/components/ui/dialog.tsx +143 -0
- package/src/components/ui/drawer.tsx +135 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/empty.tsx +104 -0
- package/src/components/ui/field.tsx +248 -0
- package/src/components/ui/form.tsx +167 -0
- package/src/components/ui/hover-card.tsx +44 -0
- package/src/components/ui/input-group.tsx +170 -0
- package/src/components/ui/input-otp.tsx +77 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/item.tsx +193 -0
- package/src/components/ui/kbd.tsx +28 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/menubar.tsx +276 -0
- package/src/components/ui/navigation-menu.tsx +168 -0
- package/src/components/ui/pagination.tsx +127 -0
- package/src/components/ui/popover.tsx +48 -0
- package/src/components/ui/progress.tsx +31 -0
- package/src/components/ui/radio-group.tsx +45 -0
- package/src/components/ui/resizable.tsx +56 -0
- package/src/components/ui/scroll-area.tsx +58 -0
- package/src/components/ui/select.tsx +187 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +139 -0
- package/src/components/ui/sidebar.tsx +726 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/slider.tsx +63 -0
- package/src/components/ui/sonner.tsx +40 -0
- package/src/components/ui/spinner.tsx +16 -0
- package/src/components/ui/switch.tsx +31 -0
- package/src/components/ui/table.tsx +116 -0
- package/src/components/ui/tabs.tsx +66 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/toggle-group.tsx +83 -0
- package/src/components/ui/toggle.tsx +47 -0
- package/src/components/ui/tooltip.tsx +61 -0
- package/src/exports/components.ts +15 -0
- package/src/exports/index.ts +4 -0
- package/src/exports/providers.ts +4 -0
- package/src/exports/types.ts +26 -0
- package/src/hooks/use-ai-chat.ts +182 -0
- package/src/hooks/use-all-connections.ts +66 -0
- package/src/hooks/use-api-call.ts +71 -0
- package/src/hooks/use-auth.ts +51 -0
- package/src/hooks/use-connection-form.ts +349 -0
- package/src/hooks/use-connection-manager.ts +169 -0
- package/src/hooks/use-connection-payload.ts +15 -0
- package/src/hooks/use-inline-editing.ts +109 -0
- package/src/hooks/use-mobile.ts +20 -0
- package/src/hooks/use-monitoring-data.ts +270 -0
- package/src/hooks/use-provider-metadata.ts +62 -0
- package/src/hooks/use-query-execution.ts +478 -0
- package/src/hooks/use-storage-sync.ts +259 -0
- package/src/hooks/use-tab-manager.ts +231 -0
- package/src/hooks/use-toast.ts +20 -0
- package/src/hooks/use-transaction-control.ts +64 -0
- package/src/lib/api/error-codes.ts +30 -0
- package/src/lib/api/errors.ts +236 -0
- package/src/lib/api/with-error-handler.ts +41 -0
- package/src/lib/audit.ts +105 -0
- package/src/lib/auth.ts +87 -0
- package/src/lib/connection-string-parser.ts +172 -0
- package/src/lib/data-masking.ts +385 -0
- package/src/lib/db/base-provider.ts +325 -0
- package/src/lib/db/errors.ts +317 -0
- package/src/lib/db/factory.ts +324 -0
- package/src/lib/db/index.ts +123 -0
- package/src/lib/db/providers/document/index.ts +6 -0
- package/src/lib/db/providers/document/mongodb.ts +992 -0
- package/src/lib/db/providers/keyvalue/redis.ts +554 -0
- package/src/lib/db/providers/sql/index.ts +11 -0
- package/src/lib/db/providers/sql/mssql.ts +1065 -0
- package/src/lib/db/providers/sql/mysql.ts +978 -0
- package/src/lib/db/providers/sql/oracle.ts +1044 -0
- package/src/lib/db/providers/sql/postgres.ts +1179 -0
- package/src/lib/db/providers/sql/sql-base.ts +174 -0
- package/src/lib/db/providers/sql/sqlite.ts +721 -0
- package/src/lib/db/types.ts +437 -0
- package/src/lib/db/utils/pool-manager.ts +287 -0
- package/src/lib/db/utils/query-limiter.ts +239 -0
- package/src/lib/db-ui-config.ts +86 -0
- package/src/lib/editor/mongodb-completions.ts +172 -0
- package/src/lib/editor/sql-completions.ts +280 -0
- package/src/lib/llm/base-provider.ts +117 -0
- package/src/lib/llm/factory.ts +102 -0
- package/src/lib/llm/index.ts +90 -0
- package/src/lib/llm/providers/custom.ts +181 -0
- package/src/lib/llm/providers/gemini.ts +126 -0
- package/src/lib/llm/providers/ollama.ts +154 -0
- package/src/lib/llm/providers/openai.ts +146 -0
- package/src/lib/llm/types.ts +173 -0
- package/src/lib/llm/utils/config.ts +187 -0
- package/src/lib/llm/utils/retry.ts +119 -0
- package/src/lib/llm/utils/streaming.ts +202 -0
- package/src/lib/logger.ts +127 -0
- package/src/lib/monitoring-thresholds.ts +44 -0
- package/src/lib/oidc.ts +262 -0
- package/src/lib/query-generators.ts +61 -0
- package/src/lib/schema-diff/diff-engine.ts +273 -0
- package/src/lib/schema-diff/migration-generator.ts +208 -0
- package/src/lib/schema-diff/types.ts +55 -0
- package/src/lib/seed/config-loader.ts +79 -0
- package/src/lib/seed/connection-filter.ts +49 -0
- package/src/lib/seed/credential-resolver.ts +62 -0
- package/src/lib/seed/index.ts +40 -0
- package/src/lib/seed/resolve-connection.ts +57 -0
- package/src/lib/seed/types.ts +69 -0
- package/src/lib/sql/alias-extractor.ts +267 -0
- package/src/lib/sql/index.ts +8 -0
- package/src/lib/sql/statement-splitter.ts +167 -0
- package/src/lib/sql/types.ts +40 -0
- package/src/lib/ssh/tunnel.ts +142 -0
- package/src/lib/storage/factory.ts +84 -0
- package/src/lib/storage/index.ts +14 -0
- package/src/lib/storage/local-storage.ts +99 -0
- package/src/lib/storage/providers/postgres.ts +225 -0
- package/src/lib/storage/providers/sqlite.ts +153 -0
- package/src/lib/storage/storage-facade.ts +272 -0
- package/src/lib/storage/types.ts +75 -0
- package/src/lib/time-series-buffer.ts +58 -0
- package/src/lib/types.ts +173 -0
- package/src/lib/utils.ts +6 -0
- package/src/proxy.ts +104 -0
- package/src/types/db-drivers.d.ts +23 -0
- package/src/types/html2canvas.d.ts +9 -0
- package/tests/api/admin/audit.test.ts +178 -0
- package/tests/api/admin/fleet-health.test.ts +183 -0
- package/tests/api/ai/autopilot.test.ts +174 -0
- package/tests/api/ai/chat.test.ts +250 -0
- package/tests/api/ai/describe-schema.test.ts +266 -0
- package/tests/api/ai/explain.test.ts +199 -0
- package/tests/api/ai/impact.test.ts +168 -0
- package/tests/api/ai/index-advisor.test.ts +171 -0
- package/tests/api/ai/nl2sql.test.ts +202 -0
- package/tests/api/ai/query-safety.test.ts +196 -0
- package/tests/api/auth/login.test.ts +170 -0
- package/tests/api/auth/logout.test.ts +140 -0
- package/tests/api/auth/me.test.ts +73 -0
- package/tests/api/auth/oidc-callback.test.ts +215 -0
- package/tests/api/auth/oidc-login.test.ts +127 -0
- package/tests/api/db/cancel.test.ts +198 -0
- package/tests/api/db/disconnect.test.ts +124 -0
- package/tests/api/db/health.test.ts +222 -0
- package/tests/api/db/maintenance.test.ts +263 -0
- package/tests/api/db/monitoring.test.ts +221 -0
- package/tests/api/db/multi-query.test.ts +316 -0
- package/tests/api/db/pool-stats.test.ts +135 -0
- package/tests/api/db/profile.test.ts +330 -0
- package/tests/api/db/provider-meta.test.ts +193 -0
- package/tests/api/db/query.test.ts +314 -0
- package/tests/api/db/schema-snapshot.test.ts +170 -0
- package/tests/api/db/schema.test.ts +191 -0
- package/tests/api/db/test-connection.test.ts +185 -0
- package/tests/api/db/transaction.test.ts +314 -0
- package/tests/api/proxy.test.ts +191 -0
- package/tests/api/seed/managed-route.test.ts +113 -0
- package/tests/api/storage/config.test.ts +42 -0
- package/tests/api/storage/storage-routes.test.ts +309 -0
- package/tests/components/AIAutopilotPanel.test.tsx +756 -0
- package/tests/components/AdminPage.test.tsx +33 -0
- package/tests/components/CodeGenerator.test.tsx +182 -0
- package/tests/components/CommandPalette.test.tsx +428 -0
- package/tests/components/CommunitySection.test.tsx +91 -0
- package/tests/components/ConnectionModal.mobile.test.tsx +284 -0
- package/tests/components/ConnectionModal.test.tsx +570 -0
- package/tests/components/CreateTableModal.test.tsx +383 -0
- package/tests/components/DataCharts.test.tsx +739 -0
- package/tests/components/DataImportModal.test.tsx +751 -0
- package/tests/components/DataProfiler.test.tsx +589 -0
- package/tests/components/DatabaseDocs.test.tsx +353 -0
- package/tests/components/LoginPage.test.tsx +163 -0
- package/tests/components/LoginPageOIDC.test.tsx +92 -0
- package/tests/components/MaskingSettings.test.tsx +498 -0
- package/tests/components/MobileNav.test.tsx +30 -0
- package/tests/components/MonitoringPage.test.tsx +32 -0
- package/tests/components/NL2SQLPanel.test.tsx +621 -0
- package/tests/components/Page.test.tsx +33 -0
- package/tests/components/PivotTable.test.tsx +350 -0
- package/tests/components/QueryEditor.test.tsx +1730 -0
- package/tests/components/QueryHistory.test.tsx +572 -0
- package/tests/components/QuerySafetyDialog.test.tsx +586 -0
- package/tests/components/ResultsGrid.test.tsx +804 -0
- package/tests/components/RootLayout.test.tsx +83 -0
- package/tests/components/SaveQueryModal.test.tsx +25 -0
- package/tests/components/SavedQueries.test.tsx +43 -0
- package/tests/components/SchemaDiagram.test.tsx +1034 -0
- package/tests/components/SchemaDiff.test.tsx +906 -0
- package/tests/components/SnapshotTimeline.test.tsx +174 -0
- package/tests/components/Studio.test.tsx +1030 -0
- package/tests/components/TestDataGenerator.test.tsx +291 -0
- package/tests/components/VisualExplain.test.tsx +704 -0
- package/tests/components/admin/AdminDashboard.test.tsx +205 -0
- package/tests/components/admin/AuditTab.test.tsx +220 -0
- package/tests/components/admin/MonitoringEmbed.test.tsx +58 -0
- package/tests/components/admin/OperationsTab.test.tsx +975 -0
- package/tests/components/admin/OverviewTab.test.tsx +254 -0
- package/tests/components/admin/SecurityTab.test.tsx +467 -0
- package/tests/components/monitoring/MetricChart.test.tsx +111 -0
- package/tests/components/monitoring/MonitoringDashboard.test.tsx +259 -0
- package/tests/components/monitoring/OverviewTab.test.tsx +78 -0
- package/tests/components/monitoring/PerformanceTab.test.tsx +87 -0
- package/tests/components/monitoring/PoolTab.test.tsx +42 -0
- package/tests/components/monitoring/QueriesTab.test.tsx +80 -0
- package/tests/components/monitoring/SessionsTab.test.tsx +154 -0
- package/tests/components/monitoring/StorageTab.test.tsx +127 -0
- package/tests/components/monitoring/TablesTab.test.tsx +153 -0
- package/tests/components/results-grid/ResultCard.test.tsx +105 -0
- package/tests/components/results-grid/RowDetailSheet.test.tsx +308 -0
- package/tests/components/results-grid/StatsBar.test.tsx +162 -0
- package/tests/components/schema-explorer/ColumnList.test.tsx +151 -0
- package/tests/components/schema-explorer/SchemaExplorer.test.tsx +461 -0
- package/tests/components/schema-explorer/TableItem.test.tsx +415 -0
- package/tests/components/sidebar/ConnectionItem.test.tsx +201 -0
- package/tests/components/sidebar/ConnectionsList.test.tsx +176 -0
- package/tests/components/sidebar/Sidebar.test.tsx +187 -0
- package/tests/components/studio/BottomPanel.test.tsx +383 -0
- package/tests/components/studio/QueryToolbar.test.tsx +321 -0
- package/tests/components/studio/StudioDesktopHeader.test.tsx +377 -0
- package/tests/components/studio/StudioMobileHeader.test.tsx +198 -0
- package/tests/components/studio/StudioTabBar.test.tsx +331 -0
- package/tests/fixtures/connections.ts +96 -0
- package/tests/fixtures/masking-configs.ts +86 -0
- package/tests/fixtures/query-results.ts +71 -0
- package/tests/fixtures/schemas.ts +64 -0
- package/tests/fixtures/seed-connections/invalid-config.yaml +7 -0
- package/tests/fixtures/seed-connections/minimal-config.yaml +8 -0
- package/tests/fixtures/seed-connections/mixed-credentials.yaml +23 -0
- package/tests/fixtures/seed-connections/multi-role-config.yaml +30 -0
- package/tests/fixtures/seed-connections/valid-config.json +15 -0
- package/tests/fixtures/seed-connections/valid-config.yaml +51 -0
- package/tests/helpers/mock-fetch.ts +59 -0
- package/tests/helpers/mock-monaco.ts +112 -0
- package/tests/helpers/mock-navigation.ts +28 -0
- package/tests/helpers/mock-next.ts +80 -0
- package/tests/helpers/mock-provider.ts +133 -0
- package/tests/helpers/mock-sonner.ts +29 -0
- package/tests/helpers/render-with-providers.tsx +19 -0
- package/tests/hooks/use-ai-chat.test.ts +600 -0
- package/tests/hooks/use-auth.test.ts +371 -0
- package/tests/hooks/use-connection-form.test.ts +743 -0
- package/tests/hooks/use-connection-manager.test.ts +466 -0
- package/tests/hooks/use-inline-editing.test.ts +321 -0
- package/tests/hooks/use-mobile.test.ts +177 -0
- package/tests/hooks/use-monitoring-data.test.ts +819 -0
- package/tests/hooks/use-provider-metadata.test.ts +228 -0
- package/tests/hooks/use-query-execution.test.ts +1212 -0
- package/tests/hooks/use-tab-manager.test.ts +756 -0
- package/tests/hooks/use-toast.test.ts +74 -0
- package/tests/hooks/use-transaction-control.test.ts +211 -0
- package/tests/integration/db/mongodb-provider.test.ts +698 -0
- package/tests/integration/db/mssql-provider.test.ts +840 -0
- package/tests/integration/db/mysql-provider.test.ts +872 -0
- package/tests/integration/db/oracle-provider.test.ts +843 -0
- package/tests/integration/db/postgres-provider.test.ts +1382 -0
- package/tests/integration/db/redis-provider.test.ts +526 -0
- package/tests/integration/db/sqlite-provider.test.ts +480 -0
- package/tests/integration/seed/seed-pipeline.test.ts +102 -0
- package/tests/isolated/factory-singleton.test.ts +150 -0
- package/tests/isolated/use-storage-sync.test.ts +389 -0
- package/tests/run-components.sh +196 -0
- package/tests/setup-dom.ts +58 -0
- package/tests/setup.ts +40 -0
- package/tests/unit/api-errors.test.ts +210 -0
- package/tests/unit/code-generator-functions.test.ts +271 -0
- package/tests/unit/components/column-list.test.tsx +190 -0
- package/tests/unit/components/data-import-modal.test.tsx +441 -0
- package/tests/unit/components/studio-mobile-header.test.tsx +327 -0
- package/tests/unit/data-charts-functions.test.ts +496 -0
- package/tests/unit/data-import-functions.test.ts +320 -0
- package/tests/unit/data-import-utils.test.ts +125 -0
- package/tests/unit/db/base-provider.test.ts +517 -0
- package/tests/unit/db/errors.test.ts +403 -0
- package/tests/unit/db/factory.test.ts +436 -0
- package/tests/unit/db/pool-manager.test.ts +440 -0
- package/tests/unit/db/query-limiter.test.ts +387 -0
- package/tests/unit/db/sql-base.test.ts +438 -0
- package/tests/unit/lib/api/error-codes.test.ts +39 -0
- package/tests/unit/lib/audit.test.ts +326 -0
- package/tests/unit/lib/auth.test.ts +146 -0
- package/tests/unit/lib/connection-string-parser.test.ts +424 -0
- package/tests/unit/lib/data-masking.test.ts +583 -0
- package/tests/unit/lib/db-icons.test.tsx +41 -0
- package/tests/unit/lib/monitoring-thresholds.test.ts +133 -0
- package/tests/unit/lib/oidc.test.ts +509 -0
- package/tests/unit/lib/query-generators.test.ts +127 -0
- package/tests/unit/lib/storage/factory.test.ts +71 -0
- package/tests/unit/lib/storage/local-storage.test.ts +114 -0
- package/tests/unit/lib/storage/providers/postgres.test.ts +312 -0
- package/tests/unit/lib/storage/providers/sqlite.test.ts +232 -0
- package/tests/unit/lib/storage/storage-facade-extended.test.ts +331 -0
- package/tests/unit/lib/storage/storage-facade.test.ts +184 -0
- package/tests/unit/lib/storage.test.ts +317 -0
- package/tests/unit/lib/time-series-buffer.test.ts +212 -0
- package/tests/unit/lib/utils.test.ts +24 -0
- package/tests/unit/llm/base-provider.test.ts +238 -0
- package/tests/unit/llm/config.test.ts +262 -0
- package/tests/unit/llm/custom-provider.test.ts +281 -0
- package/tests/unit/llm/gemini-provider.test.ts +248 -0
- package/tests/unit/llm/llm-factory.test.ts +155 -0
- package/tests/unit/llm/ollama-provider.test.ts +288 -0
- package/tests/unit/llm/openai-provider.test.ts +324 -0
- package/tests/unit/llm/retry.test.ts +180 -0
- package/tests/unit/llm/streaming.test.ts +355 -0
- package/tests/unit/logger.test.ts +198 -0
- package/tests/unit/mongodb-completions.test.ts +516 -0
- package/tests/unit/pivot-table-functions.test.ts +76 -0
- package/tests/unit/query-cancelled-error.test.ts +81 -0
- package/tests/unit/schema-diff/diff-engine.test.ts +367 -0
- package/tests/unit/schema-diff/migration-generator.test.ts +513 -0
- package/tests/unit/seed/config-loader.test.ts +73 -0
- package/tests/unit/seed/connection-filter.test.ts +91 -0
- package/tests/unit/seed/credential-resolver.test.ts +85 -0
- package/tests/unit/seed/index.test.ts +72 -0
- package/tests/unit/seed/resolve-connection.test.ts +74 -0
- package/tests/unit/seed/types.test.ts +129 -0
- package/tests/unit/sql/alias-extractor.test.ts +444 -0
- package/tests/unit/sql/statement-splitter.test.ts +348 -0
- package/tests/unit/sql-completions.test.ts +463 -0
- package/tests/unit/ssh-tunnel.test.ts +465 -0
- 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
|
+
}
|