Initial commit: SQL audit platform v0.1.0
FastAPI backend with auth, user, instance, ticket, query, dashboard APIs. React + TypeScript frontend with mock data UI (10 pages). SQLite + async SQLAlchemy, JWT auth, role-based access control. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
commit
439155c92e
290 files changed
+61950
No files matched your search
@@ -0,0 +1,164 @@
|
||||
import { createContext, useContext, useState, useCallback, useMemo } from 'react';
|
||||
import { menuItems, currentUser as defaultUser } from '@/lib/mock';
|
||||
import { authApi } from '@/api/auth';
|
||||
|
||||
export type UserRole = 'admin' | 'dba' | 'developer' | 'guest';
|
||||
|
||||
export interface AuthUser {
|
||||
id: number;
|
||||
username: string;
|
||||
email: string;
|
||||
role: UserRole;
|
||||
roleName: string;
|
||||
avatar: string;
|
||||
}
|
||||
|
||||
interface AuthContextType {
|
||||
user: AuthUser;
|
||||
isLoggedIn: boolean;
|
||||
login: (username: string, password: string) => Promise<boolean>;
|
||||
logout: () => void;
|
||||
switchRole: (role: UserRole) => void;
|
||||
hasPermission: (menuId: string) => boolean;
|
||||
visibleMenuItems: typeof menuItems;
|
||||
isAdmin: boolean;
|
||||
isDBA: boolean;
|
||||
isDeveloper: boolean;
|
||||
isGuest: boolean;
|
||||
}
|
||||
|
||||
const roleNameMap: Record<UserRole, string> = {
|
||||
admin: '超级管理员',
|
||||
dba: '数据库管理员',
|
||||
developer: '开发人员',
|
||||
guest: '访客',
|
||||
};
|
||||
|
||||
const routePermissionMap: Record<string, UserRole[]> = {
|
||||
dashboard: ['admin', 'dba', 'developer', 'guest'],
|
||||
mysql: ['admin', 'dba'],
|
||||
postgres: ['admin', 'dba'],
|
||||
redis: ['admin', 'dba'],
|
||||
mongo: ['admin', 'dba'],
|
||||
tickets: ['admin', 'dba', 'developer'],
|
||||
queries: ['admin', 'dba', 'developer', 'guest'],
|
||||
roles: ['admin'],
|
||||
users: ['admin'],
|
||||
'data-permission': ['admin', 'dba'],
|
||||
'audit-rules': ['admin', 'dba'],
|
||||
workflow: ['admin', 'dba'],
|
||||
'auto-task': ['admin'],
|
||||
announcement: ['admin'],
|
||||
'config-platform': ['admin'],
|
||||
notifications: ['admin'],
|
||||
};
|
||||
|
||||
export const actionPermissionMap: Record<string, UserRole[]> = {
|
||||
'ticket.create': ['admin', 'dba', 'developer'],
|
||||
'ticket.approve': ['admin', 'dba'],
|
||||
'ticket.execute': ['admin', 'dba'],
|
||||
'ticket.rollback': ['admin', 'dba'],
|
||||
'ticket.delete': ['admin'],
|
||||
'query.create': ['admin', 'dba', 'developer'],
|
||||
'query.export': ['admin', 'dba', 'developer'],
|
||||
'instance.add': ['admin'],
|
||||
'instance.edit': ['admin', 'dba'],
|
||||
'instance.delete': ['admin'],
|
||||
'user.manage': ['admin'],
|
||||
'role.manage': ['admin'],
|
||||
'rule.manage': ['admin', 'dba'],
|
||||
'workflow.manage': ['admin', 'dba'],
|
||||
'config.manage': ['admin'],
|
||||
'announcement.manage': ['admin'],
|
||||
'auto-task.manage': ['admin'],
|
||||
};
|
||||
|
||||
const AuthContext = createContext<AuthContextType | null>(null);
|
||||
|
||||
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
const [user, setUser] = useState<AuthUser>({
|
||||
id: defaultUser.id,
|
||||
username: defaultUser.username,
|
||||
email: defaultUser.email,
|
||||
role: defaultUser.role,
|
||||
roleName: defaultUser.roleName,
|
||||
avatar: defaultUser.avatar,
|
||||
});
|
||||
const [isLoggedIn, setIsLoggedIn] = useState(() => !!localStorage.getItem('token'));
|
||||
|
||||
const login = useCallback(async (username: string, password: string): Promise<boolean> => {
|
||||
try {
|
||||
const res = await authApi.login(username, password);
|
||||
localStorage.setItem('token', res.access_token);
|
||||
const u = res.user;
|
||||
const role = u.role as UserRole;
|
||||
setUser({
|
||||
id: u.id,
|
||||
username: u.username,
|
||||
email: u.email || '',
|
||||
role,
|
||||
roleName: roleNameMap[role] || role,
|
||||
avatar: u.display_name?.charAt(0) || username.charAt(0).toUpperCase(),
|
||||
});
|
||||
setIsLoggedIn(true);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const logout = useCallback(() => {
|
||||
localStorage.removeItem('token');
|
||||
setIsLoggedIn(false);
|
||||
}, []);
|
||||
|
||||
const switchRole = useCallback((role: UserRole) => {
|
||||
setUser(prev => ({
|
||||
...prev,
|
||||
role,
|
||||
roleName: roleNameMap[role],
|
||||
username: role === 'admin' ? 'admin' : role === 'dba' ? 'zhangsan' : role === 'developer' ? 'wangwu' : 'visitor',
|
||||
email: `${role}@company.com`,
|
||||
avatar: role.charAt(0).toUpperCase(),
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const hasPermission = useCallback((menuId: string): boolean => {
|
||||
const allowedRoles = routePermissionMap[menuId];
|
||||
if (!allowedRoles) return false;
|
||||
return allowedRoles.includes(user.role);
|
||||
}, [user.role]);
|
||||
|
||||
const visibleMenuItems = useMemo(() => {
|
||||
return menuItems.filter(item => item.roles.includes(user.role));
|
||||
}, [user.role]);
|
||||
|
||||
const value = useMemo(() => ({
|
||||
user,
|
||||
isLoggedIn,
|
||||
login,
|
||||
logout,
|
||||
switchRole,
|
||||
hasPermission,
|
||||
visibleMenuItems,
|
||||
isAdmin: user.role === 'admin',
|
||||
isDBA: user.role === 'dba',
|
||||
isDeveloper: user.role === 'developer',
|
||||
isGuest: user.role === 'guest',
|
||||
}), [user, isLoggedIn, login, logout, switchRole, hasPermission, visibleMenuItems]);
|
||||
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||||
}
|
||||
|
||||
export function useAuth(): AuthContextType {
|
||||
const ctx = useContext(AuthContext);
|
||||
if (!ctx) throw new Error('useAuth must be used within AuthProvider');
|
||||
return ctx;
|
||||
}
|
||||
|
||||
export function useActionPermission(action: string): boolean {
|
||||
const { user } = useAuth();
|
||||
const allowedRoles = actionPermissionMap[action];
|
||||
if (!allowedRoles) return false;
|
||||
return allowedRoles.includes(user.role);
|
||||
}
|
||||
Reference in new issue
Block a user