gitdataai/admin/src/app/api/platform/repos/route.ts
ZhenYi fb91f5a6c5 feat(admin): add admin panel with billing alerts and model sync
- Add libs/api/admin with admin API endpoints:
  sync models, workspace credit, billing alert check
- Add workspace_alert_config model and alert service
- Add Session::no_op() for background tasks without user context
- Add admin/ Next.js admin panel (AI models, billing, workspaces, audit)
- Start billing alert background task every 30 minutes
2026-04-19 20:48:59 +08:00

97 lines
3.1 KiB
TypeScript

import { NextRequest, NextResponse } from "next/server";
import { query } from "@/lib/db";
export const runtime = "nodejs";
export async function GET(req: NextRequest) {
try {
const { searchParams } = req.nextUrl;
const page = Math.max(1, parseInt(searchParams.get("page") || "1", 10));
const pageSize = Math.max(1, parseInt(searchParams.get("pageSize") || "30", 10));
const projectId = searchParams.get("projectId") || "";
const search = searchParams.get("search") || "";
const offset = (page - 1) * pageSize;
const conditions: string[] = ["1=1"];
const params: (string | number)[] = [];
let paramIdx = 1;
if (projectId) {
conditions.push(`r.project = $${paramIdx++}`);
params.push(projectId);
}
if (search) {
conditions.push(`r.repo_name ILIKE $${paramIdx++}`);
params.push(`%${search}%`);
}
const whereCond = conditions.join(" AND ");
const limitIdx = paramIdx++;
const offsetIdx = paramIdx++;
const [reposResult, countResult] = await Promise.all([
query<{
id: string;
repo_name: string;
project_id: string;
project_name: string;
workspace_name: string;
default_branch: string;
is_private: boolean;
created_by: string;
created_at: Date;
ai_code_review_enabled: boolean;
collaborator_count: string;
branch_count: string;
}>(
`SELECT
r.id, r.repo_name, r.project as project_id,
p.name as project_name,
COALESCE(w.name, '') as workspace_name,
r.default_branch, r.is_private,
r.created_by::text as created_by,
r.created_at::text as created_at,
r.ai_code_review_enabled,
(SELECT COUNT(*) FROM repo_collaborator WHERE repo = r.id)::text as collaborator_count,
(SELECT COUNT(*) FROM repo_branch WHERE repo = r.id)::text as branch_count
FROM repo r
JOIN project p ON p.id = r.project
LEFT JOIN workspace w ON w.id = p.workspace_id
WHERE ${whereCond}
ORDER BY r.created_at DESC
LIMIT $${limitIdx} OFFSET $${offsetIdx}`,
[...params, pageSize, offset]
),
query<{ count: string }>(
`SELECT COUNT(*)::text as count
FROM repo r
JOIN project p ON p.id = r.project
WHERE ${whereCond}`,
params
),
]);
const repos = reposResult.rows.map((r) => ({
id: r.id,
repoName: r.repo_name,
projectId: r.project_id,
projectName: r.project_name,
workspaceName: r.workspace_name,
defaultBranch: r.default_branch,
isPrivate: r.is_private,
createdBy: r.created_by,
createdAt: String(r.created_at),
aiCodeReviewEnabled: r.ai_code_review_enabled,
collaboratorCount: parseInt(r.collaborator_count || "0", 10),
branchCount: parseInt(r.branch_count || "0", 10),
}));
const total = parseInt(countResult.rows[0]?.count || "0", 10);
return NextResponse.json({ repos, total, page, pageSize });
} catch (e) {
console.error("Repos error:", e);
return NextResponse.json({ error: "服务器错误" }, { status: 500 });
}
}