Initial miaojingAI project with image resolution guard
This commit is contained in:
123
src/app/api/admin/generation-jobs/route.ts
Normal file
123
src/app/api/admin/generation-jobs/route.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { requireAdmin } from '@/lib/admin-auth';
|
||||
import { getDbClient } from '@/storage/database/local-db';
|
||||
import { markStaleRunningJobs } from '@/lib/generation-job-worker';
|
||||
import { ensureGenerationJobRuntimeSchema } from '@/lib/generation-job-estimates';
|
||||
import { writePlatformLog } from '@/lib/platform-logs';
|
||||
|
||||
const STATUSES = new Set(['queued', 'running', 'succeeded', 'failed']);
|
||||
const CLEANUP_STATUSES = new Set(['failed', 'succeeded']);
|
||||
|
||||
function intParam(value: string | null, fallback: number, min: number, max: number) {
|
||||
const parsed = Number.parseInt(value || '', 10);
|
||||
if (!Number.isFinite(parsed)) return fallback;
|
||||
return Math.min(max, Math.max(min, parsed));
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const authError = await requireAdmin(request);
|
||||
if (authError) return authError;
|
||||
|
||||
await markStaleRunningJobs();
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const status = searchParams.get('status') || '';
|
||||
const userSearch = (searchParams.get('user') || searchParams.get('userSearch') || '').trim();
|
||||
const page = intParam(searchParams.get('page'), 1, 1, 100000);
|
||||
const pageSize = intParam(searchParams.get('pageSize'), 20, 1, 100);
|
||||
const offset = (page - 1) * pageSize;
|
||||
|
||||
if (status && !STATUSES.has(status)) {
|
||||
return NextResponse.json({ error: '任务状态无效' }, { status: 400 });
|
||||
}
|
||||
|
||||
const client = await getDbClient();
|
||||
try {
|
||||
await ensureGenerationJobRuntimeSchema(client);
|
||||
const whereClauses: string[] = [];
|
||||
const params: unknown[] = [];
|
||||
if (status) {
|
||||
params.push(status);
|
||||
whereClauses.push(`j.status = $${params.length}`);
|
||||
}
|
||||
if (userSearch) {
|
||||
params.push(`%${userSearch.toLowerCase()}%`);
|
||||
whereClauses.push(`(
|
||||
j.user_id::text LIKE $${params.length}
|
||||
OR LOWER(COALESCE(p.email, '')) LIKE $${params.length}
|
||||
OR LOWER(COALESCE(p.nickname, '')) LIKE $${params.length}
|
||||
)`);
|
||||
}
|
||||
const whereSql = whereClauses.length ? `WHERE ${whereClauses.join(' AND ')}` : '';
|
||||
const countResult = await client.query(
|
||||
`SELECT COUNT(*)::int AS total
|
||||
FROM generation_jobs j
|
||||
LEFT JOIN profiles p ON p.id = j.user_id
|
||||
${whereSql}`,
|
||||
params,
|
||||
);
|
||||
const rowsResult = await client.query(
|
||||
`SELECT j.id, j.user_id, p.email AS user_email, p.nickname AS user_nickname,
|
||||
j.type, j.status, j.error, j.created_at, j.started_at, j.finished_at, j.updated_at
|
||||
FROM generation_jobs j
|
||||
LEFT JOIN profiles p ON p.id = j.user_id
|
||||
${whereSql}
|
||||
ORDER BY j.created_at DESC
|
||||
LIMIT $${params.length + 1}
|
||||
OFFSET $${params.length + 2}`,
|
||||
[...params, pageSize, offset],
|
||||
);
|
||||
|
||||
const total = countResult.rows[0]?.total || 0;
|
||||
return NextResponse.json({
|
||||
jobs: rowsResult.rows,
|
||||
total,
|
||||
page,
|
||||
pageSize,
|
||||
totalPages: Math.max(1, Math.ceil(total / pageSize)),
|
||||
});
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
export async function DELETE(request: NextRequest) {
|
||||
const authError = await requireAdmin(request);
|
||||
if (authError) return authError;
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const status = searchParams.get('status') || 'failed';
|
||||
const olderThanDays = intParam(searchParams.get('olderThanDays'), 7, 0, 3650);
|
||||
|
||||
if (!CLEANUP_STATUSES.has(status)) {
|
||||
return NextResponse.json(
|
||||
{ error: '只允许清理失败或已完成任务' },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const client = await getDbClient();
|
||||
try {
|
||||
const result = await client.query(
|
||||
`DELETE FROM generation_jobs
|
||||
WHERE status = $1
|
||||
AND updated_at < NOW() - ($2::int * INTERVAL '1 day')`,
|
||||
[status, olderThanDays],
|
||||
);
|
||||
void writePlatformLog({
|
||||
type: 'admin',
|
||||
level: 'warning',
|
||||
action: 'generation_jobs_cleanup',
|
||||
message: `管理员清理了${status === 'failed' ? '失败' : '已完成'}生成任务`,
|
||||
targetType: 'generation_jobs',
|
||||
metadata: { status, olderThanDays, deleted: result.rowCount || 0 },
|
||||
request,
|
||||
});
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
deleted: result.rowCount || 0,
|
||||
});
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user