feat: show log indices on OpenSearch admin page
Add prefix query parameter to /admin/opensearch/indices endpoint so the UI can fetch execution and log indices separately. OpenSearch admin page now shows two card sections: Execution Indices and Log Indices, each with doc count and size summary. Page restyled with CSS module replacing inline styles. Delete endpoint also allows log index deletion. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -71,13 +71,14 @@ export function usePipelineStats() {
|
||||
});
|
||||
}
|
||||
|
||||
export function useOpenSearchIndices(page = 0, size = 20, search = '') {
|
||||
export function useOpenSearchIndices(page = 0, size = 20, search = '', prefix = 'executions') {
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'opensearch', 'indices', page, size, search],
|
||||
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}`);
|
||||
},
|
||||
|
||||
63
ui/src/pages/Admin/OpenSearchAdminPage.module.css
Normal file
63
ui/src/pages/Admin/OpenSearchAdminPage.module.css
Normal file
@@ -0,0 +1,63 @@
|
||||
.statStrip {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.pipelineCard {
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-card);
|
||||
padding: 16px 20px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.pipelineTitle {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.pipelineMetrics {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.pipelineMetrics span {
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
.indexSection {
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-card);
|
||||
margin-bottom: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.indexHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
}
|
||||
|
||||
.indexTitle {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.indexMeta {
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
import { StatCard, Card, DataTable, Badge, ProgressBar, Spinner } from '@cameleer/design-system';
|
||||
import { StatCard, DataTable, Badge, ProgressBar } from '@cameleer/design-system';
|
||||
import type { Column } from '@cameleer/design-system';
|
||||
import { useOpenSearchStatus, usePipelineStats, useOpenSearchIndices, useOpenSearchPerformance, useDeleteIndex } from '../../api/queries/admin/opensearch';
|
||||
import { useState } from 'react';
|
||||
import styles from './OpenSearchAdminPage.module.css';
|
||||
|
||||
export default function OpenSearchAdminPage() {
|
||||
const { data: status } = useOpenSearchStatus();
|
||||
const { data: pipeline } = usePipelineStats();
|
||||
const { data: perf } = useOpenSearchPerformance();
|
||||
const { data: indicesData } = useOpenSearchIndices();
|
||||
const { data: execIndices } = useOpenSearchIndices(0, 50, '', 'executions');
|
||||
const { data: logIndices } = useOpenSearchIndices(0, 50, '', 'logs');
|
||||
const deleteIndex = useDeleteIndex();
|
||||
|
||||
const indexColumns: Column<any>[] = [
|
||||
@@ -20,37 +21,55 @@ export default function OpenSearchAdminPage() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2 style={{ marginBottom: '1rem' }}>OpenSearch Administration</h2>
|
||||
|
||||
<div style={{ display: 'flex', gap: '1rem', marginBottom: '1.5rem', flexWrap: 'wrap' }}>
|
||||
<div className={styles.statStrip}>
|
||||
<StatCard label="Status" value={status?.reachable ? 'Connected' : 'Disconnected'} accent={status?.reachable ? 'success' : 'error'} />
|
||||
<StatCard label="Health" value={status?.clusterHealth ?? '—'} accent={status?.clusterHealth === 'green' ? 'success' : 'warning'} />
|
||||
<StatCard label="Version" value={status?.version ?? '—'} />
|
||||
<StatCard label="Health" value={status?.clusterHealth ?? '\u2014'} accent={status?.clusterHealth === 'green' ? 'success' : 'warning'} />
|
||||
<StatCard label="Version" value={status?.version ?? '\u2014'} />
|
||||
<StatCard label="Nodes" value={status?.nodeCount ?? 0} />
|
||||
</div>
|
||||
|
||||
{pipeline && (
|
||||
<Card>
|
||||
<div style={{ padding: '1rem' }}>
|
||||
<h3 style={{ marginBottom: '0.5rem' }}>Indexing Pipeline</h3>
|
||||
<ProgressBar value={(pipeline.queueDepth / pipeline.maxQueueSize) * 100} />
|
||||
<div style={{ display: 'flex', gap: '2rem', marginTop: '0.5rem', fontSize: '0.875rem' }}>
|
||||
<span>Queue: {pipeline.queueDepth}/{pipeline.maxQueueSize}</span>
|
||||
<span>Indexed: {pipeline.indexedCount}</span>
|
||||
<span>Failed: {pipeline.failedCount}</span>
|
||||
<span>Rate: {pipeline.indexingRate}/s</span>
|
||||
</div>
|
||||
<div className={styles.pipelineCard}>
|
||||
<div className={styles.pipelineTitle}>Indexing Pipeline</div>
|
||||
<ProgressBar value={(pipeline.queueDepth / pipeline.maxQueueSize) * 100} />
|
||||
<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}/s</span>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div style={{ marginTop: '1.5rem' }}>
|
||||
<h3 style={{ marginBottom: '0.75rem' }}>Indices</h3>
|
||||
<div className={styles.indexSection}>
|
||||
<div className={styles.indexHeader}>
|
||||
<span className={styles.indexTitle}>Execution Indices ({execIndices?.totalIndices ?? 0})</span>
|
||||
<span className={styles.indexMeta}>
|
||||
{execIndices ? `${execIndices.totalDocs.toLocaleString()} docs \u00b7 ${execIndices.totalSize}` : ''}
|
||||
</span>
|
||||
</div>
|
||||
<DataTable
|
||||
columns={indexColumns}
|
||||
data={(indicesData?.indices || []).map((i: any) => ({ ...i, id: i.name }))}
|
||||
data={(execIndices?.indices || []).map((i: any) => ({ ...i, id: i.name }))}
|
||||
sortable
|
||||
pageSize={20}
|
||||
flush
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.indexSection}>
|
||||
<div className={styles.indexHeader}>
|
||||
<span className={styles.indexTitle}>Log Indices ({logIndices?.totalIndices ?? 0})</span>
|
||||
<span className={styles.indexMeta}>
|
||||
{logIndices ? `${logIndices.totalDocs.toLocaleString()} docs \u00b7 ${logIndices.totalSize}` : ''}
|
||||
</span>
|
||||
</div>
|
||||
<DataTable
|
||||
columns={indexColumns}
|
||||
data={(logIndices?.indices || []).map((i: any) => ({ ...i, id: i.name }))}
|
||||
sortable
|
||||
pageSize={20}
|
||||
flush
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user