122 lines
4.0 KiB
TypeScript
122 lines
4.0 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { query } from "@/lib/db";
|
|
import { isAuthenticated } from "@/lib/auth";
|
|
|
|
export interface DBProject {
|
|
id: string;
|
|
title: string;
|
|
category: "software" | "games" | "lab";
|
|
category_label: string;
|
|
tagline: string;
|
|
status: string;
|
|
status_type: "active" | "alpha" | "prototype" | "upcoming";
|
|
release_target: string;
|
|
description: string;
|
|
long_description: string;
|
|
features: string[] | string;
|
|
stack: string[] | string;
|
|
accent_gradient: string;
|
|
border_glow: string;
|
|
icon_name: string;
|
|
order_index?: number;
|
|
created_at?: string;
|
|
updated_at?: string;
|
|
}
|
|
|
|
export async function GET() {
|
|
try {
|
|
const rows = await query<DBProject[]>(
|
|
"SELECT * FROM projects ORDER BY order_index ASC, created_at DESC"
|
|
);
|
|
|
|
const formatted = rows.map((p) => ({
|
|
id: p.id,
|
|
title: p.title,
|
|
category: p.category,
|
|
categoryLabel: p.category_label,
|
|
tagline: p.tagline,
|
|
status: p.status,
|
|
statusType: p.status_type,
|
|
releaseTarget: p.release_target,
|
|
description: p.description,
|
|
longDescription: p.long_description,
|
|
features: typeof p.features === "string" ? JSON.parse(p.features) : p.features || [],
|
|
stack: typeof p.stack === "string" ? JSON.parse(p.stack) : p.stack || [],
|
|
accentGradient: p.accent_gradient,
|
|
borderGlow: p.border_glow,
|
|
iconName: p.icon_name,
|
|
orderIndex: p.order_index,
|
|
}));
|
|
|
|
return NextResponse.json({ success: true, data: formatted });
|
|
} catch (err: unknown) {
|
|
const message = err instanceof Error ? err.message : "Database query error";
|
|
return NextResponse.json({ error: message }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
export async function POST(req: Request) {
|
|
try {
|
|
const authed = await isAuthenticated();
|
|
if (!authed) {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
|
|
const body = await req.json();
|
|
|
|
const id = (body.id || body.title.toLowerCase().replace(/[^a-z0-9]+/g, "-")).trim();
|
|
const title = body.title?.trim();
|
|
const category = body.category || "software";
|
|
const category_label = body.categoryLabel || body.category_label || "Original Software";
|
|
const tagline = body.tagline?.trim() || "";
|
|
const status = body.status?.trim() || "In Active Dev";
|
|
const status_type = body.statusType || body.status_type || "active";
|
|
const release_target = body.releaseTarget || body.release_target || "2027";
|
|
const description = body.description?.trim() || "";
|
|
const long_description = body.longDescription || body.long_description || "";
|
|
const features = JSON.stringify(Array.isArray(body.features) ? body.features : []);
|
|
const stack = JSON.stringify(Array.isArray(body.stack) ? body.stack : []);
|
|
const accent_gradient =
|
|
body.accentGradient ||
|
|
body.accent_gradient ||
|
|
"from-cyan-500/20 via-blue-600/10 to-transparent";
|
|
const border_glow =
|
|
body.borderGlow ||
|
|
body.border_glow ||
|
|
"hover:border-cyan-400/50 hover:shadow-[0_0_30px_rgba(56,189,248,0.25)]";
|
|
const icon_name = body.iconName || body.icon_name || "Terminal";
|
|
const order_index = Number(body.orderIndex ?? body.order_index ?? 0);
|
|
|
|
await query(
|
|
`INSERT INTO projects (
|
|
id, title, category, category_label, tagline, status, status_type,
|
|
release_target, description, long_description, features, stack,
|
|
accent_gradient, border_glow, icon_name, order_index
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
[
|
|
id,
|
|
title,
|
|
category,
|
|
category_label,
|
|
tagline,
|
|
status,
|
|
status_type,
|
|
release_target,
|
|
description,
|
|
long_description,
|
|
features,
|
|
stack,
|
|
accent_gradient,
|
|
border_glow,
|
|
icon_name,
|
|
order_index,
|
|
]
|
|
);
|
|
|
|
return NextResponse.json({ success: true, id });
|
|
} catch (err: unknown) {
|
|
const message = err instanceof Error ? err.message : "Database error";
|
|
return NextResponse.json({ error: message }, { status: 500 });
|
|
}
|
|
}
|