소스 검색

🗑️ chore(logs): remove logfile module and related components

- 删除logfile实体、服务和API路由
- 移除logfile相关的数据库配置和类型定义
- 删除权限自动日志中间件permission-auto-log.middleware.ts
- 清理客户端API中的logfileClient引用
yourname 4 달 전
부모
커밋
bb0945cc6f

+ 1 - 5
src/client/api.ts

@@ -2,7 +2,7 @@ import axios, { isAxiosError } from 'axios';
 import { hc } from 'hono/client'
 import type {
   AuthRoutes, UserRoutes, RoleRoutes,
-  AreaRoutes, ClientRoutes, ExpenseRoutes, FileRoutes, HetongRoutes, HetongRenewRoutes, LinkmanRoutes, LogfileRoutes,
+  AreaRoutes, ClientRoutes, ExpenseRoutes, FileRoutes, HetongRoutes, HetongRenewRoutes, LinkmanRoutes,
   OrderRecordRoutes, FollowUpRecordRoutes, DepartmentRoutes, UserDepartmentRoutes,
   PermissionRoutes, RolePermissionRoutes
 } from '@/server/api';
@@ -102,10 +102,6 @@ export const linkmanClient = hc<LinkmanRoutes>('/', {
   fetch: axiosFetch,
 }).api.v1.contacts;
 
-export const logfileClient = hc<LogfileRoutes>('/', {
-  fetch: axiosFetch,
-}).api.v1.logs;
-
 export const orderRecordClient = hc<OrderRecordRoutes>('/', {
   fetch: axiosFetch,
 }).api.v1['order-records'];

+ 0 - 3
src/server/api.ts

@@ -11,7 +11,6 @@ import fileRoutes from './api/files/index'
 import hetongRoutes from './api/contracts/index'
 import hetongRenewRoutes from './api/contracts-renew/index'
 import linkmanRoutes from './api/contacts/index'
-import logfileRoutes from './api/logs/index'
 import operationLogRoutes from './api/operation-logs/index'
 import orderRecordRoutes from './api/order-records/index'
 import followUpRecordRoutes from './api/follow-up-records/index'
@@ -77,7 +76,6 @@ const fileApiRoutes = api.route('/api/v1/files', fileRoutes)
 const hetongApiRoutes = api.route('/api/v1/contracts', hetongRoutes)
 const hetongRenewApiRoutes = api.route('/api/v1/contracts-renew', hetongRenewRoutes)
 const linkmanApiRoutes = api.route('/api/v1/contacts', linkmanRoutes)
-const logfileApiRoutes = api.route('/api/v1/logs', logfileRoutes)
 const operationLogApiRoutes = api.route('/api/v1/operation-logs', operationLogRoutes)
 const orderRecordApiRoutes = api.route('/api/v1/order-records', orderRecordRoutes)
 const followUpRecordApiRoutes = api.route('/api/v1/follow-up-records', followUpRecordRoutes)
@@ -97,7 +95,6 @@ export type FileRoutes = typeof fileApiRoutes
 export type HetongRoutes = typeof hetongApiRoutes
 export type HetongRenewRoutes = typeof hetongRenewApiRoutes
 export type LinkmanRoutes = typeof linkmanApiRoutes
-export type LogfileRoutes = typeof logfileApiRoutes
 export type OperationLogRoutes = typeof operationLogApiRoutes
 export type OrderRecordRoutes = typeof orderRecordApiRoutes
 export type FollowUpRecordRoutes = typeof followUpRecordApiRoutes

+ 0 - 20
src/server/api/logs/index.ts

@@ -1,20 +0,0 @@
-import { createCrudRoutes } from '@/server/utils/generic-crud.routes';
-import { Logfile } from '@/server/modules/logs/logfile.entity';
-import { LogfileSchema, CreateLogfileDto, UpdateLogfileDto } from '@/server/modules/logs/logfile.entity';
-import { authMiddleware } from '@/server/middleware/auth.middleware';
-
-const logfileRoutes = createCrudRoutes({
-  entity: Logfile,
-  createSchema: CreateLogfileDto,
-  updateSchema: UpdateLogfileDto,
-  getSchema: LogfileSchema,
-  listSchema: LogfileSchema,
-  searchFields: ['class', 'action', 'relatedId'],
-  middleware: [authMiddleware],
-  userTracking: {
-    createdByField: 'createdBy',
-    updatedByField: 'updatedBy'
-  }
-});
-
-export default logfileRoutes;

+ 1 - 2
src/server/data-source.ts

@@ -13,7 +13,6 @@ import { File } from "./modules/files/file.entity"
 import { Hetong } from "./modules/contracts/hetong.entity"
 import { HetongRenew } from "./modules/contracts/hetong-renew.entity"
 import { Linkman } from "./modules/contacts/linkman.entity"
-import { Logfile } from "./modules/logs/logfile.entity"
 import { OperationLog } from "./modules/logs/operation-log.entity"
 import { OrderRecord } from "./modules/orders/order-record.entity"
 import { FollowUpRecord } from "./modules/follow-ups/follow-up-record.entity"
