221 lines
6.8 KiB
TypeScript
221 lines
6.8 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { defineQuery } from "next-sanity";
|
|
import { getAuthToken, medusaAuthFetch } from "@/lib/auth";
|
|
import { getAllProducts } from "@/lib/medusa";
|
|
import { sanity } from "@/lib/sanity";
|
|
import { formatDisplayName } from "@/lib/variants";
|
|
|
|
const RELEASES_BY_CATALOG_NOS_QUERY = defineQuery(`
|
|
*[_type == "release" && catalogNo in $catalogNos]{
|
|
catalogNo,
|
|
name,
|
|
albumArtist,
|
|
"slug": slug.current,
|
|
albumCover,
|
|
releaseDate,
|
|
genre,
|
|
instrumentation
|
|
}
|
|
`);
|
|
|
|
const MEDUSA_URL = process.env.MEDUSA_URL ?? process.env.NEXT_PUBLIC_MEDUSA_URL ?? "http://localhost:9000";
|
|
const API_KEY = process.env.MEDUSA_PUBLISHABLE_KEY ?? process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY ?? "";
|
|
|
|
type Order = { id: string };
|
|
|
|
type RawDownload = {
|
|
product_title?: string;
|
|
variant_title?: string;
|
|
sku?: string;
|
|
download_url?: string;
|
|
};
|
|
|
|
type RawUpcoming = {
|
|
product_title?: string;
|
|
variant_title?: string;
|
|
sku?: string;
|
|
release_date?: string;
|
|
};
|
|
|
|
type SanityRelease = {
|
|
catalogNo: string;
|
|
name: string;
|
|
albumArtist: string;
|
|
slug: string;
|
|
albumCover: unknown;
|
|
releaseDate: string | null;
|
|
genre: string[] | null;
|
|
instrumentation: string[] | null;
|
|
};
|
|
|
|
export async function GET() {
|
|
const token = await getAuthToken();
|
|
if (!token) {
|
|
return NextResponse.json({ error: "Not authenticated" }, { status: 401 });
|
|
}
|
|
|
|
try {
|
|
// Fetch all customer orders
|
|
const ordersData = await medusaAuthFetch<{ orders: Order[] }>(
|
|
"/store/orders",
|
|
);
|
|
|
|
const allDownloads: RawDownload[] = [];
|
|
const allUpcoming: RawUpcoming[] = [];
|
|
|
|
// For each order, fetch digital downloads via the custom Medusa endpoint
|
|
for (const order of ordersData.orders ?? []) {
|
|
try {
|
|
const res = await fetch(
|
|
`${MEDUSA_URL}/store/order-downloads?order_id=${encodeURIComponent(order.id)}`,
|
|
{
|
|
headers: {
|
|
"x-publishable-api-key": API_KEY,
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
},
|
|
);
|
|
|
|
if (res.ok) {
|
|
const data = await res.json();
|
|
if (data.downloads?.length) allDownloads.push(...data.downloads);
|
|
if (data.upcoming?.length) allUpcoming.push(...data.upcoming);
|
|
}
|
|
} catch {
|
|
// Non-critical — skip individual order failures
|
|
}
|
|
}
|
|
|
|
// Fetch grant-based downloads (admin-granted access without an order)
|
|
try {
|
|
const grantsData = await medusaAuthFetch<{ downloads: RawDownload[] }>(
|
|
"/store/customers/me/download-grants",
|
|
);
|
|
if (grantsData.downloads?.length) allDownloads.push(...grantsData.downloads);
|
|
} catch {
|
|
// Non-critical — grants may not exist or endpoint may not be available
|
|
}
|
|
|
|
// Deduplicate by SKU
|
|
const seenDownloads = new Set<string>();
|
|
const uniqueDownloads = allDownloads.filter((d) => {
|
|
if (!d.sku || seenDownloads.has(d.sku)) return false;
|
|
seenDownloads.add(d.sku);
|
|
return true;
|
|
});
|
|
const seenUpcoming = new Set<string>();
|
|
const uniqueUpcoming = allUpcoming.filter((u) => {
|
|
if (!u.sku || seenUpcoming.has(u.sku)) return false;
|
|
seenUpcoming.add(u.sku);
|
|
return true;
|
|
});
|
|
|
|
// Build product_title → catalogue_number map via Medusa products
|
|
const titleToCatalogNo = new Map<string, string>();
|
|
try {
|
|
const products = await getAllProducts();
|
|
for (const p of products) {
|
|
if (p.metadata?.catalogue_number && typeof p.metadata.catalogue_number === "string") {
|
|
titleToCatalogNo.set(p.title, p.metadata.catalogue_number);
|
|
}
|
|
}
|
|
} catch {
|
|
// Non-critical — cards will just miss Sanity data
|
|
}
|
|
|
|
// Query Sanity for matching releases
|
|
const catalogNos = [...new Set(titleToCatalogNo.values())];
|
|
const catalogNoToRelease = new Map<string, SanityRelease>();
|
|
if (catalogNos.length > 0) {
|
|
try {
|
|
const releases = await sanity.fetch<SanityRelease[]>(
|
|
RELEASES_BY_CATALOG_NOS_QUERY,
|
|
{ catalogNos },
|
|
);
|
|
for (const r of releases) {
|
|
catalogNoToRelease.set(r.catalogNo, r);
|
|
}
|
|
} catch {
|
|
// Non-critical — cards will render without covers
|
|
}
|
|
}
|
|
|
|
// Helper to look up Sanity data for a product title
|
|
function getSanityData(productTitle?: string) {
|
|
if (!productTitle) return null;
|
|
const catalogNo = titleToCatalogNo.get(productTitle);
|
|
if (!catalogNo) return null;
|
|
return catalogNoToRelease.get(catalogNo) ?? null;
|
|
}
|
|
|
|
// Helper to build enriched base fields from Sanity data
|
|
function enrichedBase(productTitle: string) {
|
|
const release = getSanityData(productTitle);
|
|
return {
|
|
product_title: productTitle,
|
|
albumCover: release?.albumCover ?? null,
|
|
albumArtist: release?.albumArtist ?? null,
|
|
slug: release?.slug ?? null,
|
|
catalogNo: release?.catalogNo ?? null,
|
|
releaseDate: release?.releaseDate ?? null,
|
|
genre: release?.genre ?? [],
|
|
instrumentation: release?.instrumentation ?? [],
|
|
};
|
|
}
|
|
|
|
// Group downloads by product_title, enriched with Sanity data
|
|
const downloadGroups = new Map<string, ReturnType<typeof enrichedBase> & {
|
|
formats: { variant_title: string; sku: string; download_url: string }[];
|
|
}>();
|
|
|
|
for (const dl of uniqueDownloads) {
|
|
const title = dl.product_title ?? "Unknown";
|
|
const existing = downloadGroups.get(title);
|
|
const fmt = {
|
|
variant_title: formatDisplayName(dl.sku ?? "") ?? dl.variant_title ?? "",
|
|
sku: dl.sku ?? "",
|
|
download_url: dl.download_url ?? "",
|
|
};
|
|
if (existing) {
|
|
existing.formats.push(fmt);
|
|
} else {
|
|
downloadGroups.set(title, { ...enrichedBase(title), formats: [fmt] });
|
|
}
|
|
}
|
|
|
|
// Group upcoming by product_title, enriched with Sanity data
|
|
const upcomingGroups = new Map<string, ReturnType<typeof enrichedBase> & {
|
|
release_date: string;
|
|
formats: { variant_title: string; sku: string }[];
|
|
}>();
|
|
|
|
for (const up of uniqueUpcoming) {
|
|
const title = up.product_title ?? "Unknown";
|
|
const existing = upcomingGroups.get(title);
|
|
const fmt = {
|
|
variant_title: formatDisplayName(up.sku ?? "") ?? up.variant_title ?? "",
|
|
sku: up.sku ?? "",
|
|
};
|
|
if (existing) {
|
|
existing.formats.push(fmt);
|
|
} else {
|
|
upcomingGroups.set(title, {
|
|
...enrichedBase(title),
|
|
release_date: up.release_date ?? "",
|
|
formats: [fmt],
|
|
});
|
|
}
|
|
}
|
|
|
|
return NextResponse.json({
|
|
downloads: Array.from(downloadGroups.values()),
|
|
upcoming: Array.from(upcomingGroups.values()),
|
|
});
|
|
} catch (e) {
|
|
console.error("[account:downloads]", (e as Error).message);
|
|
return NextResponse.json(
|
|
{ error: "Failed to fetch downloads" },
|
|
{ status: 500 },
|
|
);
|
|
}
|
|
}
|