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,77 @@
import { useQuery } from '@tanstack/react-query';
import { adminFetch } from './admin-api';
import { useRefreshInterval } from '../use-refresh-interval';
// ── Types ──────────────────────────────────────────────────────────────
export interface ClickHouseStatus {
reachable: boolean;
version: string | null;
uptime: string | null;
host: string | null;
}
export interface ClickHouseTableInfo {
name: string;
engine: string;
rowCount: number;
dataSize: string;
dataSizeBytes: number;
partitionCount: number;
}
export interface ClickHousePerformance {
queryCount: number;
insertQueryCount: number;
memoryUsage: string;
insertedRows: number;
readRows: number;
}
export interface IndexerPipeline {
queueDepth: number;
maxQueueSize: number;
failedCount: number;
indexedCount: number;
debounceMs: number;
indexingRate: number;
lastIndexedAt: string | null;
}
// ── Query Hooks ────────────────────────────────────────────────────────
export function useClickHouseStatus() {
const refetchInterval = useRefreshInterval(30_000);
return useQuery({
queryKey: ['admin', 'clickhouse', 'status'],
queryFn: () => adminFetch<ClickHouseStatus>('/clickhouse/status'),
refetchInterval,
});
}
export function useClickHouseTables() {
const refetchInterval = useRefreshInterval(60_000);
return useQuery({
queryKey: ['admin', 'clickhouse', 'tables'],
queryFn: () => adminFetch<ClickHouseTableInfo[]>('/clickhouse/tables'),
refetchInterval,
});
}
export function useClickHousePerformance() {
const refetchInterval = useRefreshInterval(30_000);
return useQuery({
queryKey: ['admin', 'clickhouse', 'performance'],
queryFn: () => adminFetch<ClickHousePerformance>('/clickhouse/performance'),
refetchInterval,
});
}
export function useIndexerPipeline() {
const refetchInterval = useRefreshInterval(10_000);
return useQuery({
queryKey: ['admin', 'clickhouse', 'pipeline'],
queryFn: () => adminFetch<IndexerPipeline>('/clickhouse/pipeline'),
refetchInterval,
});
}

View File

@@ -1,109 +0,0 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { adminFetch } from './admin-api';
import { useRefreshInterval } from '../use-refresh-interval';
// ── Types ──────────────────────────────────────────────────────────────
export interface OpenSearchStatus {
reachable: boolean;
clusterHealth: string;
version: string | null;
nodeCount: number;
host: string;
}
export interface PipelineStats {
queueDepth: number;
maxQueueSize: number;
failedCount: number;
indexedCount: number;
debounceMs: number;
indexingRate: number;
lastIndexedAt: string | null;
}
export interface IndexInfo {
name: string;
docCount: number;
size: string;
sizeBytes: number;
health: string;
primaryShards: number;
replicas: number;
}
export interface IndicesPage {
indices: IndexInfo[];
totalIndices: number;
totalDocs: number;
totalSize: string;
page: number;
pageSize: number;
totalPages: number;
}
export interface PerformanceStats {
queryCacheHitRate: number;
requestCacheHitRate: number;
searchLatencyMs: number;
indexingLatencyMs: number;
heapUsedBytes: number;
heapMaxBytes: number;
}
// ── Query Hooks ────────────────────────────────────────────────────────
export function useOpenSearchStatus() {
const refetchInterval = useRefreshInterval(30_000);
return useQuery({
queryKey: ['admin', 'opensearch', 'status'],
queryFn: () => adminFetch<OpenSearchStatus>('/opensearch/status'),
refetchInterval,
});
}
export function usePipelineStats() {
const refetchInterval = useRefreshInterval(10_000);
return useQuery({
queryKey: ['admin', 'opensearch', 'pipeline'],
queryFn: () => adminFetch<PipelineStats>('/opensearch/pipeline'),
refetchInterval,
});
}
export function useOpenSearchIndices(page = 0, size = 20, search = '', prefix = 'executions') {
return useQuery({
queryKey: ['admin', 'opensearch', 'indices', prefix, page, size, search],
queryFn: () => {
const params = new URLSearchParams();
params.set('page', String(page));
params.set('size', String(size));
params.set('prefix', prefix);
if (search) params.set('search', search);
return adminFetch<IndicesPage>(`/opensearch/indices?${params}`);
},
placeholderData: (prev) => prev,
});
}
export function useOpenSearchPerformance() {
const refetchInterval = useRefreshInterval(30_000);
return useQuery({
queryKey: ['admin', 'opensearch', 'performance'],
queryFn: () => adminFetch<PerformanceStats>('/opensearch/performance'),
refetchInterval,
});
}
// ── Mutation Hooks ─────────────────────────────────────────────────────
export function useDeleteIndex() {
const qc = useQueryClient();
return useMutation({
mutationFn: (indexName: string) =>
adminFetch<void>(`/opensearch/indices/${indexName}`, { method: 'DELETE' }),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['admin', 'opensearch', 'indices'] });
},
});
}

View File

@@ -10,20 +10,8 @@ export interface DatabaseThresholds {
queryDurationCritical: number;
}
export interface OpenSearchThresholds {
clusterHealthWarning: string;
clusterHealthCritical: string;
queueDepthWarning: number;
queueDepthCritical: number;
jvmHeapWarning: number;
jvmHeapCritical: number;
failedDocsWarning: number;
failedDocsCritical: number;
}
export interface ThresholdConfig {
database: DatabaseThresholds;
opensearch: OpenSearchThresholds;
}
// ── Query Hooks ────────────────────────────────────────────────────────