2026-05-26 20:10:36 +08:00

168 lines
4.7 KiB
TypeScript

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 }
);
}
}