feat(API): 添加代码执行 API 路由
- 创建 /api/code/execute 接口 - 集成 Piston API 支持多语言远程执行 - 添加执行超时和错误处理
This commit is contained in:
parent
192cd175da
commit
d6f2c47ddc
143
src/app/api/code/execute/route.ts
Normal file
143
src/app/api/code/execute/route.ts
Normal 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 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user