"use server"; import { prisma } from "@/core/db/prisma"; import { uploadFileToMinio } from "@/core/storage/minio"; import { verifySession } from "@/core/security/session"; import { projectSchema } from "./project-schema"; import { revalidatePath } from "next/cache"; export async function createProjectAction(prevState: any, formData: FormData) { // 1. Verify Authentication const session = await verifySession(); if (!session) return { success: false, message: "Unauthorized" }; // 2. Extract and Validate Form Data const data = { title: formData.get("title") as string, slug: formData.get("slug") as string, description: formData.get("description") as string, category: formData.get("category") as string, repoUrl: formData.get("repoUrl") as string, liveUrl: formData.get("liveUrl") as string, isPublished: formData.get("isPublished") === "on", image: formData.get("image") as File | null, techStack: formData.get("techStack") as string, year: formData.get("year") as string, highlights: formData.get("highlights") as string, }; const validation = projectSchema.safeParse(data); if (!validation.success) { return { success: false, message: validation.error.issues[0].message }; } // 3. Process Image Upload let imageUrl: string | undefined = undefined; if (data.image && data.image.size > 0 && data.image.name) { try { imageUrl = await uploadFileToMinio(data.image); } catch (e) { console.error("Image upload failed:", e); return { success: false, message: "Failed to upload image. Ensure MinIO is running and bucket exists." }; } } // 4. Save to Database try { const project = await prisma.project.create({ data: { title: validation.data.title, slug: validation.data.slug, description: validation.data.description, category: validation.data.category, repoUrl: validation.data.repoUrl || null, liveUrl: validation.data.liveUrl || null, isPublished: validation.data.isPublished, imageUrl: imageUrl, techStack: validation.data.techStack ? validation.data.techStack.split(",").map(s => s.trim()).filter(s => s) : [], year: validation.data.year || null, highlights: validation.data.highlights ? validation.data.highlights.split(",").map(s => s.trim()).filter(s => s) : [], }, }); revalidatePath("/admin/dashboard"); revalidatePath("/"); return { success: true }; } catch (error: any) { console.error("DB Error:", error); if (error?.code === "P2002") { return { success: false, message: "Project slug already exists" }; } return { success: false, message: "Failed to create project" }; } } export async function updateProjectAction(id: string, prevState: any, formData: FormData) { const session = await verifySession(); if (!session) return { success: false, message: "Unauthorized" }; const data = { title: formData.get("title") as string, slug: formData.get("slug") as string, description: formData.get("description") as string, category: formData.get("category") as string, repoUrl: formData.get("repoUrl") as string, liveUrl: formData.get("liveUrl") as string, isPublished: formData.get("isPublished") === "on", image: formData.get("image") as File | null, techStack: formData.get("techStack") as string, year: formData.get("year") as string, highlights: formData.get("highlights") as string, }; const validation = projectSchema.safeParse(data); if (!validation.success) { return { success: false, message: validation.error.issues[0].message }; } let imageUrl: string | undefined = undefined; if (data.image && data.image.size > 0 && data.image.name) { try { imageUrl = await uploadFileToMinio(data.image); } catch (e) { return { success: false, message: "Failed to upload new image." }; } } try { await prisma.project.update({ where: { id }, data: { title: validation.data.title, slug: validation.data.slug, description: validation.data.description, category: validation.data.category, repoUrl: validation.data.repoUrl || null, liveUrl: validation.data.liveUrl || null, isPublished: validation.data.isPublished, ...(imageUrl && { imageUrl }), // only update image if a new one was uploaded techStack: validation.data.techStack ? validation.data.techStack.split(",").map(s => s.trim()).filter(s => s) : [], year: validation.data.year || null, highlights: validation.data.highlights ? validation.data.highlights.split(",").map(s => s.trim()).filter(s => s) : [], }, }); revalidatePath("/admin/dashboard"); revalidatePath("/admin/dashboard/projects"); revalidatePath("/"); return { success: true }; } catch (error: any) { if (error?.code === "P2002") { return { success: false, message: "Project slug already exists" }; } return { success: false, message: "Failed to update project" }; } } export async function deleteProjectAction(id: string) { const session = await verifySession(); if (!session) return { success: false, message: "Unauthorized" }; try { await prisma.project.delete({ where: { id } }); revalidatePath("/admin/dashboard"); revalidatePath("/"); return { success: true }; } catch (error) { return { success: false, message: "Failed to delete project" }; } } export async function toggleProjectPublishAction(id: string, isPublished: boolean) { const session = await verifySession(); if (!session) return { success: false, message: "Unauthorized" }; try { await prisma.project.update({ where: { id }, data: { isPublished: !isPublished }, }); revalidatePath("/admin/dashboard"); revalidatePath("/"); return { success: true }; } catch (error) { return { success: false, message: "Failed to update project status" }; } }