fix(log): ES 7.2.0 time format .SSSZ + index dropdown + demo always visible
- ES query 502 fix: remove +00:00 conversion, use native .SSSZ format (ES 7.2.0 date_time = strict_date_optional_time only, rejects +00:00/epoch) - parseLogTime: use time.RFC3339Nano for fractional seconds - new API GET /log-datasources/indices to list ES indices via proxy - LogDatasourceDialog: add '获取索引' button + Select dropdown for ES - LogDatasourcePage: always show demo sources alongside real ones
This commit is contained in:
1 parent
2e179ef7f4
commit
a04d815efe
7 files changed
+308
-15
No files matched your search
@@ -34,6 +34,9 @@ export const logCenterApi = {
|
||||
deleteDatasource: (id: number | string) => http.delete(`/log-datasources/${id}`),
|
||||
setDefaultDatasource: (id: number | string) => http.post(`/log-datasources/${id}/default`),
|
||||
testDatasource: (data: Record<string, unknown>) => http.post('/log-datasources/test', data),
|
||||
/** 通过代理查询 ES 集群索引列表 // Logan.Li */
|
||||
listIndices: (params: Record<string, string>) =>
|
||||
http.get<string[]>('/log-datasources/indices', params),
|
||||
queryLoki: (data: {
|
||||
datasource_id: number;
|
||||
query: string;
|
||||
|
||||
@@ -7,7 +7,10 @@ import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Loader2, Plug } from 'lucide-react';
|
||||
import {
|
||||
Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { Loader2, Plug, RefreshCw } from 'lucide-react';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { logCenterApi, type LogDataSource } from '@/api/monitor/log-center';
|
||||
import type { LogEngine } from './log-center-types';
|
||||
@@ -32,6 +35,9 @@ export function LogDatasourceDialog({
|
||||
const [isDefault, setIsDefault] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [testing, setTesting] = useState(false);
|
||||
// 索引下拉: ES 索引列表 + 加载状态 // Logan.Li
|
||||
const [indices, setIndices] = useState<string[]>([]);
|
||||
const [loadingIndices, setLoadingIndices] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
@@ -44,6 +50,10 @@ export function LogDatasourceDialog({
|
||||
setPass('');
|
||||
setSkipTLS(!!editing.skip_tls_verify);
|
||||
setIsDefault(!!editing.is_default);
|
||||
// 编辑 ES 源时预加载索引列表
|
||||
if (editing.type === 'elasticsearch' && editing.url) {
|
||||
fetchIndices(editing.url, !!editing.basic_auth, editing.basic_auth_user || '', '');
|
||||
}
|
||||
} else {
|
||||
setName(engine === 'loki' ? 'Loki' : 'Elasticsearch');
|
||||
setUrl(engine === 'loki' ? 'http://localhost:3100' : 'http://localhost:9200');
|
||||
@@ -53,9 +63,30 @@ export function LogDatasourceDialog({
|
||||
setPass('');
|
||||
setSkipTLS(false);
|
||||
setIsDefault(false);
|
||||
setIndices([]);
|
||||
}
|
||||
}, [open, editing, engine]);
|
||||
|
||||
const fetchIndices = async (esUrl: string, auth: boolean, authUser: string, authPass: string) => {
|
||||
if (!esUrl) return;
|
||||
setLoadingIndices(true);
|
||||
try {
|
||||
const result = await logCenterApi.listIndices({
|
||||
url: esUrl,
|
||||
basic_auth: auth ? 'true' : 'false',
|
||||
basic_auth_user: authUser,
|
||||
basic_auth_pass: authPass,
|
||||
skip_tls_verify: skipTLS ? 'true' : 'false',
|
||||
});
|
||||
setIndices(Array.isArray(result) ? result : []);
|
||||
} catch {
|
||||
// 静默失败,仍可手动输入
|
||||
setIndices([]);
|
||||
} finally {
|
||||
setLoadingIndices(false);
|
||||
}
|
||||
};
|
||||
|
||||
const payload = () => ({
|
||||
name: name.trim(),
|
||||
type: engine,
|
||||
@@ -121,8 +152,39 @@ export function LogDatasourceDialog({
|
||||
</div>
|
||||
{engine === 'elasticsearch' && (
|
||||
<div>
|
||||
<Label className="text-xs text-[var(--dg-text-muted)]">默认索引</Label>
|
||||
<Input value={defaultIndex} onChange={e => setDefaultIndex(e.target.value)} className="mt-1 h-9 font-mono text-xs bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)]" />
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-xs text-[var(--dg-text-muted)]">默认索引</Label>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-6 px-1.5 text-[10px] text-[var(--dg-text-muted)] hover:text-[var(--dg-text-primary)]"
|
||||
onClick={() => fetchIndices(url, basicAuth, user, pass)}
|
||||
disabled={!url || loadingIndices}
|
||||
>
|
||||
{loadingIndices ? (
|
||||
<Loader2 className="h-3 w-3 animate-spin mr-0.5" />
|
||||
) : (
|
||||
<RefreshCw className="h-3 w-3 mr-0.5" />
|
||||
)}
|
||||
获取索引
|
||||
</Button>
|
||||
</div>
|
||||
{indices.length > 0 ? (
|
||||
<Select value={defaultIndex} onValueChange={setDefaultIndex}>
|
||||
<SelectTrigger className="mt-1 h-9 w-full font-mono text-xs bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)] max-h-48">
|
||||
{indices.map((idx) => (
|
||||
<SelectItem key={idx} value={idx} className="font-mono text-xs">
|
||||
{idx}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
) : (
|
||||
<Input value={defaultIndex} onChange={e => setDefaultIndex(e.target.value)} placeholder="索引名或通配符,如 logstash-*" className="mt-1 h-9 font-mono text-xs bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)]" />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center justify-between">
|
||||
@@ -157,4 +219,4 @@ export function LogDatasourceDialog({
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -32,8 +32,8 @@ export function LogDatasourcePage() {
|
||||
try {
|
||||
const res = await logCenterApi.listDatasources();
|
||||
const apiList: LogDataSource[] = Array.isArray(res) ? res : [];
|
||||
// API 有数据 → 展示真实数据源;无数据 → demo 兜底 // Logan.Li
|
||||
setSources(apiList.length > 0 ? apiList : DEMO_FALLBACK);
|
||||
// 真实数据源在前 + demo 兜底在后 // Logan.Li
|
||||
setSources([...apiList, ...DEMO_FALLBACK]);
|
||||
} catch {
|
||||
setSources(DEMO_FALLBACK);
|
||||
} finally {
|
||||
|
||||
Reference in new issue
Block a user