76 lines
4.6 KiB
TypeScript
76 lines
4.6 KiB
TypeScript
import "server-only";
|
|
import { asc } from "drizzle-orm";
|
|
import { connection } from "next/server";
|
|
import { getDb } from "@/db/client";
|
|
import { catalogArtifacts, catalogCharacters, catalogWeapons } from "@/db/schema";
|
|
import { parseLunarisVersion, weaponStatsFromLunaris } from "@/lib/catalog-stats";
|
|
import { gcsimKey } from "@/lib/translator/keys";
|
|
import type { Catalog, CatalogItem } from "@/lib/translator/types";
|
|
|
|
const fallback: Catalog = {
|
|
version: "fallback",
|
|
characters: [
|
|
{ id: "10000002", key: "kamisatoayaka", name: "Kamisato Ayaka", imageUrl: "", rarity: 5, weaponType: "WEAPON_SWORD_ONE_HAND", element: "Cryo", supported: true, baseStats: [] },
|
|
{ id: "10000032", key: "bennett", name: "Bennett", imageUrl: "", rarity: 4, weaponType: "WEAPON_SWORD_ONE_HAND", element: "Pyro", supported: true, baseStats: [] },
|
|
{ id: "10000089", key: "furina", name: "Furina", imageUrl: "", rarity: 5, weaponType: "WEAPON_SWORD_ONE_HAND", element: "Hydro", supported: true, baseStats: [] },
|
|
{ id: "10000047", key: "kaedeharakazuha", name: "Kaedehara Kazuha", imageUrl: "", rarity: 5, weaponType: "WEAPON_SWORD_ONE_HAND", element: "Anemo", supported: true, baseStats: [] },
|
|
],
|
|
weapons: [
|
|
{ id: "11401", key: "favoniussword", name: "Favonius Sword", imageUrl: "", rarity: 4, weaponType: "WEAPON_SWORD_ONE_HAND", supported: true, baseStats: [] },
|
|
{ id: "11509", key: "mistsplitterreforged", name: "Mistsplitter Reforged", imageUrl: "", rarity: 5, weaponType: "WEAPON_SWORD_ONE_HAND", supported: true, baseStats: [] },
|
|
],
|
|
artifacts: [
|
|
{ id: "15007", key: "noblesseoblige", name: "Noblesse Oblige", imageUrl: "", rarity: 5, supported: true, baseStats: [] },
|
|
{ id: "15020", key: "blizzardstrayer", name: "Blizzard Strayer", imageUrl: "", rarity: 5, supported: true, baseStats: [] },
|
|
{ id: "15032", key: "goldentroupe", name: "Golden Troupe", imageUrl: "", rarity: 5, supported: true, baseStats: [] },
|
|
],
|
|
};
|
|
|
|
function publicUrl(key: string) {
|
|
const base = (process.env.S3_PUBLIC_URL ?? "https://buzz-cdn.astrxl.dev").replace(/\/+$/u, "");
|
|
return `${base}/${key.split("/").map(encodeURIComponent).join("/")}`;
|
|
}
|
|
|
|
async function getLunarisWeaponStats() {
|
|
const root = "https://api.lunaris.moe/data";
|
|
try {
|
|
const versionResponse = await fetch(`${root}/version.json`, { next: { revalidate: 3600 } });
|
|
if (!versionResponse.ok) throw new Error(`Version request returned ${versionResponse.status}`);
|
|
const version = parseLunarisVersion(await versionResponse.json());
|
|
const weaponsResponse = await fetch(`${root}/${encodeURIComponent(version)}/weaponlist.json`, { next: { revalidate: 3600 } });
|
|
if (!weaponsResponse.ok) throw new Error(`Weapon request returned ${weaponsResponse.status}`);
|
|
return weaponStatsFromLunaris(await weaponsResponse.json());
|
|
} catch (error) {
|
|
console.error("Lunaris weapon stats unavailable", error);
|
|
return new Map();
|
|
}
|
|
}
|
|
|
|
function withWeaponStats(catalog: Catalog, stats: Awaited<ReturnType<typeof getLunarisWeaponStats>>): Catalog {
|
|
return { ...catalog, weapons: catalog.weapons.map((weapon) => ({ ...weapon, baseStats: stats.get(weapon.id) ?? [] })) };
|
|
}
|
|
|
|
export async function getCatalog(): Promise<Catalog> {
|
|
await connection();
|
|
const weaponStats = await getLunarisWeaponStats();
|
|
if (!process.env.DATABASE_URL) return withWeaponStats(fallback, weaponStats);
|
|
try {
|
|
const db = getDb();
|
|
const [characters, weapons, artifacts] = await Promise.all([
|
|
db.select().from(catalogCharacters).orderBy(asc(catalogCharacters.name)),
|
|
db.select().from(catalogWeapons).orderBy(asc(catalogWeapons.name)),
|
|
db.select().from(catalogArtifacts).orderBy(asc(catalogArtifacts.name)),
|
|
]);
|
|
if (!characters.length || !weapons.length || !artifacts.length) return withWeaponStats(fallback, weaponStats);
|
|
const map = (kind: "character" | "weapon" | "artifact", rows: typeof characters | typeof weapons | typeof artifacts): CatalogItem[] => rows.map((row) => {
|
|
const character = "element" in row ? row : null;
|
|
const key = gcsimKey(kind, row.name, character?.element, row.key);
|
|
return { id: row.key, key, name: row.name, imageUrl: publicUrl(row.imageKey), rarity: row.rarity, weaponType: "weaponType" in row ? row.weaponType : null, element: character?.element ?? null, supported: Boolean(key), baseStats: character?.baseStats ?? [] };
|
|
});
|
|
return withWeaponStats({ version: "buzz-current", characters: map("character", characters), weapons: map("weapon", weapons), artifacts: map("artifact", artifacts) }, weaponStats);
|
|
} catch (error) {
|
|
console.error("Catalog unavailable; using fallback catalog", error);
|
|
return withWeaponStats(fallback, weaponStats);
|
|
}
|
|
}
|