feat: remove OpenSearch, add ClickHouse admin page
Some checks failed
CI / cleanup-branch (push) Has been skipped
CI / build (push) Failing after 33s
CI / docker (push) Has been skipped
CI / deploy (push) Has been skipped
CI / deploy-feature (push) Has been skipped

Remove all OpenSearch code, dependencies, configuration, deployment
manifests, and CI/CD references. Replace the OpenSearch admin page
with a ClickHouse admin page showing cluster status, table sizes,
performance metrics, and indexer pipeline stats.

- Delete 11 OpenSearch Java files (config, search impl, admin controller, DTOs, tests)
- Delete 3 OpenSearch frontend files (admin page, CSS, query hooks)
- Delete deploy/opensearch.yaml K8s manifest
- Remove opensearch Maven dependencies from pom.xml
- Remove opensearch config from application.yml, Dockerfile, docker-compose
- Remove opensearch from CI workflow (secrets, deploy, cleanup steps)
- Simplify ThresholdConfig (remove OpenSearch thresholds, database-only)
- Change default search backend from opensearch to clickhouse
- Add ClickHouseAdminController with /status, /tables, /performance, /pipeline
- Add ClickHouseAdminPage with StatCards, pipeline ProgressBar, tables DataTable
- Update CLAUDE.md, HOWTO.md, and source comments

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-01 18:56:06 +02:00
parent 5ed7d38bf7
commit 283e38a20d
49 changed files with 356 additions and 1753 deletions

View File

@@ -0,0 +1,65 @@
import { StatCard, DataTable, ProgressBar } from '@cameleer/design-system';
import type { Column } from '@cameleer/design-system';
import { useClickHouseStatus, useClickHouseTables, useClickHousePerformance, useIndexerPipeline } from '../../api/queries/admin/clickhouse';
import styles from './ClickHouseAdminPage.module.css';
export default function ClickHouseAdminPage() {
const { data: status, isError: statusError } = useClickHouseStatus();
const { data: tables } = useClickHouseTables();
const { data: perf } = useClickHousePerformance();
const { data: pipeline } = useIndexerPipeline();
const unreachable = statusError || (status && !status.reachable);
const tableColumns: Column<any>[] = [
{ key: 'name', header: 'Table', sortable: true },
{ key: 'engine', header: 'Engine' },
{ key: 'rowCount', header: 'Rows', sortable: true, render: (v) => Number(v).toLocaleString() },
{ key: 'dataSize', header: 'Size', sortable: true },
{ key: 'partitionCount', header: 'Partitions', sortable: true },
];
return (
<div>
<div className={styles.statStrip}>
<StatCard label="Status" value={unreachable ? 'Disconnected' : status ? 'Connected' : '\u2014'} accent={unreachable ? 'error' : status ? 'success' : undefined} />
<StatCard label="Version" value={status?.version ?? '\u2014'} />
<StatCard label="Uptime" value={status?.uptime ?? '\u2014'} />
</div>
{pipeline && (
<div className={styles.pipelineCard}>
<div className={styles.pipelineTitle}>Indexer Pipeline</div>
<ProgressBar value={pipeline.maxQueueSize > 0 ? (pipeline.queueDepth / pipeline.maxQueueSize) * 100 : 0} />
<div className={styles.pipelineMetrics}>
<span>Queue: {pipeline.queueDepth}/{pipeline.maxQueueSize}</span>
<span>Indexed: {pipeline.indexedCount.toLocaleString()}</span>
<span>Failed: {pipeline.failedCount}</span>
<span>Rate: {pipeline.indexingRate.toFixed(1)}/s</span>
</div>
</div>
)}
{perf && (
<div className={styles.statStrip}>
<StatCard label="Select Queries" value={perf.queryCount.toLocaleString()} />
<StatCard label="Insert Queries" value={perf.insertQueryCount.toLocaleString()} />
<StatCard label="Memory" value={perf.memoryUsage} />
<StatCard label="Rows Read" value={perf.readRows.toLocaleString()} />
</div>
)}
<div className={styles.tableSection}>
<div className={styles.tableHeader}>
<span className={styles.tableTitle}>Tables ({(tables || []).length})</span>
</div>
<DataTable
columns={tableColumns}
data={(tables || []).map((t: any) => ({ ...t, id: t.name }))}
sortable
pageSize={20}
flush
/>
</div>
</div>
);
}