import { NextRequest, NextResponse } from "next/server"; import { z } from "zod"; import { auth } from "@/app/(auth)/auth"; import { ChatSDKError } from "@/lib/errors"; import { createAgentPreset, getSystemSetting, getVisibleAgentPresets, } from "@/lib/db/queries"; import { AGENT_SECONDARY_TYPE_CONFIG_KEY, isValidSecondaryTypeForPresetType, normalizeAgentSecondaryTypeConfig, } from "@/lib/agents/secondary-type-config"; const listQuerySchema = z.object({ scope: z.enum(["all", "public", "private"]).default("all"), type: z.enum(["image", "video", "text"]).optional(), sectionId: z.string().trim().max(64).optional(), level1Id: z.string().trim().max(64).optional(), level2Id: z.string().trim().max(64).optional(), level3Id: z.string().trim().max(64).optional(), productCategory: z.string().trim().max(64).optional(), audienceTag: z.string().trim().max(64).optional(), usageScenario: z.string().trim().max(64).optional(), brand: z.string().trim().max(64).optional(), keyword: z.string().trim().max(128).optional(), sort: z.enum(["latest", "hottest", "most_used"]).default("latest"), }); const slotSchema = z.object({ title: z.string().trim().max(128).optional(), promptTemplate: z.string().trim().min(1).max(8000), defaultParams: z.record(z.string(), z.any()).optional(), requireUpload: z.boolean().optional(), uploadLabel: z.string().trim().max(128).optional(), referenceAssetIds: z.array(z.string().uuid()).max(12).optional(), }); const createAgentSchema = z .object({ name: z.string().trim().min(1).max(128), type: z.enum(["image", "video", "text"]), secondaryType: z.string().trim().max(128).optional(), visibility: z.enum(["public", "private"]).default("private"), categoryPath: z .object({ sectionId: z.string().trim().max(64).optional(), sectionName: z.string().trim().max(64).optional(), level1Id: z.string().trim().max(64).optional(), level1Name: z.string().trim().max(64).optional(), level2Id: z.string().trim().max(64).optional(), level2Name: z.string().trim().max(64).optional(), level3Id: z.string().trim().max(64).optional(), level3Name: z.string().trim().max(64).optional(), productCategory: z.string().trim().max(64).optional(), audienceTag: z.string().trim().max(64).optional(), usageScenario: z.string().trim().max(64).optional(), brand: z.string().trim().max(64).optional(), }) .optional(), description: z.string().trim().max(2000).optional(), defaultModelId: z.string().uuid(), defaultParams: z.record(z.string(), z.any()).optional(), avatarAssetId: z.string().uuid().optional(), slots: z.array(slotSchema).min(1).max(20), }); export async function GET(request: NextRequest) { try { const session = await auth(); if (!session?.user?.id) { return NextResponse.json({ success: false, error: "未登录" }, { status: 401 }); } const { searchParams } = new URL(request.url); const query = listQuerySchema.parse({ scope: searchParams.get("scope") || "all", type: searchParams.get("type") || undefined, sectionId: searchParams.get("sectionId") || undefined, level1Id: searchParams.get("level1Id") || undefined, level2Id: searchParams.get("level2Id") || undefined, level3Id: searchParams.get("level3Id") || undefined, productCategory: searchParams.get("productCategory") || undefined, audienceTag: searchParams.get("audienceTag") || undefined, usageScenario: searchParams.get("usageScenario") || undefined, brand: searchParams.get("brand") || undefined, keyword: searchParams.get("keyword") || undefined, sort: searchParams.get("sort") || "latest", }); const presets = await getVisibleAgentPresets({ userId: session.user.id, scope: query.scope, type: query.type, keyword: query.keyword, sort: query.sort, }); const filterByPath = (items: typeof presets) => items.filter((item) => { const path = item.categoryPath || {}; if (query.sectionId && path.sectionId !== query.sectionId) return false; if (query.level1Id && path.level1Id !== query.level1Id) return false; if (query.level2Id && path.level2Id !== query.level2Id) return false; if (query.level3Id && path.level3Id !== query.level3Id) return false; if (query.productCategory && path.productCategory !== query.productCategory) return false; if (query.audienceTag && path.audienceTag !== query.audienceTag) return false; if (query.usageScenario && path.usageScenario !== query.usageScenario) return false; if (query.brand && path.brand !== query.brand) return false; return true; }); return NextResponse.json({ success: true, data: { categories: [], public: filterByPath(presets.filter((item) => item.visibility === "public")), private: filterByPath(presets.filter((item) => item.visibility === "private")), }, }); } catch (error) { if (error instanceof ChatSDKError) { return error.toResponse(); } if (error instanceof z.ZodError) { return NextResponse.json( { success: false, error: error.message }, { status: 400 } ); } return NextResponse.json( { success: false, error: "获取风格化列表失败" }, { status: 500 } ); } } export async function POST(request: NextRequest) { try { const session = await auth(); if (!session?.user?.id) { return NextResponse.json({ success: false, error: "未登录" }, { status: 401 }); } const body = await request.json(); const data = createAgentSchema.parse(body); const secondaryTypeConfig = normalizeAgentSecondaryTypeConfig( ( await getSystemSetting({ key: AGENT_SECONDARY_TYPE_CONFIG_KEY }).catch(() => undefined) )?.value ); if ( !isValidSecondaryTypeForPresetType({ presetType: data.type, secondaryType: data.secondaryType, config: secondaryTypeConfig, }) ) { return NextResponse.json( { success: false, error: "所选二级类型不在后台配置列表中" }, { status: 400 } ); } const preset = await createAgentPreset({ userId: session.user.id, name: data.name, type: data.type, secondaryType: data.secondaryType, visibility: data.visibility, categoryPath: data.categoryPath, description: data.description, defaultModelId: data.defaultModelId, defaultParams: data.defaultParams, avatarAssetId: data.avatarAssetId, slots: data.slots, }); return NextResponse.json({ success: true, data: preset }); } catch (error) { if (error instanceof ChatSDKError) { return error.toResponse(); } if (error instanceof z.ZodError) { return NextResponse.json( { success: false, error: error.message }, { status: 400 } ); } return NextResponse.json( { success: false, error: "创建风格化失败" }, { status: 500 } ); } }