gantt-board/src/app/api/projects/route.ts
Max b2b2beef2d removed defaults and legacy
Signed-off-by: Max <ai-agent@topdoglabs.com>
2026-02-22 17:48:49 -06:00

149 lines
4.4 KiB
TypeScript

import { NextResponse } from "next/server";
import { getServiceSupabase } from "@/lib/supabase/client";
import { getAuthenticatedUser } from "@/lib/server/auth";
export const runtime = "nodejs";
// GET - fetch all projects
export async function GET() {
try {
const user = await getAuthenticatedUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const supabase = getServiceSupabase();
const { data: projects, error } = await supabase
.from("projects")
.select("*")
.order("created_at", { ascending: true });
if (error) throw error;
return NextResponse.json({ projects: projects || [] });
} catch (error) {
console.error(">>> API GET /projects error:", error);
return NextResponse.json({ error: "Failed to fetch projects" }, { status: 500 });
}
}
// POST - create a new project
export async function POST(request: Request) {
try {
const user = await getAuthenticatedUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const body = await request.json();
const { name, description, color } = body;
if (!name || typeof name !== "string") {
return NextResponse.json({ error: "Missing project name" }, { status: 400 });
}
const supabase = getServiceSupabase();
const now = new Date().toISOString();
const { data, error } = await supabase
.from("projects")
.insert({
name,
description: description || null,
color: color || "#3b82f6",
created_at: now,
})
.select()
.single();
if (error) throw error;
return NextResponse.json({ success: true, project: data });
} catch (error) {
console.error(">>> API POST /projects error:", error);
return NextResponse.json({ error: "Failed to create project" }, { status: 500 });
}
}
// PATCH - update a project
export async function PATCH(request: Request) {
try {
const user = await getAuthenticatedUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const body = await request.json();
const { id, ...rawUpdates } = body;
if (!id) {
return NextResponse.json({ error: "Missing project id" }, { status: 400 });
}
const updates: Record<string, unknown> = {};
if (typeof rawUpdates.name === "string" && rawUpdates.name.trim().length > 0) {
updates.name = rawUpdates.name.trim();
}
if (Object.prototype.hasOwnProperty.call(rawUpdates, "description")) {
updates.description =
typeof rawUpdates.description === "string" && rawUpdates.description.trim().length > 0
? rawUpdates.description
: null;
}
if (typeof rawUpdates.color === "string" && rawUpdates.color.trim().length > 0) {
updates.color = rawUpdates.color;
}
if (Object.keys(updates).length === 0) {
return NextResponse.json({ error: "No valid project updates provided" }, { status: 400 });
}
const supabase = getServiceSupabase();
const { data, error } = await supabase
.from("projects")
.update(updates)
.eq("id", id)
.select()
.single();
if (error) throw error;
return NextResponse.json({ success: true, project: data });
} catch (error) {
console.error(">>> API PATCH /projects error:", error);
return NextResponse.json({ error: "Failed to update project" }, { status: 500 });
}
}
// DELETE - delete a project
export async function DELETE(request: Request) {
try {
const user = await getAuthenticatedUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const { id } = await request.json();
if (!id) {
return NextResponse.json({ error: "Missing project id" }, { status: 400 });
}
const supabase = getServiceSupabase();
const { error, count } = await supabase
.from("projects")
.delete({ count: "exact" })
.eq("id", id);
if (error) throw error;
if ((count ?? 0) === 0) {
return NextResponse.json({ error: "Project not found" }, { status: 404 });
}
return NextResponse.json({ success: true });
} catch (error) {
console.error(">>> API DELETE /projects error:", error);
return NextResponse.json({ error: "Failed to delete project" }, { status: 500 });
}
}