|
|
@@ -1,163 +0,0 @@
|
|
|
-import { Context, Next } from 'hono';
|
|
|
-import { checkPermission } from './permission.middleware';
|
|
|
-import { LogfileService } from '@/server/modules/logs/logfile.service';
|
|
|
-import { AppDataSource } from '@/server/data-source';
|
|
|
-
|
|
|
-/**
|
|
|
- * 增强版权限日志中间件
|
|
|
- * 自动从权限字符串和URL路径提取资源类型
|
|
|
- */
|
|
|
-export function permissionWithAutoLog(requiredPermissions: string[]) {
|
|
|
- const logService = new LogfileService(AppDataSource);
|
|
|
- const permissionChecker = checkPermission(requiredPermissions);
|
|
|
-
|
|
|
- return async (c: Context, next: Next) => {
|
|
|
- const user = c.get('user');
|
|
|
- const method = c.req.method;
|
|
|
- const url = new URL(c.req.url);
|
|
|
- const path = url.pathname;
|
|
|
- const params = c.req.param();
|
|
|
-
|
|
|
- // 自动检测资源类型
|
|
|
- const resourceClass = detectResourceType(requiredPermissions, path);
|
|
|
-
|
|
|
- // 获取相关ID
|
|
|
- const relatedId = params.id || null;
|
|
|
-
|
|
|
- // 记录操作开始时间
|
|
|
- const startTime = Date.now();
|
|
|
-
|
|
|
- // 执行权限检查
|
|
|
- const hasPermission = await permissionChecker(user);
|
|
|
-
|
|
|
- if (!hasPermission) {
|
|
|
- // 记录权限拒绝
|
|
|
- await logService.create({
|
|
|
- id: generateLogId(),
|
|
|
- class: resourceClass,
|
|
|
- action: 'permission_denied',
|
|
|
- relatedId: relatedId ? String(relatedId) : undefined,
|
|
|
- reason: `[${method} ${path}] 权限被拒绝: 需要权限 ${requiredPermissions.join(', ')}`,
|
|
|
- logTime: new Date()
|
|
|
- }, user?.id);
|
|
|
-
|
|
|
- return c.json({ message: '没有权限访问该资源', code: 403 }, 403);
|
|
|
- }
|
|
|
-
|
|
|
- // 记录操作开始
|
|
|
- await logService.create({
|
|
|
- id: generateLogId(),
|
|
|
- class: resourceClass,
|
|
|
- action: 'operation_started',
|
|
|
- relatedId: relatedId ? String(relatedId) : undefined,
|
|
|
- reason: `[${method} ${path}] 开始执行操作,权限: ${requiredPermissions.join(', ')}`,
|
|
|
- logTime: new Date()
|
|
|
- }, user?.id);
|
|
|
-
|
|
|
- try {
|
|
|
- // 继续执行后续处理
|
|
|
- await next();
|
|
|
-
|
|
|
- // 计算执行时间
|
|
|
- const duration = Date.now() - startTime;
|
|
|
-
|
|
|
- // 记录操作成功
|
|
|
- await logService.create({
|
|
|
- id: generateLogId(),
|
|
|
- class: resourceClass,
|
|
|
- action: 'operation_success',
|
|
|
- relatedId: relatedId ? String(relatedId) : undefined,
|
|
|
- reason: `[${method} ${path}] 操作成功完成,耗时: ${duration}ms`,
|
|
|
- logTime: new Date()
|
|
|
- }, user?.id);
|
|
|
-
|
|
|
- } catch (error) {
|
|
|
- // 计算执行时间
|
|
|
- const duration = Date.now() - startTime;
|
|
|
-
|
|
|
- // 记录操作失败
|
|
|
- await logService.create({
|
|
|
- id: generateLogId(),
|
|
|
- class: resourceClass,
|
|
|
- action: 'operation_failed',
|
|
|
- relatedId: relatedId ? String(relatedId) : undefined,
|
|
|
- reason: `[${method} ${path}] 操作执行失败: ${error instanceof Error ? error.message : '未知错误'}`,
|
|
|
- logTime: new Date()
|
|
|
- }, user?.id);
|
|
|
-
|
|
|
- throw error;
|
|
|
- }
|
|
|
- };
|
|
|
-}
|
|
|
-
|
|
|
-// 智能资源类型检测
|
|
|
-function detectResourceType(permissions: string[], path: string): string {
|
|
|
- if (!permissions || permissions.length === 0) {
|
|
|
- return extractFromPath(path);
|
|
|
- }
|
|
|
-
|
|
|
- // 从权限字符串提取
|
|
|
- const permission = permissions[0];
|
|
|
- const resource = extractFromPermission(permission);
|
|
|
- if (resource !== 'unknown') {
|
|
|
- return resource;
|
|
|
- }
|
|
|
-
|
|
|
- // 从路径提取
|
|
|
- return extractFromPath(path);
|
|
|
-}
|
|
|
-
|
|
|
-// 从权限字符串提取资源类型
|
|
|
-function extractFromPermission(permission: string): string {
|
|
|
- const parts = permission.split(':');
|
|
|
-
|
|
|
- if (parts.length >= 2) {
|
|
|
- // 处理 system:user:create 格式
|
|
|
- if (parts[0] === 'system' && parts[1]) {
|
|
|
- return parts[1];
|
|
|
- }
|
|
|
- // 处理 client:view 或 client:view:all 格式
|
|
|
- return parts[0];
|
|
|
- }
|
|
|
-
|
|
|
- return parts[0] || 'unknown';
|
|
|
-}
|
|
|
-
|
|
|
-// 从URL路径提取资源类型
|
|
|
-function extractFromPath(path: string): string {
|
|
|
- // 匹配 /api/v1/users 或 /api/v1/users/123
|
|
|
- const match = path.match(/\/api\/v\d+\/([^/]+)/);
|
|
|
- if (match && match[1]) {
|
|
|
- let resource = match[1];
|
|
|
- // 转换为单数形式
|
|
|
- resource = resource.replace(/s$/, '');
|
|
|
- return resource;
|
|
|
- }
|
|
|
-
|
|
|
- // 匹配 /api/v1/contract-renews -> contract_renew
|
|
|
- const hyphenMatch = path.match(/\/api\/v\d+\/([^/]+)/);
|
|
|
- if (hyphenMatch && hyphenMatch[1]) {
|
|
|
- return hyphenMatch[1].replace(/-/g, '_');
|
|
|
- }
|
|
|
-
|
|
|
- return 'unknown';
|
|
|
-}
|
|
|
-
|
|
|
-// 生成唯一日志ID
|
|
|
-function generateLogId(): string {
|
|
|
- const timestamp = Date.now();
|
|
|
- const random = Math.random().toString(36).substring(2, 8);
|
|
|
- return `LOG${timestamp}${random}`;
|
|
|
-}
|
|
|
-
|
|
|
-// 简化使用方式 - 直接导入使用
|
|
|
-export const permissionLog = permissionWithAutoLog;
|
|
|
-
|
|
|
-// 向后兼容的简化版本
|
|
|
-export function permissionWithLog(requiredPermissions: string[]) {
|
|
|
- return permissionWithAutoLog(requiredPermissions);
|
|
|
-}
|
|
|
-
|
|
|
-// 使用示例:
|
|
|
-// app.get('/api/users', authMiddleware, permissionWithLog(['system:user:view']), handler);
|
|
|
-// app.post('/api/clients', authMiddleware, permissionWithLog(['client:create']), handler);
|