update
This commit is contained in:
1 parent
f5cf3f4440
commit
0fcdbc5f6c
29 files changed
+773
-264
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { createContext, useContext, useState, useCallback, useMemo, useRef, useEffect } from 'react';
|
||||
import { menuItems } from '@/constants/menu';
|
||||
import { instanceApi } from '@/api/instance';
|
||||
|
||||
const defaultUser = {
|
||||
id: 0,
|
||||
@@ -131,6 +132,8 @@ const permissionAliasMap: Record<string, string[]> = {
|
||||
settings: ['config-platform'],
|
||||
};
|
||||
|
||||
const dataChangePermissionIds = new Set(['tickets', 'workflow']);
|
||||
|
||||
export const actionPermissionMap: Record<string, UserRole[]> = {
|
||||
'ticket.create': ['admin', 'dba', 'developer'],
|
||||
'ticket.approve': ['admin', 'dba', 'developer'],
|
||||
@@ -191,6 +194,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
} catch { /* ignore */ }
|
||||
return true;
|
||||
});
|
||||
const [hasWritableDataSourceAccess, setHasWritableDataSourceAccess] = useState(false);
|
||||
|
||||
/** 权限缓存引用 */
|
||||
const permissionCacheRef = useRef<PermissionCache>({
|
||||
@@ -216,6 +220,12 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
permissionCacheRef.current.timestamp = 0;
|
||||
}, []);
|
||||
|
||||
const hasWritableAccessInUserPayload = useCallback((u: AuthUser): boolean => {
|
||||
return Boolean(u.dataSourcePermissions?.some(ds =>
|
||||
ds.actions.some(action => ['write', 'delete', 'readwrite'].includes(String(action)))
|
||||
));
|
||||
}, []);
|
||||
|
||||
/** 触发权限变更事件 */
|
||||
const emitPermissionChange = useCallback((updatedUser: AuthUser) => {
|
||||
listenersRef.current.forEach(listener => listener(updatedUser));
|
||||
@@ -239,6 +249,43 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
return () => window.removeEventListener('storage', handleStorageChange);
|
||||
}, [clearPermissionCache]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoggedIn) {
|
||||
setHasWritableDataSourceAccess(false);
|
||||
clearPermissionCache();
|
||||
return;
|
||||
}
|
||||
|
||||
if (user.role === 'admin' || user.role === 'dba') {
|
||||
setHasWritableDataSourceAccess(true);
|
||||
clearPermissionCache();
|
||||
return;
|
||||
}
|
||||
|
||||
if (user.role !== 'developer') {
|
||||
setHasWritableDataSourceAccess(false);
|
||||
clearPermissionCache();
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
instanceApi.accessible('write')
|
||||
.then((instances) => {
|
||||
if (cancelled) return;
|
||||
setHasWritableDataSourceAccess((instances || []).length > 0);
|
||||
clearPermissionCache();
|
||||
})
|
||||
.catch(() => {
|
||||
if (cancelled) return;
|
||||
setHasWritableDataSourceAccess(hasWritableAccessInUserPayload(user));
|
||||
clearPermissionCache();
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [isLoggedIn, user.id, user.role, user.permissionsUpdatedAt, hasWritableAccessInUserPayload, clearPermissionCache]);
|
||||
|
||||
const login = useCallback(async (username: string, password: string): Promise<boolean> => {
|
||||
try {
|
||||
const res = await authApi.login(username, password);
|
||||
@@ -327,6 +374,12 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
cache.menu.set(menuId, false);
|
||||
return false;
|
||||
}
|
||||
const targetMenuId = menuItem?.id || menuId;
|
||||
if (user.role === 'developer' && dataChangePermissionIds.has(targetMenuId) && !hasWritableDataSourceAccess) {
|
||||
cache.menu.set(menuId, false);
|
||||
cache.timestamp = Date.now();
|
||||
return false;
|
||||
}
|
||||
|
||||
const permissionKeys = [
|
||||
menuId,
|
||||
@@ -340,7 +393,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
cache.menu.set(menuId, result);
|
||||
cache.timestamp = Date.now();
|
||||
return result;
|
||||
}, [user.role, user.customPermissions, isLoggedIn, isCacheValid]);
|
||||
}, [user.role, user.customPermissions, hasWritableDataSourceAccess, isLoggedIn, isCacheValid]);
|
||||
|
||||
/** 操作权限检查(带缓存,支持自定义权限覆盖) */
|
||||
const hasActionPermission = useCallback((action: PermissionAction | string): boolean => {
|
||||
@@ -351,6 +404,12 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
return cache.action.get(action)!;
|
||||
}
|
||||
|
||||
if (user.role === 'developer' && ['ticket.create', 'ticket.approve'].includes(String(action)) && !hasWritableDataSourceAccess) {
|
||||
cache.action.set(action, false);
|
||||
cache.timestamp = Date.now();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 优先检查自定义权限
|
||||
if (user.customPermissions && action in user.customPermissions) {
|
||||
const customResult = user.customPermissions[action];
|
||||
@@ -369,7 +428,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
cache.action.set(action, result);
|
||||
cache.timestamp = Date.now();
|
||||
return result;
|
||||
}, [user.role, user.customPermissions, isLoggedIn, isCacheValid]);
|
||||
}, [user.role, user.customPermissions, hasWritableDataSourceAccess, isLoggedIn, isCacheValid]);
|
||||
|
||||
/** 数据源级权限检查 */
|
||||
const hasDataSourcePermission = useCallback((
|
||||
|
||||
@@ -45,7 +45,7 @@ export function useLogin(): LoginState {
|
||||
}
|
||||
if (prefs?.login_style) localStorage.setItem('login-style', prefs.login_style);
|
||||
} catch { /* ignore */ }
|
||||
navigate('/dashboard');
|
||||
navigate('/queries');
|
||||
} else {
|
||||
setError('用户名或密码错误');
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user