first commit
Some checks failed
Some checks failed
This commit is contained in:
89
app/api/alipan-tv-token/check_status/[sid]/route.ts
Normal file
89
app/api/alipan-tv-token/check_status/[sid]/route.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { decrypt, getParams } from '@/utils/decode'
|
||||
|
||||
interface QrCodeStatus {
|
||||
status: 'WaitLogin' | 'LoginSuccess' | 'QRCodeExpired' | 'ScanSuccess' | 'LoginFailed'
|
||||
authCode?: string
|
||||
}
|
||||
|
||||
interface TokenResponseEncrypt {
|
||||
data: {
|
||||
ciphertext: string
|
||||
iv: string
|
||||
}
|
||||
}
|
||||
|
||||
interface TokenRequest {
|
||||
akv: string
|
||||
apv: string
|
||||
b: string
|
||||
d: string
|
||||
m: string
|
||||
mac: string
|
||||
n: string
|
||||
t: number
|
||||
wifiMac: string
|
||||
code?: string
|
||||
'Content-Type': string
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
request: Request,
|
||||
{ params }: { params: Promise<{ sid: string }> }
|
||||
) {
|
||||
try {
|
||||
const { sid } = await params
|
||||
|
||||
const statusResponse = await fetch(`https://openapi.alipan.com/oauth/qrcode/${sid}/status`)
|
||||
|
||||
if (!statusResponse.ok) {
|
||||
throw new Error('Failed to check status')
|
||||
}
|
||||
|
||||
const statusData: QrCodeStatus = await statusResponse.json()
|
||||
|
||||
if (statusData.status === 'LoginSuccess' && statusData.authCode) {
|
||||
try {
|
||||
const t = Math.floor(Date.now() / 1000)
|
||||
const sendData: TokenRequest = {
|
||||
...getParams(t),
|
||||
code: statusData.authCode,
|
||||
"Content-Type": "application/json"
|
||||
} as TokenRequest
|
||||
|
||||
const headers = Object.fromEntries(
|
||||
Object.entries(sendData).map(([k, v]) => [k, String(v)])
|
||||
)
|
||||
|
||||
const tokenResponse = await fetch('https://api.extscreen.com/aliyundrive/v3/token', {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
body: JSON.stringify(sendData)
|
||||
})
|
||||
|
||||
if (!tokenResponse.ok) {
|
||||
throw new Error('Failed to get token')
|
||||
}
|
||||
|
||||
const tokenResult: TokenResponseEncrypt = await tokenResponse.json()
|
||||
const plainData = decrypt(tokenResult.data.ciphertext, tokenResult.data.iv, t)
|
||||
const tokenInfo = JSON.parse(plainData)
|
||||
|
||||
return Response.json({
|
||||
status: 'LoginSuccess',
|
||||
refresh_token: tokenInfo.refresh_token,
|
||||
access_token: tokenInfo.access_token
|
||||
})
|
||||
|
||||
} catch {
|
||||
return Response.json({ status: 'LoginFailed' })
|
||||
}
|
||||
}
|
||||
|
||||
return Response.json(statusData)
|
||||
} catch (error: any) {
|
||||
return Response.json(
|
||||
{ error: error.message },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
38
app/api/alipan-tv-token/generate_qr/route.ts
Normal file
38
app/api/alipan-tv-token/generate_qr/route.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
interface ApiResponse<T> {
|
||||
data: T
|
||||
}
|
||||
|
||||
interface QrCodeData {
|
||||
qrCodeUrl: string
|
||||
sid: string
|
||||
}
|
||||
|
||||
export async function POST() {
|
||||
try {
|
||||
const response = await fetch('https://api.extscreen.com/aliyundrive/qrcode', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
scopes: ["user:base", "file:all:read", "file:all:write"].join(','),
|
||||
width: 500,
|
||||
height: 500,
|
||||
})
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to generate QR code')
|
||||
}
|
||||
|
||||
const result: ApiResponse<QrCodeData> = await response.json()
|
||||
|
||||
return Response.json({
|
||||
qr_link: result.data.qrCodeUrl,
|
||||
sid: result.data.sid
|
||||
})
|
||||
} catch (error: any) {
|
||||
return Response.json(
|
||||
{ error: error.message },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
94
app/api/oauth/alipan/token/route.ts
Normal file
94
app/api/oauth/alipan/token/route.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { decrypt, getParams } from '@/utils/decode'
|
||||
|
||||
interface TokenResponseEncrypt {
|
||||
data: {
|
||||
ciphertext: string
|
||||
iv: string
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshToken(refreshTokenValue: string) {
|
||||
const t = Math.floor(Date.now() / 1000)
|
||||
|
||||
const sendData = {
|
||||
...getParams(t),
|
||||
refresh_token: refreshTokenValue,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
const headers = Object.fromEntries(
|
||||
Object.entries(sendData).map(([k, v]) => [k, String(v)])
|
||||
)
|
||||
|
||||
const tokenResponse = await fetch('https://api.extscreen.com/aliyundrive/v3/token', {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
body: JSON.stringify(sendData)
|
||||
})
|
||||
|
||||
if (!tokenResponse.ok) {
|
||||
throw new Error('Failed to refresh token')
|
||||
}
|
||||
|
||||
const tokenData: TokenResponseEncrypt = await tokenResponse.json()
|
||||
const plainData = decrypt(tokenData.data.ciphertext, tokenData.data.iv, t)
|
||||
const tokenInfo = JSON.parse(plainData)
|
||||
|
||||
return tokenInfo
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const { refresh_token } = await request.json()
|
||||
const tokenInfo = await refreshToken(refresh_token)
|
||||
|
||||
return Response.json({
|
||||
token_type: 'Bearer',
|
||||
access_token: tokenInfo.access_token,
|
||||
refresh_token: tokenInfo.refresh_token,
|
||||
expires_in: tokenInfo.expires_in
|
||||
})
|
||||
|
||||
} catch (error: any) {
|
||||
return Response.json(
|
||||
{
|
||||
code: 500,
|
||||
message: error.message,
|
||||
data: null
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const refresh_ui = searchParams.get('refresh_ui')
|
||||
|
||||
|
||||
|
||||
if (!refresh_ui) {
|
||||
return Response.json({
|
||||
refresh_token: '',
|
||||
access_token: '',
|
||||
text: 'refresh_ui parameter is required'
|
||||
})
|
||||
}
|
||||
|
||||
const tokenInfo = await refreshToken(refresh_ui)
|
||||
|
||||
return Response.json({
|
||||
refresh_token: tokenInfo.refresh_token,
|
||||
access_token: tokenInfo.access_token,
|
||||
text: ''
|
||||
})
|
||||
|
||||
} catch (error: any) {
|
||||
return Response.json({
|
||||
refresh_token: '',
|
||||
access_token: '',
|
||||
text: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
42
app/api/testcase-generator/download/[taskId]/route.ts
Normal file
42
app/api/testcase-generator/download/[taskId]/route.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { NextRequest } from "next/server";
|
||||
import { getTask, getZipPathForTask } from "@/lib/testcase-backend";
|
||||
import fs from "fs/promises";
|
||||
|
||||
export async function GET(
|
||||
_req: NextRequest,
|
||||
{ params }: { params: Promise<{ taskId: string }> }
|
||||
) {
|
||||
const { taskId } = await params;
|
||||
if (!taskId) {
|
||||
return Response.json({ error: "缺少 taskId" }, { status: 400 });
|
||||
}
|
||||
|
||||
const task = getTask(taskId);
|
||||
if (!task) {
|
||||
return Response.json({ error: "任务未找到" }, { status: 404 });
|
||||
}
|
||||
|
||||
if (task.status !== "completed") {
|
||||
return Response.json({ error: "任务尚未完成" }, { status: 400 });
|
||||
}
|
||||
|
||||
const zipPath = getZipPathForTask(taskId);
|
||||
if (!zipPath) {
|
||||
return Response.json({ error: "文件未找到" }, { status: 404 });
|
||||
}
|
||||
|
||||
try {
|
||||
const buffer = await fs.readFile(zipPath);
|
||||
const filename = `${task.englishName}_testcases.zip`;
|
||||
return new Response(buffer, {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Content-Type": "application/zip",
|
||||
"Content-Disposition": `attachment; filename="${encodeURIComponent(filename)}"`,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("[testcase] download read error:", e);
|
||||
return Response.json({ error: "文件读取失败" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
75
app/api/testcase-generator/generate/route.ts
Normal file
75
app/api/testcase-generator/generate/route.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { NextRequest } from "next/server";
|
||||
import {
|
||||
createTask,
|
||||
getEstimatedTime,
|
||||
findRunningTaskByEnglishName,
|
||||
processTask,
|
||||
} from "@/lib/testcase-backend";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
const body = await req.json();
|
||||
const {
|
||||
title,
|
||||
englishName,
|
||||
description,
|
||||
standardCode,
|
||||
serviceLevel = "standard",
|
||||
testCaseCount: rawCount,
|
||||
} = body;
|
||||
|
||||
if (!title || !englishName || !description || !standardCode) {
|
||||
return Response.json(
|
||||
{ success: false, error: "请填写必填字段:题目标题、题目英文名、题目描述、C++标准程序" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
if (!/^[a-z][a-z0-9_]*$/.test(String(englishName).trim())) {
|
||||
return Response.json(
|
||||
{ success: false, error: "题目英文名格式错误:只能包含小写字母、数字和下划线,必须以字母开头" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const testCaseCount = Math.min(100, Math.max(1, parseInt(String(rawCount), 10) || 10));
|
||||
|
||||
const existing = findRunningTaskByEnglishName(String(englishName));
|
||||
if (existing) {
|
||||
return Response.json(
|
||||
{ success: false, error: "相同英文名的任务正在处理中,请稍后再试或使用不同的英文名" },
|
||||
{ status: 409 }
|
||||
);
|
||||
}
|
||||
|
||||
const taskId = "task_" + Date.now() + "_" + Math.random().toString(36).slice(2, 11);
|
||||
createTask(taskId, {
|
||||
title: String(title).trim(),
|
||||
englishName: String(englishName).trim().toLowerCase(),
|
||||
description: String(description).trim(),
|
||||
standardCode: String(standardCode).trim(),
|
||||
testCaseCount,
|
||||
serviceLevel: String(serviceLevel),
|
||||
});
|
||||
|
||||
setImmediate(() => {
|
||||
processTask(taskId).catch((err) => {
|
||||
console.error(`[testcase] processTask error:`, err);
|
||||
});
|
||||
});
|
||||
|
||||
const estimated_time = getEstimatedTime(String(serviceLevel), testCaseCount);
|
||||
return Response.json({
|
||||
success: true,
|
||||
task_id: taskId,
|
||||
message: "任务已创建,正在处理中...",
|
||||
estimated_time,
|
||||
});
|
||||
} catch (e) {
|
||||
const message = e instanceof Error ? e.message : String(e);
|
||||
return Response.json(
|
||||
{ success: false, error: "创建任务时发生错误,请重试:" + message },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
37
app/api/testcase-generator/task_status/[taskId]/route.ts
Normal file
37
app/api/testcase-generator/task_status/[taskId]/route.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { NextRequest } from "next/server";
|
||||
import { getTask, getEstimatedTime } from "@/lib/testcase-backend";
|
||||
|
||||
export async function GET(
|
||||
_req: NextRequest,
|
||||
{ params }: { params: Promise<{ taskId: string }> }
|
||||
) {
|
||||
const { taskId } = await params;
|
||||
if (!taskId) {
|
||||
return Response.json({ error: "缺少 taskId" }, { status: 400 });
|
||||
}
|
||||
|
||||
const task = getTask(taskId);
|
||||
if (!task) {
|
||||
return Response.json({ error: "任务未找到" }, { status: 404 });
|
||||
}
|
||||
|
||||
const runningTime = Math.floor((Date.now() - task.createdAt.getTime()) / 1000);
|
||||
const estimated_time = getEstimatedTime(task.serviceLevel, task.testCaseCount);
|
||||
|
||||
return Response.json({
|
||||
task_id: task.id,
|
||||
status: task.status,
|
||||
progress: task.progress,
|
||||
title: task.title,
|
||||
english_name: task.englishName,
|
||||
test_case_count: task.testCaseCount,
|
||||
generated_cases: task.generatedCases ?? 0,
|
||||
service_level: task.serviceLevel,
|
||||
created_at: task.createdAt,
|
||||
updated_at: task.updatedAt,
|
||||
running_time: runningTime,
|
||||
estimated_time,
|
||||
error_message: task.errorMessage ?? undefined,
|
||||
currentMessage: task.currentMessage ?? undefined,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user