@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,590 @@
|
|
|
1
|
+
# Seed Connections — Pre-Configured Database Connections
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-03-25
|
|
4
|
+
**Status:** Approved
|
|
5
|
+
**Author:** cevheri + Claude
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
LibreDB Studio'yu Platform/SaaS olarak deploy ederken, kullanıcıların login olduktan sonra önceden tanımlı veritabanı bağlantılarını hazır olarak görmesi gerekiyor. Mevcut sistemde sadece tek bir demo connection mekanizması (`DEMO_DB_*` env vars) var ve çoklu, rol bazlı bağlantı tanımlama desteklenmiyor.
|
|
10
|
+
|
|
11
|
+
## Goals
|
|
12
|
+
|
|
13
|
+
- Container başlatıldığında YAML/JSON config dosyasından çoklu veritabanı bağlantısı yüklensin
|
|
14
|
+
- Rol bazlı erişim kontrolü: her connection'a hangi rollerin erişebileceği tanımlansın
|
|
15
|
+
- Hybrid model: `managed: true` (read-only, admin-controlled) ve `managed: false` (kullanıcıya kopyalanır, düzenlenebilir)
|
|
16
|
+
- Credential'lar `${ENV_VAR}` syntax ile inject edilsin, plaintext tutulmasın
|
|
17
|
+
- Hot-reload: config değişikliği restart gerektirmesin (TTL-based cache)
|
|
18
|
+
- Multi-tenant / role-based fleet yönetimine genişletilebilir altyapı
|
|
19
|
+
|
|
20
|
+
## Non-Goals
|
|
21
|
+
|
|
22
|
+
- Multi-tenant UI (tenant yönetim paneli) — gelecek iterasyon
|
|
23
|
+
- Vault / External Secrets Operator entegrasyonu — gelecek iterasyon
|
|
24
|
+
- Config dosyası UI'dan düzenleme
|
|
25
|
+
- SSH tunnel support for seed connections — gelecek iterasyon (requires `${ENV_VAR}` support for SSH private keys)
|
|
26
|
+
- Custom OIDC role claim expansion — gelecek iterasyon (see Role Model section)
|
|
27
|
+
|
|
28
|
+
## Decisions
|
|
29
|
+
|
|
30
|
+
| Decision | Choice | Rationale |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| Config format | YAML/JSON file (volume mount) | Okunabilir, GitOps-friendly, Helm ConfigMap ile doğal uyum |
|
|
33
|
+
| Connection model | Hybrid (managed + unmanaged) | `managed: true` = admin-controlled read-only, `managed: false` = inject & release to user |
|
|
34
|
+
| Credential handling | `${ENV_VAR}` injection from env/Secret | Separation of concerns: structure in ConfigMap, secrets in K8s Secret |
|
|
35
|
+
| Role model | Whitelist + Wildcard (`roles: ["*"]`) | OIDC role claim ile doğal uyum, secure by default (boş roles = nobody) |
|
|
36
|
+
| Config loading | Runtime provider (TTL cache, no storage write) | Hot-reload, clean separation (config != storage), zero storage side effects |
|
|
37
|
+
| Architecture | Dedicated Seed Module (`src/lib/seed/`) | Minimal coupling to existing code, independently testable, easy to evolve |
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 1. Config File Format
|
|
42
|
+
|
|
43
|
+
Path: `SEED_CONFIG_PATH` env var (default: `/app/config/seed-connections.yaml`)
|
|
44
|
+
|
|
45
|
+
```yaml
|
|
46
|
+
version: "1"
|
|
47
|
+
|
|
48
|
+
defaults:
|
|
49
|
+
managed: true
|
|
50
|
+
environment: production
|
|
51
|
+
ssl:
|
|
52
|
+
mode: require
|
|
53
|
+
rejectUnauthorized: true
|
|
54
|
+
|
|
55
|
+
connections:
|
|
56
|
+
- id: "prod-analytics"
|
|
57
|
+
name: "Production Analytics"
|
|
58
|
+
type: postgres
|
|
59
|
+
host: analytics-db.internal
|
|
60
|
+
port: 5432
|
|
61
|
+
database: analytics
|
|
62
|
+
user: "readonly_user"
|
|
63
|
+
password: "${ANALYTICS_DB_PASSWORD}"
|
|
64
|
+
environment: production
|
|
65
|
+
group: "Data Team"
|
|
66
|
+
roles: ["admin"]
|
|
67
|
+
managed: true
|
|
68
|
+
color: "#10B981"
|
|
69
|
+
|
|
70
|
+
- id: "staging-api"
|
|
71
|
+
name: "Staging API Database"
|
|
72
|
+
type: mysql
|
|
73
|
+
host: staging-mysql.internal
|
|
74
|
+
port: 3306
|
|
75
|
+
database: api_db
|
|
76
|
+
user: "dev_user"
|
|
77
|
+
password: "${STAGING_DB_PASSWORD}"
|
|
78
|
+
environment: staging
|
|
79
|
+
group: "Backend"
|
|
80
|
+
roles: ["*"]
|
|
81
|
+
managed: false
|
|
82
|
+
|
|
83
|
+
- id: "shared-mongo"
|
|
84
|
+
name: "Shared MongoDB"
|
|
85
|
+
type: mongodb
|
|
86
|
+
connectionString: "${MONGO_CONNECTION_STRING}"
|
|
87
|
+
group: "Platform"
|
|
88
|
+
roles: ["admin"]
|
|
89
|
+
managed: true
|
|
90
|
+
|
|
91
|
+
- id: "dev-redis"
|
|
92
|
+
name: "Dev Redis Cache"
|
|
93
|
+
type: redis
|
|
94
|
+
host: redis.internal
|
|
95
|
+
port: 6379
|
|
96
|
+
database: "0"
|
|
97
|
+
password: "${REDIS_PASSWORD}"
|
|
98
|
+
roles: ["*"]
|
|
99
|
+
managed: true
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Key rules:**
|
|
103
|
+
|
|
104
|
+
- `version: "1"` — required, enables future format migration. Unrecognized versions are rejected with error log (fail-fast). Migration tooling will be provided when v2 is introduced.
|
|
105
|
+
- `defaults` — optional, merged into each connection (connection-level overrides win)
|
|
106
|
+
- `id` — required, unique, slug format (`[a-z0-9-]+`), max 64 chars
|
|
107
|
+
- `roles` — required, min 1 entry. `["*"]` = all authenticated users. `["admin"]` = admin only. `["user"]` = user only. Empty array = nobody (validation rejects this).
|
|
108
|
+
- `managed` — inherits from `defaults.managed` if not set
|
|
109
|
+
- `${ENV_VAR}` — resolved at runtime from `process.env`. Unresolvable = connection skipped with error log
|
|
110
|
+
- `ssl` — uses the existing `SSLConfig` shape (`mode: SSLMode`, `rejectUnauthorized`, `caCert`, etc.)
|
|
111
|
+
|
|
112
|
+
### Role Model — Current Scope & Future Expansion
|
|
113
|
+
|
|
114
|
+
**Current JWT:** The system stores `role: 'admin' | 'user'` in the JWT payload. OIDC claims are collapsed to these two values via `mapOIDCRole()`.
|
|
115
|
+
|
|
116
|
+
**This iteration:** Config `roles` field only supports `["*"]`, `["admin"]`, `["user"]`, and `["admin", "user"]`. The Zod schema validates against these known values. This matches the existing auth system without modification.
|
|
117
|
+
|
|
118
|
+
**Future iteration (multi-tenant):** When custom OIDC roles are needed:
|
|
119
|
+
1. Expand JWT payload to carry `roles: string[]` (original OIDC claims) alongside binary `role`
|
|
120
|
+
2. Update `mapOIDCRole()` to preserve claim array
|
|
121
|
+
3. Seed connection filter will already accept `roles: string[]` — only the JWT extraction changes
|
|
122
|
+
4. Config can then use `roles: ["data-team", "backend"]` etc.
|
|
123
|
+
|
|
124
|
+
The data model (`roles: string[]`) is future-proof. Only the runtime filter validation is restricted for now.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 2. Module Architecture
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
src/lib/seed/
|
|
132
|
+
index.ts # Public API: getManagedConnections(roles)
|
|
133
|
+
types.ts # Zod schemas + TypeScript types
|
|
134
|
+
config-loader.ts # YAML/JSON parse + Zod validation + TTL cache
|
|
135
|
+
credential-resolver.ts # ${ENV_VAR} -> process.env resolution
|
|
136
|
+
connection-filter.ts # Role filter + defaults merge + DatabaseConnection mapping
|
|
137
|
+
resolve-connection.ts # Shared utility: resolve seed connection by ID for all API routes
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Data Flow
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
seed-connections.yaml
|
|
144
|
+
|
|
|
145
|
+
ConfigLoader (parse + validate + cache)
|
|
146
|
+
| SeedConfig (raw)
|
|
147
|
+
CredentialResolver (${VAR} -> value)
|
|
148
|
+
| SeedConfig (resolved)
|
|
149
|
+
ConnectionFilter (role + defaults merge)
|
|
150
|
+
| ManagedConnection[]
|
|
151
|
+
GET /api/connections/managed
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### types.ts
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
interface SeedDefaults {
|
|
158
|
+
managed?: boolean;
|
|
159
|
+
environment?: ConnectionEnvironment;
|
|
160
|
+
ssl?: SSLConfig;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
interface SeedConnection {
|
|
164
|
+
id: string; // slug format, unique
|
|
165
|
+
name: string;
|
|
166
|
+
type: DatabaseType; // excludes 'demo'
|
|
167
|
+
host?: string;
|
|
168
|
+
port?: number;
|
|
169
|
+
database?: string;
|
|
170
|
+
user?: string;
|
|
171
|
+
password?: string;
|
|
172
|
+
connectionString?: string;
|
|
173
|
+
environment?: ConnectionEnvironment;
|
|
174
|
+
group?: string;
|
|
175
|
+
color?: string;
|
|
176
|
+
roles: string[]; // ["*"] = all, ["admin"] = admin only
|
|
177
|
+
managed?: boolean; // inherits from defaults
|
|
178
|
+
ssl?: SSLConfig;
|
|
179
|
+
serviceName?: string; // Oracle
|
|
180
|
+
instanceName?: string; // MSSQL
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
interface SeedConfig {
|
|
184
|
+
version: "1";
|
|
185
|
+
defaults?: SeedDefaults;
|
|
186
|
+
connections: SeedConnection[];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
interface ManagedConnection extends DatabaseConnection {
|
|
190
|
+
managed: boolean;
|
|
191
|
+
roles: string[];
|
|
192
|
+
seedId: string; // original id from config (stable reference)
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Zod schemas validate all above at parse time. Duplicate `id` values rejected via `.refine()`.
|
|
197
|
+
|
|
198
|
+
### config-loader.ts
|
|
199
|
+
|
|
200
|
+
- Reads file from `SEED_CONFIG_PATH` (default `/app/config/seed-connections.yaml`)
|
|
201
|
+
- Auto-detects format: `.yaml`/`.yml` = YAML, `.json` = JSON
|
|
202
|
+
- Validates with Zod schema
|
|
203
|
+
- TTL cache: `SEED_CACHE_TTL_MS` (default 60000ms). Stale reads re-read from disk.
|
|
204
|
+
- File not found = graceful empty config (warn log), app continues
|
|
205
|
+
- Parse/validation error = error log, endpoint returns 500
|
|
206
|
+
- Unrecognized `version` value = error log, endpoint returns 500 (fail-fast)
|
|
207
|
+
|
|
208
|
+
### credential-resolver.ts
|
|
209
|
+
|
|
210
|
+
- Pattern: `${VARIABLE_NAME}` — resolved from `process.env`
|
|
211
|
+
- Applies to: `password`, `connectionString`, `user`, `host`, `database` fields
|
|
212
|
+
- Unresolvable env var = that connection skipped (error log), others continue
|
|
213
|
+
- Plaintext password detection: warns **once per connection ID** (not on every cache refresh) if password doesn't use `${...}` syntax. Uses a `Set<string>` to track warned IDs.
|
|
214
|
+
- Pure function (except `process.env` read)
|
|
215
|
+
|
|
216
|
+
### connection-filter.ts
|
|
217
|
+
|
|
218
|
+
- Merges `defaults` into each connection (connection values override defaults)
|
|
219
|
+
- Filters by role: connection included if `roles` contains `"*"` or any intersection with the user's roles array
|
|
220
|
+
- Maps `SeedConnection` to `ManagedConnection` (adds `createdAt`, `seedId`, strips `roles` metadata for non-admin)
|
|
221
|
+
- Sets `managed` flag from resolved config
|
|
222
|
+
|
|
223
|
+
### resolve-connection.ts — Shared Seed Connection Resolver
|
|
224
|
+
|
|
225
|
+
All API routes that accept connection objects need to handle managed connections. Instead of modifying each route individually, a shared utility resolves seed connections:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
/**
|
|
229
|
+
* Resolves a connection from the request body.
|
|
230
|
+
* If connectionId starts with "seed:", loads from config with full credentials.
|
|
231
|
+
* Otherwise, returns the connection object from the body as-is.
|
|
232
|
+
*
|
|
233
|
+
* @param body - Request body (may contain `connection` or `connectionId`)
|
|
234
|
+
* @param session - Verified JWT session (for role checking)
|
|
235
|
+
* @returns Resolved DatabaseConnection with full credentials
|
|
236
|
+
* @throws 403 if user role doesn't have access to seed connection
|
|
237
|
+
* @throws 404 if seed connection ID not found in config
|
|
238
|
+
*/
|
|
239
|
+
export async function resolveConnection(
|
|
240
|
+
body: { connection?: DatabaseConnection; connectionId?: string },
|
|
241
|
+
session: { role: string; username: string }
|
|
242
|
+
): Promise<DatabaseConnection>
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
This utility is called at the top of every route handler that currently extracts `connection` from the request body, before passing to `getOrCreateProvider()`.
|
|
246
|
+
|
|
247
|
+
### index.ts
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
export async function getManagedConnections(roles: string[]): Promise<ManagedConnection[]>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Single public function. Orchestrates: loadConfig -> resolveCredentials -> filterByRoles. Signature accepts `roles: string[]` (array) for future multi-role OIDC support. Current callers pass `[session.role]`.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## 3. API Endpoint
|
|
258
|
+
|
|
259
|
+
### GET /api/connections/managed
|
|
260
|
+
|
|
261
|
+
**Auth:** Required (JWT session)
|
|
262
|
+
|
|
263
|
+
**Response:**
|
|
264
|
+
```json
|
|
265
|
+
{
|
|
266
|
+
"connections": ["ManagedConnection, ..."],
|
|
267
|
+
"cacheHint": 60000
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**Behavior:**
|
|
272
|
+
1. Extract `role` from JWT session (server-side truth, never client-supplied)
|
|
273
|
+
2. Call `getManagedConnections([role])`
|
|
274
|
+
3. For `managed: true` connections: strip `password` and `connectionString` from response
|
|
275
|
+
4. For `managed: false` connections: include credentials (for initial inject into user storage)
|
|
276
|
+
5. Return with `cacheHint` for client-side cache duration
|
|
277
|
+
|
|
278
|
+
### All DB API Routes — Shared Connection Resolution
|
|
279
|
+
|
|
280
|
+
Managed connections require server-side credential resolution. This affects **all** routes that accept a `connection` object from the client, not just the query route:
|
|
281
|
+
|
|
282
|
+
**Affected routes (12+):**
|
|
283
|
+
- `POST /api/db/query` — single query execution
|
|
284
|
+
- `POST /api/db/multi-query` — multi-statement execution
|
|
285
|
+
- `POST /api/db/schema` — schema fetch
|
|
286
|
+
- `POST /api/db/health` — connection health check (Note: `GET /api/db/health` is the app-level health check and is unaffected)
|
|
287
|
+
- `POST /api/db/cancel` — query cancellation
|
|
288
|
+
- `POST /api/db/transaction` — BEGIN/COMMIT/ROLLBACK
|
|
289
|
+
- `POST /api/db/maintenance` — VACUUM, ANALYZE, etc.
|
|
290
|
+
- `POST /api/db/monitoring` — live metrics
|
|
291
|
+
- `GET /api/db/pool-stats` — connection pool stats
|
|
292
|
+
- `POST /api/db/profile` — data profiling
|
|
293
|
+
- `GET /api/db/provider-meta` — capabilities/labels
|
|
294
|
+
- `POST /api/db/test-connection` — connection testing
|
|
295
|
+
- `POST /api/db/schema-snapshot` — schema snapshots
|
|
296
|
+
- `POST /api/db/disconnect` — explicit disconnection
|
|
297
|
+
|
|
298
|
+
**Pattern for each route:**
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
// Before (current):
|
|
302
|
+
const { connection, sql } = await request.json();
|
|
303
|
+
const provider = await getOrCreateProvider(connection);
|
|
304
|
+
|
|
305
|
+
// After (with seed support):
|
|
306
|
+
import { resolveConnection } from '@/lib/seed/resolve-connection';
|
|
307
|
+
|
|
308
|
+
const body = await request.json();
|
|
309
|
+
const session = await verifySession(request);
|
|
310
|
+
const connection = await resolveConnection(body, session);
|
|
311
|
+
const provider = await getOrCreateProvider(connection);
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Provider Cache Key Namespacing
|
|
315
|
+
|
|
316
|
+
Seed connections use a namespaced cache key to prevent collision with user-created connections:
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
// In resolveConnection(), seed connections get prefixed ID:
|
|
320
|
+
if (isSeedConnection) {
|
|
321
|
+
resolvedConnection.id = `seed:${seedId}`; // e.g., "seed:prod-analytics"
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
This ensures the provider cache in `factory.ts` never conflates a seed connection with a user connection that might have the same slug ID.
|
|
326
|
+
|
|
327
|
+
### Request Format
|
|
328
|
+
|
|
329
|
+
```
|
|
330
|
+
// Existing: client sends full connection object (user connections)
|
|
331
|
+
{ connection: { host, port, user, password, ... }, sql: "..." }
|
|
332
|
+
|
|
333
|
+
// New: managed connections identified by connectionId
|
|
334
|
+
{ connectionId: "seed:prod-analytics", sql: "..." }
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Both formats are supported. `resolveConnection()` detects which format is used and handles accordingly.
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## 4. Client Integration
|
|
342
|
+
|
|
343
|
+
### useConnectionManager Hook Changes
|
|
344
|
+
|
|
345
|
+
```
|
|
346
|
+
Current:
|
|
347
|
+
storage.getConnections() -> connections[]
|
|
348
|
+
|
|
349
|
+
New:
|
|
350
|
+
storage.getConnections() -> userConnections[]
|
|
351
|
+
GET /api/connections/managed -> managedConnections[]
|
|
352
|
+
merge(managed, user) -> allConnections[]
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**Client-side managed connection handling:**
|
|
356
|
+
|
|
357
|
+
When a managed connection (`managed: true`) is active, all API calls from hooks (`useQueryExecution`, `useTransactionControl`, etc.) must send `{ connectionId: "seed:<seedId>" }` instead of the full connection object. This is achieved by checking the `managed` flag on the active connection:
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
// In useQueryExecution and other hooks:
|
|
361
|
+
const requestBody = activeConnection.managed
|
|
362
|
+
? { connectionId: `seed:${activeConnection.seedId}`, sql }
|
|
363
|
+
: { connection: activeConnection, sql };
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**cacheHint behavior:** Client caches managed connections for `cacheHint` milliseconds after mount. No polling. Re-fetch occurs on connection list focus or manual refresh.
|
|
367
|
+
|
|
368
|
+
**Merge rules:**
|
|
369
|
+
|
|
370
|
+
| Scenario | Behavior |
|
|
371
|
+
|---|---|
|
|
372
|
+
| Managed connection, first seen | Added to list |
|
|
373
|
+
| `managed: false`, first seen | Copied to user storage with credentials, marked with `seedId` for tracking |
|
|
374
|
+
| `managed: false`, already copied (matching `seedId` in storage) | User copy wins, no overwrite (user owns it) |
|
|
375
|
+
| Managed connection removed from config | Disappears from list (`managed: true`) or user copy remains (`managed: false`) |
|
|
376
|
+
| Same `id` as user connection | `managed: true` wins; `managed: false` user copy wins |
|
|
377
|
+
|
|
378
|
+
**`managed: false` idempotency:** User connections that were copied from seeds carry a `seedId` field. On merge, `useConnectionManager` checks if a user connection with matching `seedId` already exists. If it does, the copy is skipped. This prevents duplicate entries from concurrent tabs and ensures credential rotation for `managed: false` connections requires the user to delete and re-import (admin should use `managed: true` for connections requiring automated credential rotation).
|
|
379
|
+
|
|
380
|
+
### UI Changes
|
|
381
|
+
|
|
382
|
+
- `managed: true`: lock icon, edit/delete buttons hidden, tooltip: "Managed by administrator"
|
|
383
|
+
- `managed: false` + not yet copied: "Join Connection" button
|
|
384
|
+
- Connection color and group from config
|
|
385
|
+
- `data-testid="managed-lock-{id}"` for E2E testing
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
## 5. Deployment Integration
|
|
390
|
+
|
|
391
|
+
### Docker
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
docker run -v ./seed-connections.yaml:/app/config/seed-connections.yaml:ro \
|
|
395
|
+
-e ANALYTICS_DB_PASSWORD=secret \
|
|
396
|
+
ghcr.io/libredb/libredb-studio:latest
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### docker-compose
|
|
400
|
+
|
|
401
|
+
```yaml
|
|
402
|
+
services:
|
|
403
|
+
app:
|
|
404
|
+
volumes:
|
|
405
|
+
- ./seed-connections.yaml:/app/config/seed-connections.yaml:ro
|
|
406
|
+
environment:
|
|
407
|
+
SEED_CONFIG_PATH: /app/config/seed-connections.yaml
|
|
408
|
+
ANALYTICS_DB_PASSWORD: ${ANALYTICS_DB_PASSWORD}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Helm
|
|
412
|
+
|
|
413
|
+
**values.yaml additions:**
|
|
414
|
+
|
|
415
|
+
```yaml
|
|
416
|
+
seedConnections:
|
|
417
|
+
enabled: false
|
|
418
|
+
config: {} # inline YAML config
|
|
419
|
+
existingConfigMap: "" # or reference external ConfigMap
|
|
420
|
+
configMapKey: "seed-connections.yaml"
|
|
421
|
+
cacheTTL: 60000
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
**New template:** `seed-configmap.yaml` — creates ConfigMap from `seedConnections.config`
|
|
425
|
+
|
|
426
|
+
**deployment.yaml changes:**
|
|
427
|
+
- Volume mount: seed-config ConfigMap at `/app/config` (readOnly)
|
|
428
|
+
- Env vars: `SEED_CONFIG_PATH`, `SEED_CACHE_TTL_MS`
|
|
429
|
+
- Credentials via `extraEnv` / `extraEnvFrom` referencing K8s Secrets
|
|
430
|
+
|
|
431
|
+
**values.schema.json:** Updated with `seedConnections` object schema.
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## 6. Security Model
|
|
436
|
+
|
|
437
|
+
### Credential Protection
|
|
438
|
+
|
|
439
|
+
- `managed: true` passwords never reach client — stripped in API response
|
|
440
|
+
- Server resolves credentials at query execution time from config via `resolveConnection()`
|
|
441
|
+
- `${ENV_VAR}` pattern enforced; plaintext passwords trigger warn log (once per connection ID)
|
|
442
|
+
- `credential-resolver.ts` is server-only code (not bundled for client)
|
|
443
|
+
|
|
444
|
+
### Role Escalation Prevention
|
|
445
|
+
|
|
446
|
+
- Role extracted from JWT session server-side (never from client request)
|
|
447
|
+
- `resolveConnection()` verifies role access before returning credentials
|
|
448
|
+
- Every DB API route uses `resolveConnection()` which enforces role check
|
|
449
|
+
|
|
450
|
+
### Pre-existing Note: Client-Supplied Credentials
|
|
451
|
+
|
|
452
|
+
The existing architecture allows any authenticated user to submit arbitrary connection credentials via the `{ connection: {...} }` request body pattern. This is by design — users manage their own connections. A user with network access to a database host could connect directly regardless of LibreDB's seed connection role restrictions. Seed connection role filtering protects credential distribution (who sees what in the UI and whose credentials are resolved server-side), not network-level access. Network-level isolation should be handled via Kubernetes NetworkPolicy, VPC rules, or database-level access control.
|
|
453
|
+
|
|
454
|
+
### Managed Connection Query Flow (all routes)
|
|
455
|
+
|
|
456
|
+
```
|
|
457
|
+
Client: POST /api/db/* { connectionId: "seed:prod-analytics", ... }
|
|
458
|
+
Server: 1. Verify JWT session (proxy middleware)
|
|
459
|
+
2. resolveConnection(body, session):
|
|
460
|
+
a. Detect "seed:" prefix in connectionId
|
|
461
|
+
b. Extract role from session
|
|
462
|
+
c. Load seed connection from config
|
|
463
|
+
d. Verify role in connection's roles list (403 if denied)
|
|
464
|
+
e. Resolve credentials from env vars
|
|
465
|
+
f. Return full DatabaseConnection with namespaced ID
|
|
466
|
+
3. getOrCreateProvider(resolvedConnection)
|
|
467
|
+
4. Execute operation
|
|
468
|
+
5. Audit log event (for managed connections)
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Audit Trail
|
|
472
|
+
|
|
473
|
+
Every managed connection operation logged:
|
|
474
|
+
```typescript
|
|
475
|
+
{ event: 'managed_connection_query', connectionId, user, role, route, timestamp }
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
## 7. Error Handling
|
|
481
|
+
|
|
482
|
+
| Error Level | Example | Behavior |
|
|
483
|
+
|---|---|---|
|
|
484
|
+
| Config file missing | File not found at path | Graceful: empty array, warn log, app runs |
|
|
485
|
+
| Parse error | Invalid YAML/JSON | Fail-fast: Zod error, error log, endpoint returns 500 |
|
|
486
|
+
| Version mismatch | `version: "2"` on v1-only code | Fail-fast: error log, endpoint returns 500 |
|
|
487
|
+
| Credential resolve error | `${VAR}` undefined | Per-connection skip: that connection omitted, others work |
|
|
488
|
+
| Role filter empty result | User role matches nothing | Normal: empty array returned |
|
|
489
|
+
| Seed connection not found | `connectionId: "seed:nonexistent"` | 404 response |
|
|
490
|
+
| Role access denied | User requests seed connection they can't access | 403 response |
|
|
491
|
+
|
|
492
|
+
**Principle:** One broken connection definition must not break all others. Pipeline processes each connection independently.
|
|
493
|
+
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
## 8. Testing Strategy
|
|
497
|
+
|
|
498
|
+
### Test Pyramid
|
|
499
|
+
|
|
500
|
+
| Layer | Count | Scope |
|
|
501
|
+
|---|---|---|
|
|
502
|
+
| Unit | ~30 | config-loader, credential-resolver, connection-filter, resolve-connection, Zod schemas |
|
|
503
|
+
| API | 10 | endpoint auth, role filter, cache, password stripping, errors, seed query resolution |
|
|
504
|
+
| Integration | 8 | full pipeline, hot-reload, partial failure, query with managed conn, audit, multi-route seed resolution |
|
|
505
|
+
| E2E | 1 | managed connection visible in sidebar after login |
|
|
506
|
+
|
|
507
|
+
### Test Fixtures
|
|
508
|
+
|
|
509
|
+
```
|
|
510
|
+
tests/fixtures/seed-connections/
|
|
511
|
+
valid-config.yaml
|
|
512
|
+
minimal-config.yaml
|
|
513
|
+
invalid-config.yaml
|
|
514
|
+
mixed-credentials.yaml
|
|
515
|
+
multi-role-config.yaml
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
---
|
|
519
|
+
|
|
520
|
+
## 9. New Files
|
|
521
|
+
|
|
522
|
+
```
|
|
523
|
+
src/lib/seed/
|
|
524
|
+
index.ts
|
|
525
|
+
types.ts
|
|
526
|
+
config-loader.ts
|
|
527
|
+
credential-resolver.ts
|
|
528
|
+
connection-filter.ts
|
|
529
|
+
resolve-connection.ts
|
|
530
|
+
|
|
531
|
+
src/app/api/connections/managed/route.ts
|
|
532
|
+
|
|
533
|
+
charts/libredb-studio/templates/seed-configmap.yaml
|
|
534
|
+
|
|
535
|
+
tests/unit/seed/config-loader.test.ts
|
|
536
|
+
tests/unit/seed/credential-resolver.test.ts
|
|
537
|
+
tests/unit/seed/connection-filter.test.ts
|
|
538
|
+
tests/unit/seed/resolve-connection.test.ts
|
|
539
|
+
tests/unit/seed/types.test.ts
|
|
540
|
+
tests/api/seed/managed-route.test.ts
|
|
541
|
+
tests/integration/seed/seed-pipeline.test.ts
|
|
542
|
+
tests/fixtures/seed-connections/*.yaml
|
|
543
|
+
e2e/seed-connections.spec.ts
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
## 10. Modified Files
|
|
547
|
+
|
|
548
|
+
| File | Change |
|
|
549
|
+
|---|---|
|
|
550
|
+
| `src/hooks/use-connection-manager.ts` | Managed connection fetch + merge logic + seedId tracking |
|
|
551
|
+
| `src/hooks/use-query-execution.ts` | Send `connectionId` for managed connections instead of full connection |
|
|
552
|
+
| `src/hooks/use-transaction-control.ts` | Send `connectionId` for managed connections |
|
|
553
|
+
| `src/app/api/db/query/route.ts` | Use `resolveConnection()` before `getOrCreateProvider()` |
|
|
554
|
+
| `src/app/api/db/multi-query/route.ts` | Use `resolveConnection()` |
|
|
555
|
+
| `src/app/api/db/schema/route.ts` | Use `resolveConnection()` |
|
|
556
|
+
| `src/app/api/db/transaction/route.ts` | Use `resolveConnection()` |
|
|
557
|
+
| `src/app/api/db/cancel/route.ts` | Use `resolveConnection()` |
|
|
558
|
+
| `src/app/api/db/maintenance/route.ts` | Use `resolveConnection()` |
|
|
559
|
+
| `src/app/api/db/monitoring/route.ts` | Use `resolveConnection()` |
|
|
560
|
+
| `src/app/api/db/pool-stats/route.ts` | Use `resolveConnection()` |
|
|
561
|
+
| `src/app/api/db/profile/route.ts` | Use `resolveConnection()` |
|
|
562
|
+
| `src/app/api/db/provider-meta/route.ts` | Use `resolveConnection()` |
|
|
563
|
+
| `src/app/api/db/test-connection/route.ts` | Use `resolveConnection()` |
|
|
564
|
+
| `src/app/api/db/schema-snapshot/route.ts` | Use `resolveConnection()` |
|
|
565
|
+
| `src/app/api/db/disconnect/route.ts` | Use `resolveConnection()` |
|
|
566
|
+
| `src/lib/db/factory.ts` | Accept namespaced `seed:` IDs in provider cache key |
|
|
567
|
+
| `src/components/sidebar/ConnectionItem.tsx` | Lock icon + hide edit/delete for managed |
|
|
568
|
+
| `src/lib/types.ts` | `ManagedConnection` type, `managed` + `seedId` fields on `DatabaseConnection` |
|
|
569
|
+
| `charts/libredb-studio/values.yaml` | `seedConnections` section |
|
|
570
|
+
| `charts/libredb-studio/values.schema.json` | Schema update |
|
|
571
|
+
| `charts/libredb-studio/templates/deployment.yaml` | Volume mount + env vars |
|
|
572
|
+
| `docker-compose.yml` | Config volume mount example |
|
|
573
|
+
| `.env.example` | New env var documentation |
|
|
574
|
+
|
|
575
|
+
## 11. New Environment Variables
|
|
576
|
+
|
|
577
|
+
| Variable | Required | Default | Description |
|
|
578
|
+
|---|---|---|---|
|
|
579
|
+
| `SEED_CONFIG_PATH` | No | `/app/config/seed-connections.yaml` | Config file path |
|
|
580
|
+
| `SEED_CACHE_TTL_MS` | No | `60000` | Cache TTL in milliseconds |
|
|
581
|
+
|
|
582
|
+
## 12. Future Extensions
|
|
583
|
+
|
|
584
|
+
- **Multi-Tenant:** Add `tenant` field to config connections, filter by tenant ID from JWT claims
|
|
585
|
+
- **Custom OIDC Roles:** Expand JWT payload to `roles: string[]`, update `mapOIDCRole()` to preserve claim array, enable `roles: ["data-team", "backend"]` in config
|
|
586
|
+
- **RBAC UI:** Admin panel tab for viewing/managing seed connections
|
|
587
|
+
- **Vault Integration:** New credential resolver backend for HashiCorp Vault / AWS SSM
|
|
588
|
+
- **SSH Tunnel Support:** Add `sshTunnel` field to `SeedConnection` with `${ENV_VAR}` support for SSH keys
|
|
589
|
+
- **Connection Source Abstraction:** Evolve to Approach 3 (ConnectionRegistry) when 3+ sources needed
|
|
590
|
+
- **Demo Connection Migration:** Existing `GET /api/demo-connection` with `DEMO_DB_*` env vars can be migrated to a seed connection entry. Both mechanisms coexist for backward compatibility; demo deprecation planned for a future major version.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
test.describe('Admin Dashboard', () => {
|
|
4
|
+
test.beforeEach(async ({ page }) => {
|
|
5
|
+
// Login as admin
|
|
6
|
+
await page.goto('/login');
|
|
7
|
+
await page.locator('input[type="email"]').fill('admin@libredb.org');
|
|
8
|
+
await page.locator('input[type="password"]').fill('test-admin');
|
|
9
|
+
await page.getByRole('button', { name: /sign in/i }).click();
|
|
10
|
+
await page.waitForURL('**/admin**');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('admin dashboard loads', async ({ page }) => {
|
|
14
|
+
await expect(page.locator('text=Admin Dashboard')).toBeVisible({ timeout: 10000 });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('shows 5 tab triggers', async ({ page }) => {
|
|
18
|
+
await expect(page.getByRole('tab', { name: /Overview/i })).toBeVisible({ timeout: 10000 });
|
|
19
|
+
await expect(page.getByRole('tab', { name: /Operations/i })).toBeVisible();
|
|
20
|
+
await expect(page.getByRole('tab', { name: /Monitoring/i })).toBeVisible();
|
|
21
|
+
await expect(page.getByRole('tab', { name: /Security/i })).toBeVisible();
|
|
22
|
+
await expect(page.getByRole('tab', { name: /Audit/i })).toBeVisible();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('default tab is overview', async ({ page }) => {
|
|
26
|
+
// Overview tab content should be visible by default
|
|
27
|
+
await expect(page.locator('text=Command Center').first()).toBeVisible({ timeout: 10000 });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('can switch to operations tab', async ({ page }) => {
|
|
31
|
+
await page.locator('button:has-text("Operations"), [role="tab"]:has-text("Operations")').first().click();
|
|
32
|
+
await page.waitForTimeout(500);
|
|
33
|
+
// Operations tab content
|
|
34
|
+
await expect(page.locator('text=Connection').first()).toBeVisible({ timeout: 5000 });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('can switch to security tab', async ({ page }) => {
|
|
38
|
+
await page.locator('button:has-text("Security"), [role="tab"]:has-text("Security")').first().click();
|
|
39
|
+
await page.waitForTimeout(500);
|
|
40
|
+
// Security tab should show Data Masking content
|
|
41
|
+
await expect(page.locator('text=Data Masking').first()).toBeVisible({ timeout: 5000 });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('can switch to audit tab', async ({ page }) => {
|
|
45
|
+
await page.locator('button:has-text("Audit"), [role="tab"]:has-text("Audit")').first().click();
|
|
46
|
+
await page.waitForTimeout(500);
|
|
47
|
+
// Audit tab should show operations/queries
|
|
48
|
+
await expect(page.locator('text=Operations').first()).toBeVisible({ timeout: 5000 });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('editor button navigates to studio', async ({ page }) => {
|
|
52
|
+
const editorBtn = page.locator('button:has-text("Editor"), a:has-text("Editor")').first();
|
|
53
|
+
await editorBtn.click();
|
|
54
|
+
await page.waitForURL('/');
|
|
55
|
+
await expect(page).toHaveURL('/');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('logout button redirects to login', async ({ page }) => {
|
|
59
|
+
const logoutBtn = page.locator('button:has-text("Logout")').first();
|
|
60
|
+
await logoutBtn.click();
|
|
61
|
+
await page.waitForURL('**/login**');
|
|
62
|
+
await expect(page).toHaveURL(/\/login/);
|
|
63
|
+
});
|
|
64
|
+
});
|