← back to Nineoh Guide

apps/web/app/api/cast/route.ts

53 lines

import { NextResponse } from "next/server";
import { pool } from "@/lib/db";

export const dynamic = "force-dynamic";

/**
 * Cast list for the Expo app. Mirrors the web /cast page query.
 * Photo attribution is carried through — CC-BY-SA requires the caption be shown.
 */
export async function GET() {
  try {
    // Main cast + recurring/guest stars. `kind` lets the client group them.
    const { rows } = await pool.query(
      `select distinct on (cp.id)
              cp.id, cp.name, cp.biography_original,
              ch.name as character_name, cr.credit_type as kind, cr.billing_order,
              a.file_url, a.attribution_text, a.license_type, a.license_url
         from cast_people cp
         left join credits cr on cr.person_id = cp.id
              and cr.credit_type in ('main-cast','recurring')
         left join characters ch on ch.id = cr.character_id
         left join assets a on a.id = cp.headshot_asset_id
        order by cp.id,
                 (cr.credit_type = 'main-cast') desc nulls last,
                 cr.billing_order nulls last`
    );
    const cast = rows
      .map((r) => ({
        id: r.id,
        name: r.name,
        characterName: r.character_name ?? null,
        bio: r.biography_original ?? null,
        photoUrl: r.file_url ?? null,
        attribution: r.attribution_text ?? null,
        licenseType: r.license_type ?? null,
        licenseUrl: r.license_url ?? null,
        kind: (r.kind ?? "recurring") as string,
        billingOrder: r.billing_order as number | null,
      }))
      // main cast first, then recurring by episode count (billing_order = -eps)
      .sort((a, b) => {
        const am = a.kind === "main-cast" ? 0 : 1;
        const bm = b.kind === "main-cast" ? 0 : 1;
        if (am !== bm) return am - bm;
        return (a.billingOrder ?? 0) - (b.billingOrder ?? 0);
      });
    return NextResponse.json({ cast });
  } catch (err) {
    console.error("[api/cast] DB error:", err);
    return NextResponse.json({ cast: [] }, { status: 500 });
  }
}