Files
gcsim-translater/lib/catalog.ts
T
gunshiz 3314b2a9c6
CI / verify (push) Successful in 40s
CI / build-and-deploy (push) Successful in 1m13s
feat: show level 90 base stats
2026-09-09 20:25:20 +07:00

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);
}
}