Files
foka-ci/apps/server/middlewares/exception.ts
hurole ef473d6084 feat: 完善项目架构和功能
- 修复路由配置,实现根路径自动重定向到/project
- 新增Gitea OAuth认证系统和相关组件
- 完善日志系统实现,包含pino日志工具和中间件
- 重构页面结构,分离项目管理和环境管理页面
- 新增CORS、Session等关键中间件
- 优化前端请求封装和类型定义
- 修复TypeScript类型错误和参数传递问题
2025-09-04 23:19:52 +08:00

152 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type Koa from 'koa';
import type { Middleware } from './types.ts';
import { log } from '../libs/logger.ts';
/**
* 统一响应体结构
*/
export interface ApiResponse<T = any> {
code: number; // 状态码0表示成功其他表示失败
message: string; // 响应消息
data?: T; // 响应数据
timestamp: number; // 时间戳
}
/**
* 自定义业务异常类
*/
export class BusinessError extends Error {
public code: number;
public httpStatus: number;
constructor(message: string, code = 1000, httpStatus = 400) {
super(message);
this.name = 'BusinessError';
this.code = code;
this.httpStatus = httpStatus;
}
}
/**
* 全局异常处理中间件
*/
export class Exception implements Middleware {
apply(app: Koa): void {
app.use(async (ctx, next) => {
try {
await next();
// 如果没有设置响应体则返回404
if (ctx.status === 404) {
this.sendResponse(ctx, 404, 'Not Found', null, 404);
}
} catch (error) {
log.error('Exception', 'catch error: %o', error);
this.handleError(ctx, error);
}
});
}
/**
* 处理错误
*/
private handleError(ctx: Koa.Context, error: any): void {
if (error instanceof BusinessError) {
// 业务异常
this.sendResponse(ctx, error.code, error.message, null, error.httpStatus);
} else if (error.status) {
// Koa HTTP 错误
const message =
error.status === 401
? '未授权访问'
: error.status === 403
? '禁止访问'
: error.status === 404
? '资源不存在'
: error.status === 422
? '请求参数错误'
: error.message || '请求失败';
this.sendResponse(ctx, error.status, message, null, error.status);
} else {
// 系统异常
const isDev = process.env.NODE_ENV === 'development';
const message = isDev ? error.message : '服务器内部错误';
const data = isDev ? { stack: error.stack } : null;
this.sendResponse(ctx, 500, message, data, 500);
}
}
/**
* 发送统一响应
*/
private sendResponse(
ctx: Koa.Context,
code: number,
message: string,
data: any = null,
httpStatus = 200,
): void {
const response: ApiResponse = {
code,
message,
data,
timestamp: Date.now(),
};
ctx.status = httpStatus;
ctx.body = response;
ctx.type = 'application/json';
}
}
/**
* 创建成功响应的辅助函数
*/
export function createSuccessResponse<T>(
data: T,
message = '操作成功',
): ApiResponse<T> {
return {
code: 0,
message,
data,
timestamp: Date.now(),
};
}
/**
* 创建成功响应的辅助函数(自动设置消息)
*/
export function createAutoSuccessResponse<T>(data: T): ApiResponse<T> {
let message = '操作成功';
// 根据数据类型自动生成消息
if (Array.isArray(data)) {
message = `获取列表成功,共${data.length}条记录`;
} else if (data && typeof data === 'object') {
message = '获取数据成功';
} else if (data === null || data === undefined) {
message = '操作完成';
}
return createSuccessResponse(data, message);
}
/**
* 创建失败响应的辅助函数
*/
export function createErrorResponse(
code: number,
message: string,
data?: any,
): ApiResponse {
return {
code,
message,
data,
timestamp: Date.now(),
};
}