feat(auth): 能源管理模块需要 BI-LEADER-ENERGY 角色
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- 新增 ENERGY_ACCESS_ROLES 与 canAccessEnergy(roles) 守卫(全量权限角色亦可访问) - 后端 /api/energy/* 加模块级守卫:无角色返回 403 - 前端 App.tsx 按角色动态注入 EnergyModule,无权限时主导航不显示 - dev mock 用户(前端 + 后端)追加 BI-LEADER-ENERGY 便于本地调试 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
15
src/App.tsx
15
src/App.tsx
@@ -10,14 +10,17 @@ import FeedbackAdminPage from './modules/admin/FeedbackAdminPage';
|
||||
import AuthProvider from './auth/AuthProvider';
|
||||
import { useAuth } from './auth/useAuth';
|
||||
import UnauthorizedPage from './auth/UnauthorizedPage';
|
||||
import { canAccessScheduling } from './shared/auth/roles';
|
||||
import { canAccessScheduling, canAccessEnergy } from './shared/auth/roles';
|
||||
|
||||
const BASE_MODULES: ModuleConfig[] = [
|
||||
{ id: 'assets', label: '资产管理', icon: Truck, component: AssetsModule },
|
||||
{ id: 'mileage', label: '里程管理', icon: Route, component: MileageModule },
|
||||
{ id: 'energy', label: '能源管理', icon: Zap, component: EnergyModule },
|
||||
];
|
||||
|
||||
const ENERGY_MODULE: ModuleConfig = {
|
||||
id: 'energy', label: '能源管理', icon: Zap, component: EnergyModule,
|
||||
};
|
||||
|
||||
const SCHEDULING_MODULE: ModuleConfig = {
|
||||
id: 'scheduling', label: '智能调度', icon: Activity, component: SchedulingModule,
|
||||
};
|
||||
@@ -47,10 +50,10 @@ function AuthGate() {
|
||||
}, []);
|
||||
|
||||
const modules = useMemo(() => {
|
||||
if (canAccessScheduling(user?.roles)) {
|
||||
return [...BASE_MODULES, SCHEDULING_MODULE];
|
||||
}
|
||||
return BASE_MODULES;
|
||||
const result = [...BASE_MODULES];
|
||||
if (canAccessEnergy(user?.roles)) result.push(ENERGY_MODULE);
|
||||
if (canAccessScheduling(user?.roles)) result.push(SCHEDULING_MODULE);
|
||||
return result;
|
||||
}, [user?.roles]);
|
||||
|
||||
if (isLoading) {
|
||||
|
||||
@@ -46,7 +46,7 @@ export default function AuthProvider({ children }: { children: ReactNode }) {
|
||||
userName: '本地开发',
|
||||
permissionLevel: 'full',
|
||||
depName: '',
|
||||
roles: ['所有权限', 'BI-SCHEDULE-OPT', 'BI-ADMIN-FEEDBACK'],
|
||||
roles: ['所有权限', 'BI-SCHEDULE-OPT', 'BI-ADMIN-FEEDBACK', 'BI-LEADER-ENERGY'],
|
||||
},
|
||||
error: null,
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ export async function authMiddleware(c: Context, next: Next) {
|
||||
depCode: '',
|
||||
depName: '',
|
||||
permissionLevel: 'full',
|
||||
roles: ['所有权限', 'BI-SCHEDULE-OPT', 'BI-ADMIN-FEEDBACK'],
|
||||
roles: ['所有权限', 'BI-SCHEDULE-OPT', 'BI-ADMIN-FEEDBACK', 'BI-LEADER-ENERGY'],
|
||||
};
|
||||
c.set('user', devUser);
|
||||
return next();
|
||||
|
||||
@@ -29,6 +29,8 @@ export {
|
||||
DEPT_ACCESS_ROLES,
|
||||
SCHEDULING_ACCESS_ROLES,
|
||||
FEEDBACK_ADMIN_ROLES,
|
||||
ENERGY_ACCESS_ROLES,
|
||||
canAccessScheduling,
|
||||
canManageFeedback,
|
||||
canAccessEnergy,
|
||||
} from '../../shared/auth/roles.js';
|
||||
|
||||
@@ -2,9 +2,21 @@ import { Hono } from 'hono';
|
||||
import type { RowDataPacket } from 'mysql2';
|
||||
import pool from '../../db.js';
|
||||
import { cached } from './cache.js';
|
||||
import type { AuthUser } from '../../auth/types.js';
|
||||
import { canAccessEnergy } from '../../auth/types.js';
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
// 模块级访问守卫:dev 旁路 auth 时 user 为 undefined,直接放行;
|
||||
// 生产环境必须具备 BI-LEADER-ENERGY 或全量权限角色
|
||||
app.use('*', async (c, next) => {
|
||||
const user = (c as { get: (k: string) => unknown }).get('user') as AuthUser | undefined;
|
||||
if (user && !canAccessEnergy(user.roles)) {
|
||||
return c.json({ error: 'Forbidden: 能源管理访问需要 BI-LEADER-ENERGY 角色' }, 403);
|
||||
}
|
||||
return next();
|
||||
});
|
||||
|
||||
const HYDROGEN_MIN_DATE = '2024-01-01';
|
||||
|
||||
// hydrogen_time 已是 CST 字面值,直接使用即可(不再 +8 小时)
|
||||
|
||||
@@ -13,6 +13,9 @@ export const SCHEDULING_ACCESS_ROLES = ['BI-SCHEDULE-OPT'];
|
||||
/** 反馈管理(管理员)访问角色 */
|
||||
export const FEEDBACK_ADMIN_ROLES = ['BI-ADMIN-FEEDBACK'];
|
||||
|
||||
/** 能源管理模块访问角色 */
|
||||
export const ENERGY_ACCESS_ROLES = ['BI-LEADER-ENERGY'];
|
||||
|
||||
/** 用户是否可访问智能调度模块。仅 BI-SCHEDULE-OPT 角色允许访问。 */
|
||||
export function canAccessScheduling(roles: readonly string[] | null | undefined): boolean {
|
||||
if (!roles || roles.length === 0) return false;
|
||||
@@ -24,3 +27,9 @@ export function canManageFeedback(roles: readonly string[] | null | undefined):
|
||||
if (!roles || roles.length === 0) return false;
|
||||
return roles.some(r => FEEDBACK_ADMIN_ROLES.includes(r) || FULL_ACCESS_ROLES.includes(r));
|
||||
}
|
||||
|
||||
/** 用户是否可访问能源管理模块。BI-LEADER-ENERGY 或全量权限角色可访问。 */
|
||||
export function canAccessEnergy(roles: readonly string[] | null | undefined): boolean {
|
||||
if (!roles || roles.length === 0) return false;
|
||||
return roles.some(r => ENERGY_ACCESS_ROLES.includes(r) || FULL_ACCESS_ROLES.includes(r));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user