feat(ui): Data Sources UI — enterprise browser for all 5 source databases
New "Data Sources UI" tab with per-database Browser (catalog + sample data),
Query Console (SqlWorkbench) and embedded interactive Shell (DbShell via SSH).
Backend:
- Extend sql_console.py with Cassandra (CQL) + Neo4j (Cypher) engines
- Add GET /api/sql/catalog/{engine} and GET /api/sql/sample/{engine}
- ssh_terminal: optional initial_command for auto-launching DB CLIs
Frontend:
- DataSourcesView with 5-DB rail, health dots, Browser/Console/Shell sub-tabs
- DbShell embedded xterm terminal with docker exec CLI per engine
- Deep-link topology DB nodes to Data Sources UI (no SQL dock on platform)
- WorkbenchPanel restricted to agent mode only — frees dashboard space
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { Database, Loader2, Play, Zap } from 'lucide-react'
|
||||
import type { SourceEngine } from '../../lib/dataSourceCatalog'
|
||||
import { cn } from '../../lib/utils'
|
||||
import { subTabActive, subTabIdle } from '../../lib/tabActive'
|
||||
|
||||
@@ -14,7 +15,7 @@ type SqlResult = {
|
||||
sql?: string
|
||||
}
|
||||
|
||||
type Engine = 'postgres' | 'mysql' | 'mongodb' | 'trino'
|
||||
type Engine = SourceEngine | 'trino'
|
||||
|
||||
type Props = {
|
||||
engine: Engine
|
||||
@@ -25,7 +26,7 @@ const ENGINE_META: Record<Engine, { title: string; sub: string; accent: string;
|
||||
postgres: {
|
||||
title: 'PostgreSQL SQL Console',
|
||||
sub: 'DB Vault · 10.0.21.51:5432 · user mo',
|
||||
accent: 'text-blue-400',
|
||||
accent: 'text-sky-400',
|
||||
queryLabel: 'SQL',
|
||||
},
|
||||
mysql: {
|
||||
@@ -40,6 +41,18 @@ const ENGINE_META: Record<Engine, { title: string; sub: string; accent: string;
|
||||
accent: 'text-emerald-400',
|
||||
queryLabel: 'Command',
|
||||
},
|
||||
cassandra: {
|
||||
title: 'Cassandra CQL Console',
|
||||
sub: 'DB Vault · 10.0.21.51:9042 · keyspace telemetry',
|
||||
accent: 'text-cyan-400',
|
||||
queryLabel: 'CQL',
|
||||
},
|
||||
neo4j: {
|
||||
title: 'Neo4j Cypher Console',
|
||||
sub: 'DB Vault · 10.0.21.51:7687 · graph database',
|
||||
accent: 'text-pink-400',
|
||||
queryLabel: 'Cypher',
|
||||
},
|
||||
trino: {
|
||||
title: 'Trino SQL Console',
|
||||
sub: 'Lakehouse · 10.0.21.50:8089 · federated queries',
|
||||
@@ -51,7 +64,7 @@ const ENGINE_META: Record<Engine, { title: string; sub: string; accent: string;
|
||||
export function SqlWorkbench({ engine, compact }: Props) {
|
||||
const meta = ENGINE_META[engine]
|
||||
const [samples, setSamples] = useState<Sample[]>([])
|
||||
const [sql, setSql] = useState('SELECT version();')
|
||||
const [sql, setSql] = useState('')
|
||||
const [result, setResult] = useState<SqlResult | null>(null)
|
||||
const [benchmark, setBenchmark] = useState<{
|
||||
comparison?: { postgres_ms?: number; trino_ms?: number; faster?: string; speedup_factor?: number }
|
||||
@@ -146,7 +159,7 @@ export function SqlWorkbench({ engine, compact }: Props) {
|
||||
|
||||
<div className="flex min-h-0 flex-1 overflow-hidden">
|
||||
<aside className={cn('flex min-h-0 shrink-0 flex-col border-r border-border/60 p-1.5', compact ? 'w-44' : 'w-52')}>
|
||||
<p className="mb-0.5 text-[8px] font-semibold uppercase tracking-wider text-foreground-faint">10 demo commands</p>
|
||||
<p className="mb-0.5 text-[8px] font-semibold uppercase tracking-wider text-foreground-faint">Demo queries</p>
|
||||
<div className="scrollbar-thin min-h-0 flex-1 space-y-0.5 overflow-y-auto">
|
||||
{samples.map((s) => (
|
||||
<button
|
||||
@@ -167,7 +180,7 @@ export function SqlWorkbench({ engine, compact }: Props) {
|
||||
onChange={(e) => setSql(e.target.value)}
|
||||
className={cn(
|
||||
'shrink-0 resize-y border-b border-border/60 bg-[#0d1117] p-1.5 font-mono text-[10px] text-emerald-100 outline-none focus:ring-1 focus:ring-docker/40',
|
||||
compact ? 'min-h-[48px] max-h-[96px]' : 'min-h-[56px] max-h-[120px]',
|
||||
compact ? 'min-h-[48px] max-h-[96px]' : 'min-h-[72px] max-h-[140px]',
|
||||
)}
|
||||
spellCheck={false}
|
||||
placeholder={`${meta.queryLabel}…`}
|
||||
|
||||
Reference in New Issue
Block a user