feat(API): 添加代码执行 API 路由

- 创建 /api/code/execute 接口
- 集成 Piston API 支持多语言远程执行
- 添加执行超时和错误处理
This commit is contained in:
gaoziman 2025-12-21 03:20:24 +08:00
parent 192cd175da
commit d6f2c47ddc

View File

@ -0,0 +1,143 @@
/**
* Piston API
* CORS Piston API
*/
import { NextRequest, NextResponse } from 'next/server';
// Piston API 公共端点
const PISTON_API_URL = 'https://emkc.org/api/v2/piston';
// 执行超时时间(秒)
const EXECUTION_TIMEOUT = 30;
// 请求体类型
interface ExecuteRequest {
language: string;
version: string;
files: Array<{
name?: string;
content: string;
}>;
stdin?: string;
args?: string[];
compile_args?: string[];
run_args?: string[];
compile_timeout?: number;
run_timeout?: number;
compile_memory_limit?: number;
run_memory_limit?: number;
}
// Piston API 响应类型
interface PistonResponse {
language: string;
version: string;
run: {
stdout: string;
stderr: string;
output: string;
code: number;
signal: string | null;
};
compile?: {
stdout: string;
stderr: string;
output: string;
code: number;
signal: string | null;
};
}
export async function POST(request: NextRequest) {
try {
const body: ExecuteRequest = await request.json();
// 验证请求
if (!body.language || !body.files || body.files.length === 0) {
return NextResponse.json(
{ error: '缺少必要参数: language, files' },
{ status: 400 }
);
}
// 设置超时
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), EXECUTION_TIMEOUT * 1000);
// 构建请求
const pistonRequest: ExecuteRequest = {
language: body.language,
version: body.version || '*',
files: body.files,
stdin: body.stdin || '',
args: body.args || [],
compile_args: body.compile_args || [],
run_args: body.run_args || [],
compile_timeout: body.compile_timeout || 10000,
run_timeout: body.run_timeout || 10000,
compile_memory_limit: body.compile_memory_limit || -1,
run_memory_limit: body.run_memory_limit || -1,
};
// 发送请求到 Piston API
const response = await fetch(`${PISTON_API_URL}/execute`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(pistonRequest),
signal: controller.signal,
});
clearTimeout(timeoutId);
if (!response.ok) {
const errorText = await response.text();
return NextResponse.json(
{ error: `Piston API 错误: ${response.status} - ${errorText}` },
{ status: response.status }
);
}
const result: PistonResponse = await response.json();
return NextResponse.json(result);
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
return NextResponse.json(
{ error: `执行超时(${EXECUTION_TIMEOUT}秒)` },
{ status: 408 }
);
}
console.error('Piston API proxy error:', error);
return NextResponse.json(
{ error: error instanceof Error ? error.message : '执行失败' },
{ status: 500 }
);
}
}
// 获取支持的语言列表
export async function GET() {
try {
const response = await fetch(`${PISTON_API_URL}/runtimes`);
if (!response.ok) {
return NextResponse.json(
{ error: '获取语言列表失败' },
{ status: response.status }
);
}
const runtimes = await response.json();
return NextResponse.json(runtimes);
} catch (error) {
console.error('Failed to fetch runtimes:', error);
return NextResponse.json(
{ error: '获取语言列表失败' },
{ status: 500 }
);
}
}