This commit is contained in:
xCyanGrizzly
2026-02-18 14:26:36 +01:00
commit 3a5726e82b
167 changed files with 104081 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
import { handlers } from "@/lib/auth";
export const { GET, POST } = handlers;

View File

@@ -0,0 +1,52 @@
import { NextRequest, NextResponse } from "next/server";
import type { CatalogBrand, CatalogResponse } from "@/types/catalog.types";
import { fetchFilaments } from "@/lib/catalog/shopify";
import { deduplicateItems } from "@/lib/catalog/cache";
export async function GET(req: NextRequest) {
const { searchParams } = req.nextUrl;
const brandFilter = searchParams.get("brand")?.toLowerCase();
const search = searchParams.get("search")?.toLowerCase();
try {
let items = deduplicateItems(await fetchFilaments());
// Build brand summary from unfiltered data
const brandMap = new Map<string, number>();
for (const p of items) {
brandMap.set(p.brand, (brandMap.get(p.brand) ?? 0) + 1);
}
if (brandFilter) {
items = items.filter((p) => p.brand.toLowerCase() === brandFilter);
}
if (search) {
items = items.filter(
(p) =>
p.name.toLowerCase().includes(search) ||
p.brand.toLowerCase().includes(search) ||
(p.color && p.color.toLowerCase().includes(search)) ||
(p.material && p.material.toLowerCase().includes(search)),
);
}
const brands: CatalogBrand[] = Array.from(brandMap.entries())
.map(([name, count]) => ({
id: name.toLowerCase().replace(/[^a-z0-9]/g, "_"),
name,
type: "filament" as const,
itemCount: count,
}))
.sort((a, b) => a.name.localeCompare(b.name));
const response: CatalogResponse = { items, brands };
return NextResponse.json(response);
} catch (error) {
console.error("Failed to fetch filament catalog:", error);
return NextResponse.json(
{ items: [], brands: [], error: "Failed to fetch filament data" },
{ status: 500 },
);
}
}

View File

@@ -0,0 +1,47 @@
import { NextRequest, NextResponse } from "next/server";
import type { CatalogBrand, CatalogItem, CatalogResponse } from "@/types/catalog.types";
// Static import — bundled at build time from the generated JSON
import paintsData from "@/data/catalog/paints.json";
const allPaints = paintsData as CatalogItem[];
export async function GET(req: NextRequest) {
const { searchParams } = req.nextUrl;
const brandFilter = searchParams.get("brand")?.toLowerCase();
const search = searchParams.get("search")?.toLowerCase();
let items = allPaints;
if (brandFilter) {
items = items.filter((p) => p.brand.toLowerCase() === brandFilter);
}
if (search) {
items = items.filter(
(p) =>
p.name.toLowerCase().includes(search) ||
p.brand.toLowerCase().includes(search) ||
(p.line && p.line.toLowerCase().includes(search)) ||
(p.productCode && p.productCode.toLowerCase().includes(search)),
);
}
// Build brand summary from the FULL dataset (not filtered)
const brandMap = new Map<string, number>();
for (const p of allPaints) {
brandMap.set(p.brand, (brandMap.get(p.brand) ?? 0) + 1);
}
const brands: CatalogBrand[] = Array.from(brandMap.entries())
.map(([name, count]) => ({
id: name.toLowerCase().replace(/[^a-z0-9]/g, "_"),
name,
type: "paint" as const,
itemCount: count,
}))
.sort((a, b) => a.name.localeCompare(b.name));
const response: CatalogResponse = { items, brands };
return NextResponse.json(response);
}

View File

@@ -0,0 +1,52 @@
import { NextRequest, NextResponse } from "next/server";
import type { CatalogBrand, CatalogResponse } from "@/types/catalog.types";
import { fetchResins } from "@/lib/catalog/shopify";
import { deduplicateItems } from "@/lib/catalog/cache";
export async function GET(req: NextRequest) {
const { searchParams } = req.nextUrl;
const brandFilter = searchParams.get("brand")?.toLowerCase();
const search = searchParams.get("search")?.toLowerCase();
try {
let items = deduplicateItems(await fetchResins());
// Build brand summary from unfiltered data
const brandMap = new Map<string, number>();
for (const p of items) {
brandMap.set(p.brand, (brandMap.get(p.brand) ?? 0) + 1);
}
if (brandFilter) {
items = items.filter((p) => p.brand.toLowerCase() === brandFilter);
}
if (search) {
items = items.filter(
(p) =>
p.name.toLowerCase().includes(search) ||
p.brand.toLowerCase().includes(search) ||
(p.color && p.color.toLowerCase().includes(search)) ||
(p.resinType && p.resinType.toLowerCase().includes(search)),
);
}
const brands: CatalogBrand[] = Array.from(brandMap.entries())
.map(([name, count]) => ({
id: name.toLowerCase().replace(/[^a-z0-9]/g, "_"),
name,
type: "resin" as const,
itemCount: count,
}))
.sort((a, b) => a.name.localeCompare(b.name));
const response: CatalogResponse = { items, brands };
return NextResponse.json(response);
} catch (error) {
console.error("Failed to fetch resin catalog:", error);
return NextResponse.json(
{ items: [], brands: [], error: "Failed to fetch resin data" },
{ status: 500 },
);
}
}

View File

@@ -0,0 +1,27 @@
import { prisma } from "@/lib/prisma";
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function GET() {
try {
await prisma.$queryRaw`SELECT 1`;
return NextResponse.json(
{
status: "healthy",
timestamp: new Date().toISOString(),
database: "connected",
},
{ status: 200 }
);
} catch {
return NextResponse.json(
{
status: "unhealthy",
timestamp: new Date().toISOString(),
database: "disconnected",
},
{ status: 503 }
);
}
}