refactor(frontend): api/components/pages 目录架构统一对齐菜单域 + 日志中心修复
1. 目录架构统一(API = components = 菜单域,一域一目录 + barrel) - log/ 提为独立顶层域(日志中心在菜单中与监控中心平级,不再塞在 monitor/) - task/ 拆分原 automation.ts → cron-job / script / remote-exec - aiops/ 合并 ai.ts + agentCli.ts → assistant + agent-cli - agent/ approval/ audit/ 提升为域目录 - auth 拆分:authApi(认证自服务) + system/user.ts(userApi+ldapApi 管理端) - components/automation → components/task,菜单"自动化运维"→"作业任务" - pages/ 按域分子目录(approval/audit/monitor),删孤儿 AuditPage.tsx - 所有 API 符号名不变,组件经 @/api 聚合引用,运行时行为零变化 2. Loki 查询无返回修复:aiops 容器 DNS 解析外网域名失败 (宿主机 systemd-resolved 转发链路异常),docker-compose 显式配 dns 上游 3. ES 查询框布局:工具栏单行挤 7 控件(ES 多 150px 索引框),拆两行—— 查询输入独占一行(+查询按钮),次级控件换行 flex-wrap Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
da01cbb119
commit
c0ee8e4ddd
74 files changed
+229
-216
No files matched your search
@@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { User, Lock, Palette, Check, Loader2, AlertCircle, Eye, EyeOff, Languages } from 'lucide-react';
|
||||
import { useUserStore, useLanguageStore } from '@/store';
|
||||
import { userApi } from '@/api';
|
||||
import { authApi } from '@/api';
|
||||
import { useTheme, THEME_OPTIONS } from '@/hooks/useTheme';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { SUPPORTED_LANGUAGES, type AppLanguage } from '@/i18n';
|
||||
@@ -89,7 +89,7 @@ export function Login() {
|
||||
setError('');
|
||||
setLoading(true);
|
||||
try {
|
||||
const result = await userApi.login(username, password) as { user: any; token: string; permissions?: string[] };
|
||||
const result = await authApi.login(username, password) as { user: any; token: string; permissions?: string[] };
|
||||
useUserStore.getState().login(result.user, result.token, result.permissions ?? null);
|
||||
localStorage.setItem('token', result.token);
|
||||
navigate('/', { replace: true });
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useEffect, useMemo, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { CodeSnippetBlock } from '@/components/ui/code-snippet-block';
|
||||
import { userApi } from '@/api';
|
||||
import { authApi } from '@/api';
|
||||
import { getPublicApiBase } from '@/utils/request';
|
||||
import { getSessionApiAccessKey } from '@/utils/storage';
|
||||
|
||||
@@ -37,7 +37,7 @@ export function CICDWebhookContent({
|
||||
const cached = getSessionApiAccessKey();
|
||||
if (cached) setAccessKey(cached);
|
||||
try {
|
||||
const meta = await userApi.getApiAccessKey();
|
||||
const meta = await authApi.getApiAccessKey();
|
||||
if (!cancelled) {
|
||||
setHasKey(!!meta?.has_token);
|
||||
setKeyExpired(!!meta?.expired);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useThemeStore, useUserStore } from '@/store';
|
||||
import { Sidebar } from './Sidebar';
|
||||
import { Header } from './Header';
|
||||
import { Breadcrumb } from './Breadcrumb';
|
||||
import { userApi } from '@/api';
|
||||
import { authApi } from '@/api';
|
||||
import type { JsonData } from '@/types/common';
|
||||
|
||||
/**
|
||||
@@ -30,7 +30,7 @@ export function MainLayout() {
|
||||
useEffect(() => {
|
||||
if (!token || permissions !== null) return;
|
||||
let cancelled = false;
|
||||
userApi.getMyPermissions()
|
||||
authApi.getMyPermissions()
|
||||
.then((data: JsonData) => {
|
||||
if (cancelled) return;
|
||||
const perms: string[] = data?.permissions ?? [];
|
||||
|
||||
@@ -136,7 +136,7 @@ const menuItems: MenuItem[] = [
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'automation', name: '自动化运维', labelKey: 'menu.automation', icon: PlayCircle, permission: 'auto:cron',
|
||||
id: 'automation', name: '作业任务', labelKey: 'menu.automation', icon: PlayCircle, permission: 'auto:cron',
|
||||
children: [
|
||||
{ id: 'cronjobs', name: '定时任务', labelKey: 'menu.cronJobs', icon: PlayCircle, path: '/cronjobs', permission: 'auto:cron' },
|
||||
{ id: 'scripts', name: '脚本库', labelKey: 'menu.scripts', icon: FileCode, path: '/scripts', permission: 'auto:script' },
|
||||
|
||||
@@ -13,8 +13,7 @@ import {
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { alertApi } from '@/api';
|
||||
import { logCenterApi, type LogDataSource } from '@/api/monitor/log-center';
|
||||
import { alertApi, logCenterApi, type LogDataSource } from '@/api';
|
||||
import { parseChannels, getChannelIcon, getChannelTypeLabel } from './alertShared';
|
||||
import type { JsonData } from '@/types/common';
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from '@/components/ui/select';
|
||||
import { Loader2, Plug, RefreshCw } from 'lucide-react';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { logCenterApi, type LogDataSource, type TestConnectionResult } from '@/api/monitor/log-center';
|
||||
import { logCenterApi, type LogDataSource, type TestConnectionResult } from '@/api';
|
||||
import type { LogEngine } from './log-center-types';
|
||||
|
||||
export function LogDatasourceDialog({
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { useConfirm } from '@/components/ui/confirm-dialog';
|
||||
import { logCenterApi, type LogDataSource, type TestConnectionResult } from '@/api/monitor/log-center';
|
||||
import { logCenterApi, type LogDataSource, type TestConnectionResult } from '@/api';
|
||||
import { LogDatasourceDialog } from './LogDatasourceDialog';
|
||||
import type { LogEngine } from './log-center-types';
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { LogClusterPanel } from './LogClusterPanel';
|
||||
import { SaveQueryDialog, SaveAlertDialog } from './SaveQueryDialog';
|
||||
import { QueryHistoryPanel } from './QueryHistoryPanel';
|
||||
import { LogAlertFromQueryDialog } from './LogAlertFromQueryDialog';
|
||||
import { logCenterApi, type LogDataSource } from '@/api/monitor/log-center';
|
||||
import { logCenterApi, type LogDataSource } from '@/api';
|
||||
import type { LogEngine, TimeRange, ResultTab, ViewMode, LogEntry, StatTable, LogCluster, QueryHistoryItem } from './types-v3';
|
||||
import { ENGINE_META, buildStatTableFromEntries, buildClustersFromEntries, extractFields, generateHistogramData, historyStorage } from './types-v3';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
@@ -280,8 +280,13 @@ export function UnifiedQueryPanel({ engine, onRuleCreated }: Props) {
|
||||
<span className="text-[11px] font-semibold" style={{ color: badge.dotColor }}>{badge.label}</span>
|
||||
</div>
|
||||
<SyntaxHighlightInput value={query} onChange={setQuery} engine={effEngine} placeholder={meta.queryPlaceholder} />
|
||||
<button onClick={handleRun} disabled={loading || !currentSource} className="h-10 px-4 rounded text-xs font-medium text-white flex items-center gap-1.5 flex-shrink-0 transition-opacity hover:opacity-90" style={{ background: '#22c55e' }}>
|
||||
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Play className="h-4 w-4" />} 查询
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{/* 次级控件行(时间 / 数据源 / 索引 / 上限 / 另存为 / 历史)// Logan.Li */}
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<TimeRangePicker value={timeRange} onChange={setTimeRange} />
|
||||
|
||||
<select value={dsId} onChange={e => setDsId(e.target.value)} className="h-10 px-2 text-xs rounded flex-shrink-0" style={{ background: 'var(--dg-bg-tertiary)', border: '1px solid var(--dg-border)', color: 'var(--dg-text-primary)' }}>
|
||||
@@ -316,10 +321,6 @@ export function UnifiedQueryPanel({ engine, onRuleCreated }: Props) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button onClick={handleRun} disabled={loading || !currentSource} className="h-10 px-4 rounded text-xs font-medium text-white flex items-center gap-1.5 flex-shrink-0 transition-opacity hover:opacity-90" style={{ background: '#22c55e' }}>
|
||||
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Play className="h-4 w-4" />} 查询
|
||||
</button>
|
||||
|
||||
<div className="relative flex-shrink-0">
|
||||
<button onClick={() => setSaveMenuOpen(!saveMenuOpen)} className="h-10 px-3 rounded text-xs flex items-center gap-1" style={{ border: '1px solid var(--dg-border)', color: 'var(--dg-text-secondary)', background: 'var(--dg-bg-tertiary)' }}>
|
||||
<Save className="h-3.5 w-3.5" /> 另存为 <ChevronDown className="h-3 w-3" />
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -14,7 +14,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { useConfirm } from '@/components/ui/confirm-dialog';
|
||||
import { userApi } from '@/api';
|
||||
import { authApi } from '@/api';
|
||||
import { getPublicApiBase } from '@/utils/request';
|
||||
import { getSessionApiAccessKey, setSessionApiAccessKey } from '@/utils/storage';
|
||||
import { cn } from '@/lib/utils';
|
||||
@@ -93,7 +93,7 @@ export function ApiAccessKeySection() {
|
||||
try {
|
||||
const cached = getSessionApiAccessKey();
|
||||
if (cached) setPlainToken(cached);
|
||||
const meta = await userApi.getApiAccessKey().catch(() => ({ has_token: false }));
|
||||
const meta = await authApi.getApiAccessKey().catch(() => ({ has_token: false }));
|
||||
setKeyMeta(meta);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -130,7 +130,7 @@ export function ApiAccessKeySection() {
|
||||
if (formMode === 'edit') {
|
||||
setActionLoading(true);
|
||||
try {
|
||||
const res = await userApi.updateApiAccessKey({
|
||||
const res = await authApi.updateApiAccessKey({
|
||||
description: formDescription,
|
||||
expires_at: formExpiresAt,
|
||||
});
|
||||
@@ -156,7 +156,7 @@ export function ApiAccessKeySection() {
|
||||
|
||||
setActionLoading(true);
|
||||
try {
|
||||
const res = await userApi.regenerateApiAccessKey({
|
||||
const res = await authApi.regenerateApiAccessKey({
|
||||
description: formDescription,
|
||||
expires_at: formExpiresAt,
|
||||
});
|
||||
@@ -195,7 +195,7 @@ export function ApiAccessKeySection() {
|
||||
if (!ok) return;
|
||||
setActionLoading(true);
|
||||
try {
|
||||
await userApi.revokeApiAccessKey();
|
||||
await authApi.revokeApiAccessKey();
|
||||
setPlainToken('');
|
||||
setSessionApiAccessKey('');
|
||||
setKeyMeta({ has_token: false });
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Label } from '@/components/ui/label';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { User, Shield, Loader2 } from 'lucide-react';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { userApi } from '@/api';
|
||||
import { authApi } from '@/api';
|
||||
import { useUserStore } from '@/store';
|
||||
import { ApiAccessKeySection } from './ApiAccessKeySection';
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function ProfilePage() {
|
||||
const load = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const p = await userApi.getProfile();
|
||||
const p = await authApi.getProfile();
|
||||
setProfile(p);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -41,7 +41,7 @@ export default function ProfilePage() {
|
||||
if (!oldPwd || newPwd.length < 6) return;
|
||||
setPwdSaving(true);
|
||||
try {
|
||||
await userApi.changePassword({ old_password: oldPwd, new_password: newPwd });
|
||||
await authApi.changePassword({ old_password: oldPwd, new_password: newPwd });
|
||||
toast({ title: '密码已修改' });
|
||||
setOldPwd('');
|
||||
setNewPwd('');
|
||||
|
||||
Reference in new issue
Block a user