fix: merge后14个测试修复+前端TS编译修复 - 895/895通过+前端零错误
This commit is contained in:
1 parent
58eb78c987
commit
b39c426317
24 files changed
+3918
-760
No files matched your search
@@ -9,6 +9,9 @@ export interface LoginResponse {
|
||||
display_name: string;
|
||||
role: string;
|
||||
email: string | null;
|
||||
data_source_permissions?: string;
|
||||
custom_permissions?: string;
|
||||
group_ids?: number[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,6 +21,9 @@ export interface MeResponse {
|
||||
display_name: string;
|
||||
role: string;
|
||||
email: string | null;
|
||||
data_source_permissions?: string;
|
||||
custom_permissions?: string;
|
||||
group_ids?: number[];
|
||||
}
|
||||
|
||||
export const authApi = {
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
} from '@codemirror/language';
|
||||
import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
|
||||
import { searchKeymap, highlightSelectionMatches } from '@codemirror/search';
|
||||
import { rectangularSelection } from '@codemirror/rectangular-selection';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
@@ -296,7 +295,6 @@ const SqlEditor: React.FC<SqlEditorProps> = ({
|
||||
bracketMatching(),
|
||||
closeBrackets(),
|
||||
highlightSelectionMatches(),
|
||||
rectangularSelection(),
|
||||
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
||||
appTheme,
|
||||
oneDark,
|
||||
|
||||
@@ -335,7 +335,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
table?: string
|
||||
): boolean => {
|
||||
if (!isLoggedIn) return false;
|
||||
if (isAdmin) return true; // 管理员拥有所有数据源权限
|
||||
if (user.role === 'admin') return true; // 管理员拥有所有数据源权限
|
||||
|
||||
const cache = permissionCacheRef.current;
|
||||
const cacheKey = `${instanceId}:${action}:${database || '*'}:${table || '*'}`;
|
||||
@@ -368,7 +368,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
cache.dataSource.set(cacheKey, hasPerm);
|
||||
cache.timestamp = Date.now();
|
||||
return hasPerm;
|
||||
}, [user.dataSourcePermissions, user.role, isAdmin, isLoggedIn, isCacheValid]);
|
||||
}, [user.dataSourcePermissions, user.role, isLoggedIn, isCacheValid]);
|
||||
|
||||
/** 批量检查数据源权限 */
|
||||
const checkDataSourcePermissions = useCallback((
|
||||
@@ -387,34 +387,31 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
/** 从服务器刷新用户权限 */
|
||||
const refreshPermissions = useCallback(async (): Promise<void> => {
|
||||
try {
|
||||
const res = await authApi.getCurrentUser();
|
||||
if (res.user) {
|
||||
const u = res.user;
|
||||
const role = u.role as UserRole;
|
||||
const dataSourcePermissions: DataSourcePermission[] = u.data_source_permissions
|
||||
? JSON.parse(u.data_source_permissions)
|
||||
: [];
|
||||
const customPermissions: Record<string, boolean> = u.custom_permissions
|
||||
? JSON.parse(u.custom_permissions)
|
||||
: {};
|
||||
const u = await authApi.getMe();
|
||||
const role = u.role as UserRole;
|
||||
const dataSourcePermissions: DataSourcePermission[] = u.data_source_permissions
|
||||
? JSON.parse(u.data_source_permissions)
|
||||
: [];
|
||||
const customPermissions: Record<string, boolean> = u.custom_permissions
|
||||
? JSON.parse(u.custom_permissions)
|
||||
: {};
|
||||
|
||||
const newUser: AuthUser = {
|
||||
...user,
|
||||
id: u.id,
|
||||
username: u.username,
|
||||
email: u.email || '',
|
||||
role,
|
||||
roleName: roleNameMap[role] || role,
|
||||
groupIds: u.group_ids || [],
|
||||
dataSourcePermissions,
|
||||
customPermissions,
|
||||
permissionsUpdatedAt: Date.now(),
|
||||
};
|
||||
const newUser: AuthUser = {
|
||||
...user,
|
||||
id: u.id,
|
||||
username: u.username,
|
||||
email: u.email || '',
|
||||
role,
|
||||
roleName: roleNameMap[role] || role,
|
||||
groupIds: u.group_ids || [],
|
||||
dataSourcePermissions,
|
||||
customPermissions,
|
||||
permissionsUpdatedAt: Date.now(),
|
||||
};
|
||||
|
||||
setUser(newUser);
|
||||
clearPermissionCache();
|
||||
emitPermissionChange(newUser);
|
||||
}
|
||||
setUser(newUser);
|
||||
clearPermissionCache();
|
||||
emitPermissionChange(newUser);
|
||||
} catch (error) {
|
||||
console.error('刷新权限失败:', error);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
Search, Download, Filter, X, ChevronDown, ChevronRight,
|
||||
Search, Download, Filter, ChevronDown, ChevronRight,
|
||||
FileText, User, Globe, CheckCircle2, XCircle, AlertTriangle,
|
||||
BarChart3, Clock, RotateCcw,
|
||||
} from 'lucide-react';
|
||||
@@ -131,7 +131,7 @@ function exportLogsToCSV(logs: AuditLog[]) {
|
||||
|
||||
export default function AuditLogsPage() {
|
||||
const [logs, setLogs] = useState<AuditLog[]>([]);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [, setTotal] = useState(0);
|
||||
const [page, setPage] = useState(1);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const pageSize = 20;
|
||||
|
||||
@@ -2,8 +2,8 @@ import { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import request from '../api/request';
|
||||
import {
|
||||
Search, Filter, ChevronDown, ChevronUp, AlertTriangle,
|
||||
Info, CheckCircle, XCircle, Database, Table, FileSearch,
|
||||
Shield, Loader2, Save, RotateCcw, Leaf, Server,
|
||||
Info, XCircle, Database, Table, FileSearch,
|
||||
Shield, Loader2, Leaf, Server,
|
||||
} from 'lucide-react';
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
@@ -136,7 +136,7 @@ export default function AuditRulesPage() {
|
||||
const res = await request.get('/audit-rules', {
|
||||
params: { db_type: dbType },
|
||||
});
|
||||
const data = (res as { data?: unknown[] }).data ?? (res as unknown[]);
|
||||
const data = (res as { data?: unknown[] }).data ?? (res as unknown as unknown[]);
|
||||
setRules(Array.isArray(data) ? data as AuditRule[] : []);
|
||||
} catch {
|
||||
setRules([]);
|
||||
|
||||
@@ -2,9 +2,9 @@ import { useEffect, useRef, useState, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
ClipboardList, Clock, CheckCircle, AlertTriangle,
|
||||
Database, ChevronRight, X, AlertCircle, Info, Bell,
|
||||
Activity, TrendingUp, Zap, Server, Plus, Search,
|
||||
Activity, TrendingUp, Zap, Server, Search,
|
||||
FileText, ArrowUpRight, ArrowDownRight, Minus,
|
||||
Filter, Eye, Loader2, BarChart3,
|
||||
Filter, Eye, BarChart3,
|
||||
} from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { dbInstances, statusMap, statusColorMap, dbTypeColorMap, tickets, announcements } from '@/lib/mock';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
BookOpen, Database, Table2, Search, Download, Columns3, Key, Code2, RefreshCw,
|
||||
ChevronRight, ChevronDown, Hash, AlignLeft, Gauge, FolderOpen, ArrowRight,
|
||||
ChevronRight, Hash, AlignLeft, Gauge, FolderOpen, ArrowRight,
|
||||
FileJson, TableProperties, Info,
|
||||
} from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@@ -173,7 +173,6 @@ export default function DataDictionaryPage() {
|
||||
const [activeTab, setActiveTab] = useState<TabKey>('columns');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [expandedDbs, setExpandedDbs] = useState<Set<string>>(new Set());
|
||||
const [dbTreeExpanded, setDbTreeExpanded] = useState(true);
|
||||
|
||||
// Load instances
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
Plus, Search, Edit2, Trash2, Server, Users, ChevronRight, X,
|
||||
XCircle, Shield, Grid3x3, BarChart3, Clock, CheckCircle2,
|
||||
Database, HardDrive, Cpu, Activity, Filter, RotateCcw, ChevronDown,
|
||||
Shield, Grid3x3, Clock, CheckCircle2,
|
||||
Database, HardDrive, Activity,
|
||||
} from 'lucide-react';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { instanceGroupApi, type InstanceGroup } from '@/api/instanceGroup';
|
||||
|
||||
@@ -2,8 +2,8 @@ import { useState, useEffect, useMemo, useCallback, useRef } from 'react';
|
||||
import {
|
||||
Plus, X, AlertTriangle, CheckCircle, Play, Loader2,
|
||||
ChevronDown, Clock, FileText, Sparkles, Star,
|
||||
StarOff, Download, Search, RotateCcw, ArrowUpDown,
|
||||
ArrowUp, ArrowDown, Eye, Hash, PanelLeftClose, PanelLeft,
|
||||
StarOff, Download, Search, ArrowUpDown,
|
||||
ArrowUp, ArrowDown, Eye, Hash, PanelLeftClose,
|
||||
} from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { useActionPermission } from '@/hooks/useAuth';
|
||||
@@ -12,7 +12,7 @@ import EmptyState from '@/components/EmptyState';
|
||||
import { queryApi } from '@/api/query';
|
||||
import { instanceApi } from '@/api/instance';
|
||||
import SqlEditor from '@/components/SqlEditor';
|
||||
import { queryTemplates, getTemplateSQL, getTemplatesForType } from '@/lib/queryTemplates';
|
||||
import { getTemplateSQL, getTemplatesForType } from '@/lib/queryTemplates';
|
||||
import type { DBType, QueryTemplate } from '@/lib/queryTemplates';
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
Search, Clock, Database, AlertTriangle, ChevronDown, ChevronRight,
|
||||
BarChart3, Play, Loader2, RefreshCw, Download, Lightbulb, X,
|
||||
GitCompare, XCircle, FileText, Skull, Zap, CheckCircle2,
|
||||
GitCompare, XCircle, Skull, Zap, CheckCircle2,
|
||||
} from 'lucide-react';
|
||||
import {
|
||||
BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid,
|
||||
|
||||
@@ -174,7 +174,7 @@ export default function TicketCreatePage() {
|
||||
return;
|
||||
}
|
||||
setLoadingDatabases(true);
|
||||
instanceApi.getDatabases?.(Number(instanceId))
|
||||
instanceApi.listDatabases(Number(instanceId))
|
||||
.then((res: unknown) => {
|
||||
const data = (res as { databases?: string[] })?.databases
|
||||
|| (res as string[])
|
||||
@@ -240,39 +240,6 @@ export default function TicketCreatePage() {
|
||||
}
|
||||
}, [sqlContent, instanceId, database]);
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
/* Pre-submit audit check */
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
const handlePreSubmitAudit = useCallback(async () => {
|
||||
if (!title.trim() || !sqlContent.trim() || !instanceId || !database || !reason.trim()) {
|
||||
setError('请填写所有必填项');
|
||||
return;
|
||||
}
|
||||
// If already audited and SQL hasn't changed, go directly to step 3
|
||||
if (auditResult) {
|
||||
setStep(3);
|
||||
return;
|
||||
}
|
||||
// Auto-audit before submission
|
||||
setAuditing(true);
|
||||
setError('');
|
||||
try {
|
||||
const result = await ticketApi.audit({
|
||||
sql_content: sqlContent,
|
||||
instance_id: Number(instanceId),
|
||||
database,
|
||||
}) as unknown as AuditResult;
|
||||
setAuditResult(result);
|
||||
setStep(3);
|
||||
} catch (e: unknown) {
|
||||
const err = e as { response?: { data?: { detail?: string } } };
|
||||
setError(err?.response?.data?.detail || '预审核失败,请检查 SQL 语法');
|
||||
} finally {
|
||||
setAuditing(false);
|
||||
}
|
||||
}, [title, sqlContent, instanceId, database, reason, auditResult]);
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
/* Submit handler */
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
@@ -198,15 +198,13 @@ export default function TicketDetailPage() {
|
||||
/* Permission checks */
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
const { isAdminOrDba, isApplicant, canApprove, canExecute, canRollback, canCancel } = useMemo(() => {
|
||||
const { canApprove, canExecute, canRollback, canCancel } = useMemo(() => {
|
||||
if (!ticket) {
|
||||
return { isAdminOrDba: false, isApplicant: false, canApprove: false, canExecute: false, canRollback: false, canCancel: false };
|
||||
return { canApprove: false, canExecute: false, canRollback: false, canCancel: false };
|
||||
}
|
||||
const adminOrDba = auth.user.role === 'admin' || auth.user.role === 'dba';
|
||||
const applicant = ticket.applicant_id === auth.user.id;
|
||||
return {
|
||||
isAdminOrDba: adminOrDba,
|
||||
isApplicant: applicant,
|
||||
canApprove: adminOrDba && (ticket.status === 'pending' || ticket.status === 'reviewing'),
|
||||
canExecute: adminOrDba && ticket.status === 'approved',
|
||||
canRollback: adminOrDba && (ticket.status === 'success' || ticket.status === 'failed'),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
Plus, Search, Eye, CheckCircle, XCircle, Play, RotateCcw,
|
||||
ChevronRight, FileCode, History, Undo2, XCircle as XIcon,
|
||||
FileCode, History, Undo2, XCircle as XIcon,
|
||||
Clock, AlertTriangle, AlertCircle, Info, ChevronLeft,
|
||||
Download, Filter, X, Loader2, BarChart3, Users, Calendar,
|
||||
Filter, X, Loader2, Users, Calendar,
|
||||
CheckSquare, Square, ArrowUpDown, FileDown, Ban,
|
||||
} from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@@ -540,6 +540,7 @@ export default function TicketsPage() {
|
||||
? <CheckSquare className="w-4 h-4 text-blue-400" />
|
||||
: <Square className="w-4 h-4 text-[var(--dg-text-muted)]" />
|
||||
}
|
||||
</button>
|
||||
</th>
|
||||
<th className="text-left px-4 py-3 font-semibold">工单号</th>
|
||||
<th className="text-left px-4 py-3 font-semibold">标题</th>
|
||||
|
||||
@@ -2,10 +2,10 @@ import { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
Plus, Search, Edit2, Trash2, XCircle, Eye, Download, Upload,
|
||||
CheckSquare, Square, X, RotateCcw, KeyRound, ChevronDown,
|
||||
UserCheck, UserX, Filter, MoreHorizontal, Lock,
|
||||
UserCheck, UserX, Filter,
|
||||
} from 'lucide-react';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { roles, users as mockUsers } from '@/lib/mock';
|
||||
import { users as mockUsers } from '@/lib/mock';
|
||||
import { userApi } from '@/api/user';
|
||||
|
||||
/* ───────── Types ───────── */
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
Plus, Edit2, Eye, ToggleLeft, ToggleRight, GitBranch, Users, CheckCircle,
|
||||
ArrowRight, Shield, Trash2, X, ChevronDown, ChevronUp,
|
||||
Save, Play, Copy, History, TestTube, AlertTriangle,
|
||||
Loader2, Check, XCircle, MessageSquare,
|
||||
Loader2, Check, XCircle,
|
||||
} from 'lucide-react';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { workflows as initialWorkflows, dbTypeColorMap } from '@/lib/mock';
|
||||
|
||||
Reference in new issue
Block a user