import { NextRequest, NextResponse } from "next/server"; import { z } from "zod"; import { db } from "@/lib/db"; import { tutorialVideos } from "@/lib/db/schema"; import { eq, asc } from "drizzle-orm"; import { auth } from "@/app/(auth)/auth"; import { cachedPublicJson } from "@/lib/public-api/performance"; // GET /api/tutorial-videos - 获取所有视频教程 (公开) export async function GET() { return cachedPublicJson({ cacheKey: "tutorial-videos", fallbackData: { videos: [] }, timeoutMs: 1600, load: async () => { const videos = await db .select() .from(tutorialVideos) .where(eq(tutorialVideos.isEnabled, true)) .orderBy(asc(tutorialVideos.order)); return { videos }; }, }); } const createSchema = z.object({ title: z.string().min(1).max(128), videoUrl: z.string().url(), coverUrl: z.string().url().optional().or(z.literal("")).transform(v => v || null), description: z.string().nullable().optional(), order: z.number().int().default(0), isEnabled: z.boolean().default(true), }); // POST /api/tutorial-videos - 创建视频教程(仅管理员) export async function POST(request: NextRequest) { try { const session = await auth(); if (!session?.user) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } if (session.user.type !== "admin") { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); } const body = await request.json(); const data = createSchema.parse(body); const [created] = await db .insert(tutorialVideos) .values({ ...data, coverUrl: data.coverUrl || null, description: data.description || null, createdBy: session.user.id, }) .returning(); return NextResponse.json(created); } catch (error) { console.error("[Tutorial Videos API] Create error:", error); if (error instanceof z.ZodError) { return NextResponse.json( { error: "Invalid request data", details: error.errors }, { status: 400 } ); } return NextResponse.json( { error: "Failed to create tutorial video" }, { status: 500 } ); } } const updateSchema = z.object({ id: z.string().uuid(), title: z.string().min(1).max(128).optional(), videoUrl: z.string().url().optional(), coverUrl: z.string().url().optional().or(z.literal("")).transform(v => v || null), description: z.string().nullable().optional(), order: z.number().int().optional(), isEnabled: z.boolean().optional(), }); // PUT /api/tutorial-videos - 更新视频教程(仅管理员) export async function PUT(request: NextRequest) { try { const session = await auth(); if (!session?.user) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } if (session.user.type !== "admin") { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); } const body = await request.json(); const { id, ...data } = updateSchema.parse(body); const [updated] = await db .update(tutorialVideos) .set({ ...data, updatedAt: new Date(), }) .where(eq(tutorialVideos.id, id)) .returning(); if (!updated) { return NextResponse.json({ error: "Tutorial video not found" }, { status: 404 }); } return NextResponse.json(updated); } catch (error) { console.error("[Tutorial Videos API] Update error:", error); if (error instanceof z.ZodError) { return NextResponse.json( { error: "Invalid request data", details: error.errors }, { status: 400 } ); } return NextResponse.json( { error: "Failed to update tutorial video" }, { status: 500 } ); } } // DELETE /api/tutorial-videos - 删除视频教程(仅管理员) export async function DELETE(request: NextRequest) { try { const session = await auth(); if (!session?.user) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } if (session.user.type !== "admin") { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); } const { searchParams } = new URL(request.url); const id = searchParams.get("id"); if (!id) { return NextResponse.json({ error: "ID is required" }, { status: 400 }); } await db.delete(tutorialVideos).where(eq(tutorialVideos.id, id)); return NextResponse.json({ success: true }); } catch (error) { console.error("[Tutorial Videos API] Delete error:", error); return NextResponse.json( { error: "Failed to delete tutorial video" }, { status: 500 } ); } }