@@ -29,7 +28,7 @@ export const AppDataSource = new DataSource({
   database: process.env.DB_DATABASE || "d8dai",
   entities: [
     User, Role,
-    AreaData, Client, Expense, File, Hetong, HetongRenew, Linkman, Logfile, OperationLog,
+    AreaData, Client, Expense, File, Hetong, HetongRenew, Linkman, OperationLog,
     OrderRecord, FollowUpRecord, Department, UserDepartment, Permission, RolePermission
   ],
   migrations: [],

+ 0 - 163
src/server/middleware/permission-auto-log.middleware.ts

@@ -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);

+ 0 - 133
src/server/modules/logs/logfile.entity.ts

@@ -1,133 +0,0 @@
-import { Entity, PrimaryColumn, Column, Index } from 'typeorm';
-import { z } from '@hono/zod-openapi';
-
-@Entity('logfile')
-export class Logfile {
-  @PrimaryColumn({ name: 'id', type: 'varchar', length: 50 })
-  id!: string;
-
-  @Column({ name: 'related_id', type: 'varchar', length: 50, nullable: true })
-  relatedId?: string;
-
-  @Column({ name: 'class', type: 'varchar', length: 50 })
-  class!: string;
-
-  @Column({ name: 'action', type: 'varchar', length: 50 })
-  action!: string;
-
-  @Column({ name: 'reason', type: 'text', nullable: true })
-  reason?: string;
-
-  @Column({ name: 'created_by', type: 'int', nullable: true, comment: '创建用户ID' }) //abc    
-  createdBy?: number;
-
-  @Column({ name: 'updated_by', type: 'int', nullable: true, comment: '更新用户ID' }) // ijfioejfowf
-  updatedBy?: number;
-
-  @Column({ name: 'log_time', type: 'datetime' })
-  logTime!: Date;
-
-  @Column({ name: 'created_at', type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
-  createdAt!: Date;
-
-  @Column({ 
-    name: 'updated_at', 
-    type: 'timestamp', 
-    default: () => 'CURRENT_TIMESTAMP', 
-    onUpdate: 'CURRENT_TIMESTAMP' 
-  })
-  updatedAt!: Date;
-}
-
-export const LogfileSchema = z.object({
-  id: z.string().max(50).openapi({ 
-    description: '日志记录ID',
-    example: 'LOG20230001' 
-  }),
-  relatedId: z.string().max(50).nullable().openapi({ 
-    description: '关联记录ID',
-    example: 'C2001' 
-  }),
-  class: z.string().max(50).openapi({ 
-    description: '日志类别',
-    example: 'client' 
-  }),
-  action: z.string().max(50).openapi({ 
-    description: '操作动作:如创建、更新、删除等',
-    example: 'create' 
-  }),
-  reason: z.string().nullable().openapi({ 
-    description: '操作原因',
-    example: '新建客户记录' 
-  }),
-  createdBy: z.number().int().positive().nullable().openapi({
-    description: '创建用户ID',
-    example: 1001
-  }),
-  updatedBy: z.number().int().positive().nullable().openapi({
-    description: '更新用户ID',
-    example: 1001
-  }),
-  logTime: z.date().openapi({ 
-    description: '日志记录时间',
-    example: '2023-01-15T10:30:00Z' 
-  }),
-  createdAt: z.date().openapi({ 
-    description: '创建时间',
-    example: '2023-01-15T10:30:00Z' 
-  }),
-  updatedAt: z.date().openapi({ 
-    description: '更新时间',
-    example: '2023-01-15T10:30:00Z' 
-  })
-});
-
-export const CreateLogfileDto = z.object({
-  id: z.string().max(50).openapi({ 
-    description: '日志记录ID',
-    example: 'LOG20230001' 
-  }),
-  relatedId: z.string().max(50).nullable().optional().openapi({ 
-    description: '关联记录ID',
-    example: 'C2001' 
-  }),
-  class: z.string().max(50).openapi({ 
-    description: '日志类别',
-    example: 'client' 
-  }),
-  action: z.string().max(50).openapi({ 
-    description: '操作动作:如创建、更新、删除等',
-    example: 'create' 
-  }),
-  reason: z.string().nullable().optional().openapi({ 
-    description: '操作原因',
-    example: '新建客户记录' 
-  }),
-  logTime: z.coerce.date().openapi({ 
-    description: '日志记录时间',
-    example: '2023-01-15T10:30:00Z' 
-  })
-});
-
-export const UpdateLogfileDto = z.object({
-  relatedId: z.string().max(50).nullable().optional().openapi({ 
-    description: '关联记录ID',
-    example: 'C2001' 
-  }),
-  class: z.string().max(50).optional().openapi({ 
-    description: '日志类别',
-    example: 'client' 
-  }),
-  action: z.string().max(50).optional().openapi({ 
-    description: '操作动作:如创建、更新、删除等',
-    example: 'create' 
-  }),
-  reason: z.string().nullable().optional().openapi({ 
-    description: '操作原因',
-    example: '新建客户记录' 
-  }),
-  logTime: z.coerce.date().optional().openapi({ 
-    description: '日志记录时间',
-    example: '2023-01-15T10:30:00Z' 
-  })
-});

+ 0 - 9
src/server/modules/logs/logfile.service.ts

@@ -1,9 +0,0 @@
-import { GenericCrudService } from '@/server/utils/generic-crud.service';
-import { DataSource } from 'typeorm';
-import { Logfile } from './logfile.entity';
-
-export class LogfileService extends GenericCrudService<Logfile> {
-  constructor(dataSource: DataSource) {
-    super(dataSource, Logfile);
-  }
-}