refactor: remove catalog sync jobs and version history
This commit is contained in:
+21
-130
@@ -1,4 +1,3 @@
|
||||
import { asc, eq } from "drizzle-orm";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import * as z from "zod";
|
||||
@@ -6,10 +5,7 @@ import * as z from "zod";
|
||||
import { getDb } from "@/db/client";
|
||||
import {
|
||||
catalogArtifacts,
|
||||
catalogCharacterCurrent,
|
||||
catalogCharacters,
|
||||
catalogSyncJobs,
|
||||
catalogVersions,
|
||||
catalogWeapons,
|
||||
} from "@/db/schema";
|
||||
import {
|
||||
@@ -63,7 +59,6 @@ const artifactSchema = z.record(
|
||||
);
|
||||
|
||||
interface SyncedCharacter {
|
||||
version: string;
|
||||
key: string;
|
||||
name: string;
|
||||
element: string | null;
|
||||
@@ -138,7 +133,6 @@ async function mapConcurrent<T, R>(
|
||||
}
|
||||
|
||||
async function uploadAssets(
|
||||
jobId: string,
|
||||
assets: Array<{
|
||||
source?: string;
|
||||
body?: Uint8Array;
|
||||
@@ -148,12 +142,7 @@ async function uploadAssets(
|
||||
): Promise<Set<string>> {
|
||||
const storage = await getMediaStorage();
|
||||
let next = 0;
|
||||
let completed = 0;
|
||||
const missing = new Set<string>();
|
||||
await getDb()
|
||||
.update(catalogSyncJobs)
|
||||
.set({ total: assets.length, progress: 0 })
|
||||
.where(eq(catalogSyncJobs.id, jobId));
|
||||
async function worker() {
|
||||
while (next < assets.length) {
|
||||
const asset = assets[next++];
|
||||
@@ -178,13 +167,6 @@ async function uploadAssets(
|
||||
});
|
||||
}
|
||||
}
|
||||
completed += 1;
|
||||
if (completed % 10 === 0 || completed === assets.length) {
|
||||
await getDb()
|
||||
.update(catalogSyncJobs)
|
||||
.set({ progress: completed })
|
||||
.where(eq(catalogSyncJobs.id, jobId));
|
||||
}
|
||||
}
|
||||
}
|
||||
await Promise.all(
|
||||
@@ -193,40 +175,19 @@ async function uploadAssets(
|
||||
return missing;
|
||||
}
|
||||
|
||||
export async function claimCatalogSyncJob() {
|
||||
const [pending] = await getDb()
|
||||
.select()
|
||||
.from(catalogSyncJobs)
|
||||
.where(eq(catalogSyncJobs.status, "pending"))
|
||||
.orderBy(asc(catalogSyncJobs.createdAt))
|
||||
.limit(1);
|
||||
if (!pending) return null;
|
||||
const [claimed] = await getDb()
|
||||
.update(catalogSyncJobs)
|
||||
.set({ status: "processing", startedAt: new Date(), error: null })
|
||||
.where(eq(catalogSyncJobs.id, pending.id))
|
||||
.returning();
|
||||
return claimed ?? null;
|
||||
}
|
||||
|
||||
export async function processCatalogSyncJob(jobId: string): Promise<void> {
|
||||
try {
|
||||
const versionInfo = versionSchema.parse(await getJson(VERSION_URL));
|
||||
const version = versionInfo.version;
|
||||
await getDb()
|
||||
.update(catalogSyncJobs)
|
||||
.set({ version })
|
||||
.where(eq(catalogSyncJobs.id, jobId));
|
||||
const [charactersRaw, weaponsRaw, artifactsRaw] = await Promise.all([
|
||||
export async function processCatalogSync(): Promise<void> {
|
||||
const versionInfo = versionSchema.parse(await getJson(VERSION_URL));
|
||||
const version = versionInfo.version;
|
||||
const [charactersRaw, weaponsRaw, artifactsRaw] = await Promise.all([
|
||||
getJson(`${API_ROOT}/${version}/charlist.json`),
|
||||
getJson(`${API_ROOT}/${version}/weaponlist.json`),
|
||||
getJson(`${API_ROOT}/${version}/artifactlist.json`),
|
||||
]);
|
||||
const characterData = characterSchema.parse(charactersRaw);
|
||||
const weaponData = weaponSchema.parse(weaponsRaw);
|
||||
const artifactData = artifactSchema.parse(artifactsRaw);
|
||||
const characterData = characterSchema.parse(charactersRaw);
|
||||
const weaponData = weaponSchema.parse(weaponsRaw);
|
||||
const artifactData = artifactSchema.parse(artifactsRaw);
|
||||
|
||||
const remoteCharacters: SyncedCharacter[] = await mapConcurrent(
|
||||
const remoteCharacters: SyncedCharacter[] = await mapConcurrent(
|
||||
Object.entries(characterData).filter(
|
||||
([, value]) =>
|
||||
value.enName &&
|
||||
@@ -246,7 +207,6 @@ export async function processCatalogSyncJob(jobId: string): Promise<void> {
|
||||
const nameCardImage = nameCardImageName(value.CardImg);
|
||||
const element = detail?.info?.element ?? value.element ?? null;
|
||||
return {
|
||||
version,
|
||||
key,
|
||||
name: value.enName,
|
||||
element,
|
||||
@@ -276,7 +236,7 @@ export async function processCatalogSyncJob(jobId: string): Promise<void> {
|
||||
: [],
|
||||
};
|
||||
},
|
||||
);
|
||||
);
|
||||
const customCharacters: SyncedCharacter[] = await mapConcurrent(
|
||||
[...customTravelers],
|
||||
async (traveler) => {
|
||||
@@ -286,7 +246,6 @@ export async function processCatalogSyncJob(jobId: string): Promise<void> {
|
||||
),
|
||||
);
|
||||
return {
|
||||
version,
|
||||
key: traveler.key,
|
||||
name: traveler.name,
|
||||
element: traveler.element,
|
||||
@@ -318,12 +277,11 @@ export async function processCatalogSyncJob(jobId: string): Promise<void> {
|
||||
value.enName && value.enName !== "???" && value.weaponIcon,
|
||||
)
|
||||
.map(([key, value]) => ({
|
||||
version,
|
||||
key,
|
||||
name: value.enName,
|
||||
rarity: rarity(value.qualityType),
|
||||
weaponType: value.weaponType ?? null,
|
||||
imageKey: `catalog/${version}/weapons/${webpName(value.weaponIcon)}`,
|
||||
imageKey: `catalog/weapons/${webpName(value.weaponIcon)}`,
|
||||
sourceImage: `${API_ROOT}/assets/weaponicon/${encodeURIComponent(webpName(value.weaponIcon))}`,
|
||||
}));
|
||||
let artifacts = Object.entries(artifactData)
|
||||
@@ -331,11 +289,10 @@ export async function processCatalogSyncJob(jobId: string): Promise<void> {
|
||||
([, value]) => value.enName && value.enName !== "???" && value.setIcon,
|
||||
)
|
||||
.map(([key, value]) => ({
|
||||
version,
|
||||
key,
|
||||
name: value.enName,
|
||||
rarity: rarity(value.qualityType),
|
||||
imageKey: `catalog/${version}/artifacts/${webpName(value.setIcon)}`,
|
||||
imageKey: `catalog/artifacts/${webpName(value.setIcon)}`,
|
||||
sourceImage: `${API_ROOT}/assets/artifacts/${encodeURIComponent(webpName(value.setIcon))}`,
|
||||
}));
|
||||
const uniqueAssets = new Map<
|
||||
@@ -398,7 +355,7 @@ export async function processCatalogSyncJob(jobId: string): Promise<void> {
|
||||
});
|
||||
}
|
||||
}
|
||||
const missingAssets = await uploadAssets(jobId, [...uniqueAssets.values()]);
|
||||
const missingAssets = await uploadAssets([...uniqueAssets.values()]);
|
||||
characters = characters.filter((item) => !missingAssets.has(item.imageKey));
|
||||
characters = characters.map((item) => ({
|
||||
...item,
|
||||
@@ -454,10 +411,9 @@ export async function processCatalogSyncJob(jobId: string): Promise<void> {
|
||||
{ type: "application/json", acl: "public-read" },
|
||||
),
|
||||
storage.write(
|
||||
`catalog/${version}/weapons.json`,
|
||||
"catalog/weapons.json",
|
||||
JSON.stringify(
|
||||
weapons.map((item) => ({
|
||||
version: item.version,
|
||||
key: item.key,
|
||||
name: item.name,
|
||||
rarity: item.rarity,
|
||||
@@ -468,10 +424,9 @@ export async function processCatalogSyncJob(jobId: string): Promise<void> {
|
||||
{ type: "application/json", acl: "public-read" },
|
||||
),
|
||||
storage.write(
|
||||
`catalog/${version}/artifacts.json`,
|
||||
"catalog/artifacts.json",
|
||||
JSON.stringify(
|
||||
artifacts.map((item) => ({
|
||||
version: item.version,
|
||||
key: item.key,
|
||||
name: item.name,
|
||||
rarity: item.rarity,
|
||||
@@ -483,32 +438,14 @@ export async function processCatalogSyncJob(jobId: string): Promise<void> {
|
||||
]);
|
||||
|
||||
await getDb().transaction(async (tx) => {
|
||||
await tx
|
||||
.insert(catalogVersions)
|
||||
.values({ version, sourceTimestamp: versionInfo.timestamp ?? null })
|
||||
.onConflictDoUpdate({
|
||||
target: catalogVersions.version,
|
||||
set: {
|
||||
sourceTimestamp: versionInfo.timestamp ?? null,
|
||||
syncedAt: new Date(),
|
||||
},
|
||||
});
|
||||
await tx
|
||||
.delete(catalogCharacters)
|
||||
.where(eq(catalogCharacters.version, version));
|
||||
await tx.delete(catalogCharacterCurrent);
|
||||
await tx
|
||||
.delete(catalogWeapons)
|
||||
.where(eq(catalogWeapons.version, version));
|
||||
await tx
|
||||
.delete(catalogArtifacts)
|
||||
.where(eq(catalogArtifacts.version, version));
|
||||
await tx.delete(catalogCharacters);
|
||||
await tx.delete(catalogWeapons);
|
||||
await tx.delete(catalogArtifacts);
|
||||
if (characters.length)
|
||||
await tx
|
||||
.insert(catalogCharacters)
|
||||
.values(
|
||||
characters.map((item) => ({
|
||||
version: item.version,
|
||||
key: item.key,
|
||||
name: item.name,
|
||||
element: item.element,
|
||||
@@ -522,28 +459,11 @@ export async function processCatalogSyncJob(jobId: string): Promise<void> {
|
||||
talents: item.talents,
|
||||
})),
|
||||
);
|
||||
if (characters.length)
|
||||
await tx.insert(catalogCharacterCurrent).values(
|
||||
characters.map((item) => ({
|
||||
key: item.key,
|
||||
name: item.name,
|
||||
element: item.element,
|
||||
rarity: item.rarity,
|
||||
weaponType: item.weaponType,
|
||||
imageKey: item.imageKey,
|
||||
elementIconKey: item.elementIconKey,
|
||||
splashImageKey: item.splashImageKey,
|
||||
nameCardImageKey: item.nameCardImageKey,
|
||||
constellations: item.constellations,
|
||||
talents: item.talents,
|
||||
})),
|
||||
);
|
||||
if (weapons.length)
|
||||
await tx
|
||||
.insert(catalogWeapons)
|
||||
.values(
|
||||
weapons.map((item) => ({
|
||||
version: item.version,
|
||||
key: item.key,
|
||||
name: item.name,
|
||||
rarity: item.rarity,
|
||||
@@ -556,44 +476,15 @@ export async function processCatalogSyncJob(jobId: string): Promise<void> {
|
||||
.insert(catalogArtifacts)
|
||||
.values(
|
||||
artifacts.map((item) => ({
|
||||
version: item.version,
|
||||
key: item.key,
|
||||
name: item.name,
|
||||
rarity: item.rarity,
|
||||
imageKey: item.imageKey,
|
||||
})),
|
||||
);
|
||||
await tx.update(catalogVersions).set({ active: false });
|
||||
await tx
|
||||
.update(catalogVersions)
|
||||
.set({ active: true, syncedAt: new Date() })
|
||||
.where(eq(catalogVersions.version, version));
|
||||
await tx
|
||||
.update(catalogSyncJobs)
|
||||
.set({
|
||||
status: "completed",
|
||||
completedAt: new Date(),
|
||||
progress: uniqueAssets.size,
|
||||
total: uniqueAssets.size,
|
||||
})
|
||||
.where(eq(catalogSyncJobs.id, jobId));
|
||||
});
|
||||
await storage.write("catalog/current.json", JSON.stringify({ version }), {
|
||||
type: "application/json",
|
||||
acl: "public-read",
|
||||
});
|
||||
} catch (cause) {
|
||||
await getDb()
|
||||
.update(catalogSyncJobs)
|
||||
.set({
|
||||
status: "failed",
|
||||
completedAt: new Date(),
|
||||
error:
|
||||
cause instanceof Error
|
||||
? cause.message.slice(0, 4_000)
|
||||
: "Unknown sync failure",
|
||||
})
|
||||
.where(eq(catalogSyncJobs.id, jobId));
|
||||
throw cause;
|
||||
}
|
||||
await storage.write("catalog/current.json", JSON.stringify({ version }), {
|
||||
type: "application/json",
|
||||
acl: "public-read",
|
||||
});
|
||||
}
|
||||
|
||||
+13
-19
@@ -8,9 +8,7 @@ import {
|
||||
artifactProfiles,
|
||||
artifactRecommendations,
|
||||
catalogArtifacts,
|
||||
catalogCharacterCurrent,
|
||||
catalogSyncJobs,
|
||||
catalogVersions,
|
||||
catalogCharacters,
|
||||
catalogWeapons,
|
||||
constellationRecommendations,
|
||||
extraSectionRows,
|
||||
@@ -33,6 +31,7 @@ import {
|
||||
updateGuideSchema,
|
||||
weaponSectionSchema,
|
||||
} from "@/lib/guides/schemas";
|
||||
import { processCatalogSync } from "@/lib/catalog/sync";
|
||||
|
||||
const DEFAULT_SECTIONS = [
|
||||
{ kind: "weapon", slug: "weapon", title: "Weapons", sortOrder: 0 },
|
||||
@@ -52,10 +51,8 @@ async function emitGuideEvents(tx: Parameters<Parameters<ReturnType<typeof getDb
|
||||
export async function createGuide(input: unknown) {
|
||||
const parsed = createGuideSchema.parse(input);
|
||||
const db = getDb();
|
||||
const [version] = await db.select().from(catalogVersions).where(eq(catalogVersions.active, true)).limit(1);
|
||||
if (!version) throw new Error("กรุณา Sync ข้อมูล Lunaris ก่อนสร้างคู่มือ");
|
||||
const [character] = await db.select().from(catalogCharacterCurrent).where(eq(catalogCharacterCurrent.key, parsed.characterKey)).limit(1);
|
||||
if (!character) throw new Error("ไม่พบตัวละครใน Catalog เวอร์ชันปัจจุบัน");
|
||||
const [character] = await db.select().from(catalogCharacters).where(eq(catalogCharacters.key, parsed.characterKey)).limit(1);
|
||||
if (!character) throw new Error("ไม่พบตัวละครใน Catalog");
|
||||
const [cover] = await db.select().from(media).where(and(eq(media.id, parsed.coverMediaId), eq(media.status, "ready"))).limit(1);
|
||||
if (!cover) throw new Error("กรุณาอัปโหลดภาพ Guide ให้สำเร็จ");
|
||||
const baseSlug = slugFromCharacterName(character.name, character.key);
|
||||
@@ -65,7 +62,6 @@ export async function createGuide(input: unknown) {
|
||||
return db.transaction(async (tx) => {
|
||||
const [guide] = await tx.insert(guides).values({
|
||||
characterKey: character.key,
|
||||
catalogVersion: version.version,
|
||||
slug,
|
||||
name: character.name,
|
||||
overview: parsed.overview,
|
||||
@@ -97,7 +93,7 @@ export async function saveWeapons(guideId: string, input: unknown) {
|
||||
return getDb().transaction(async (tx) => {
|
||||
const updated = await advanceGuideVersion(tx, guideId, data.expectedVersion);
|
||||
await tx.delete(weaponRecommendations).where(eq(weaponRecommendations.guideId, guideId));
|
||||
if (data.rows.length) await tx.insert(weaponRecommendations).values(data.rows.map((row, sortOrder) => ({ ...row, overallPercent: row.overallPercent?.toString() ?? null, personalPercent: row.personalPercent?.toString() ?? null, guideId, catalogVersion: guide.catalogVersion, sortOrder })));
|
||||
if (data.rows.length) await tx.insert(weaponRecommendations).values(data.rows.map((row, sortOrder) => ({ ...row, overallPercent: row.overallPercent?.toString() ?? null, personalPercent: row.personalPercent?.toString() ?? null, guideId, sortOrder })));
|
||||
return updated;
|
||||
});
|
||||
}
|
||||
@@ -128,7 +124,7 @@ export async function saveArtifacts(guideId: string, input: unknown) {
|
||||
await tx.delete(artifactGroups).where(eq(artifactGroups.guideId, guideId));
|
||||
for (const [sortOrder, group] of data.groups.entries()) {
|
||||
const [created] = await tx.insert(artifactGroups).values({ guideId, name: group.name, sortOrder }).returning();
|
||||
if (group.rows.length) await tx.insert(artifactRecommendations).values(group.rows.map((row, rowOrder) => ({ ...row, percent: row.percent.toString(), groupId: created.id, catalogVersion: guide.catalogVersion, sortOrder: rowOrder })));
|
||||
if (group.rows.length) await tx.insert(artifactRecommendations).values(group.rows.map((row, rowOrder) => ({ ...row, percent: row.percent.toString(), groupId: created.id, sortOrder: rowOrder })));
|
||||
}
|
||||
await tx.insert(artifactProfiles).values({ guideId, sands: data.sands, goblet: data.goblet, circlet: data.circlet, substats: data.substats, targets: data.targets, talents: data.talents, note: data.note }).onConflictDoUpdate({ target: artifactProfiles.guideId, set: { sands: data.sands, goblet: data.goblet, circlet: data.circlet, substats: data.substats, targets: data.targets, talents: data.talents, note: data.note } });
|
||||
return updated;
|
||||
@@ -154,9 +150,9 @@ export async function saveTeams(guideId: string, input: unknown) {
|
||||
const weaponKeys = [...new Set(members.map((member) => member.weaponKey).filter((key): key is string => Boolean(key)))];
|
||||
const artifactKeys = [...new Set(members.map((member) => member.artifactKey).filter((key): key is string => Boolean(key)))];
|
||||
const [characters, weapons, artifacts] = await Promise.all([
|
||||
characterKeys.length ? getDb().select().from(catalogCharacterCurrent).where(inArray(catalogCharacterCurrent.key, characterKeys)) : [],
|
||||
weaponKeys.length ? getDb().select().from(catalogWeapons).where(and(eq(catalogWeapons.version, guide.catalogVersion), inArray(catalogWeapons.key, weaponKeys))) : [],
|
||||
artifactKeys.length ? getDb().select().from(catalogArtifacts).where(and(eq(catalogArtifacts.version, guide.catalogVersion), inArray(catalogArtifacts.key, artifactKeys))) : [],
|
||||
characterKeys.length ? getDb().select().from(catalogCharacters).where(inArray(catalogCharacters.key, characterKeys)) : [],
|
||||
weaponKeys.length ? getDb().select().from(catalogWeapons).where(inArray(catalogWeapons.key, weaponKeys)) : [],
|
||||
artifactKeys.length ? getDb().select().from(catalogArtifacts).where(inArray(catalogArtifacts.key, artifactKeys)) : [],
|
||||
]);
|
||||
if (characters.length !== characterKeys.length) throw new Error("ไม่พบตัวละครบางรายการใน Catalog");
|
||||
if (weapons.length !== weaponKeys.length) throw new Error("ไม่พบอาวุธบางรายการใน Catalog");
|
||||
@@ -173,7 +169,7 @@ export async function saveTeams(guideId: string, input: unknown) {
|
||||
await tx.delete(teamCompositions).where(eq(teamCompositions.guideId, guideId));
|
||||
for (const [sortOrder, team] of data.teams.entries()) {
|
||||
const [created] = await tx.insert(teamCompositions).values({ guideId, title: team.title, rotationSeconds: team.rotationSeconds.toString(), combo: team.combo, sortOrder }).returning();
|
||||
await tx.insert(teamMembers).values(team.members.map((member, position) => ({ ...member, teamId: created.id, position, catalogVersion: guide.catalogVersion, damage: member.damage.toString() })));
|
||||
await tx.insert(teamMembers).values(team.members.map((member, position) => ({ ...member, teamId: created.id, position, damage: member.damage.toString() })));
|
||||
}
|
||||
return updated;
|
||||
});
|
||||
@@ -222,9 +218,7 @@ export async function permanentlyDeleteGuide(guideId: string) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function enqueueCatalogSync(userId: string) {
|
||||
const [running] = await getDb().select().from(catalogSyncJobs).where(sql`${catalogSyncJobs.status} in ('pending', 'processing')`).limit(1);
|
||||
if (running) return running;
|
||||
const [job] = await getDb().insert(catalogSyncJobs).values({ triggeredById: userId }).returning();
|
||||
return job;
|
||||
export async function runCatalogSync() {
|
||||
await processCatalogSync();
|
||||
return { status: "saved" as const };
|
||||
}
|
||||
|
||||
+27
-42
@@ -1,6 +1,6 @@
|
||||
import "server-only";
|
||||
|
||||
import { and, asc, desc, eq, inArray, isNotNull, isNull } from "drizzle-orm";
|
||||
import { and, asc, eq, inArray, isNotNull, isNull } from "drizzle-orm";
|
||||
|
||||
import { getDb } from "@/db";
|
||||
import {
|
||||
@@ -8,9 +8,7 @@ import {
|
||||
artifactProfiles,
|
||||
artifactRecommendations,
|
||||
catalogArtifacts,
|
||||
catalogCharacterCurrent,
|
||||
catalogSyncJobs,
|
||||
catalogVersions,
|
||||
catalogCharacters,
|
||||
catalogWeapons,
|
||||
constellationRecommendations,
|
||||
extraSectionRows,
|
||||
@@ -23,11 +21,11 @@ import {
|
||||
} from "@/db/schema";
|
||||
import { publicMediaUrl } from "@/lib/media/storage";
|
||||
|
||||
function catalogCharacterWithUrls(item: typeof catalogCharacterCurrent.$inferSelect) {
|
||||
function catalogCharacterWithUrls(item: typeof catalogCharacters.$inferSelect) {
|
||||
return {
|
||||
...item,
|
||||
imageUrl: publicMediaUrl(item.imageKey),
|
||||
splashImageUrl: item.splashImageKey ? publicMediaUrl(item.splashImageKey) : null,
|
||||
splashImageUrl: item.coopImageKey ? publicMediaUrl(item.coopImageKey) : null,
|
||||
nameCardImageUrl: item.nameCardImageKey ? publicMediaUrl(item.nameCardImageKey) : null,
|
||||
elementIconUrl: item.elementIconKey ? publicMediaUrl(item.elementIconKey) : null,
|
||||
constellations: item.constellations.map((constellation) => ({
|
||||
@@ -43,15 +41,13 @@ function catalogCharacterWithUrls(item: typeof catalogCharacterCurrent.$inferSel
|
||||
|
||||
export async function getActiveCatalog() {
|
||||
const db = getDb();
|
||||
const [version] = await db.select().from(catalogVersions).where(eq(catalogVersions.active, true)).limit(1);
|
||||
if (!version) return null;
|
||||
const [characters, weapons, artifacts] = await Promise.all([
|
||||
db.select().from(catalogCharacterCurrent).orderBy(asc(catalogCharacterCurrent.name)),
|
||||
db.select().from(catalogWeapons).where(eq(catalogWeapons.version, version.version)).orderBy(asc(catalogWeapons.name)),
|
||||
db.select().from(catalogArtifacts).where(eq(catalogArtifacts.version, version.version)).orderBy(asc(catalogArtifacts.name)),
|
||||
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) return null;
|
||||
return {
|
||||
version: version.version,
|
||||
characters: characters.map(catalogCharacterWithUrls),
|
||||
weapons: weapons.map((item) => ({ ...item, imageUrl: publicMediaUrl(item.imageKey) })),
|
||||
artifacts: artifacts.map((item) => ({ ...item, imageUrl: publicMediaUrl(item.imageKey) })),
|
||||
@@ -82,31 +78,31 @@ export async function getGuideByCharacterKey(characterKey: string) {
|
||||
|
||||
export async function getPublicGuide(slug: string) {
|
||||
const [row] = await getDb()
|
||||
.select({ guide: guides, cover: media, character: catalogCharacterCurrent })
|
||||
.select({ guide: guides, cover: media, character: catalogCharacters })
|
||||
.from(guides)
|
||||
.innerJoin(media, eq(media.id, guides.coverMediaId))
|
||||
.innerJoin(catalogCharacterCurrent, eq(catalogCharacterCurrent.key, guides.characterKey))
|
||||
.innerJoin(catalogCharacters, eq(catalogCharacters.key, guides.characterKey))
|
||||
.where(and(eq(guides.slug, slug), isNull(guides.trashedAt)))
|
||||
.limit(1);
|
||||
if (!row) return null;
|
||||
const [sections, weaponRows, groups, profile, constellations, teams] = await Promise.all([
|
||||
getDb().select().from(guideSections).where(and(eq(guideSections.guideId, row.guide.id), eq(guideSections.enabled, true))).orderBy(asc(guideSections.sortOrder)),
|
||||
getDb().select({ recommendation: weaponRecommendations, weapon: catalogWeapons }).from(weaponRecommendations).innerJoin(catalogWeapons, and(eq(catalogWeapons.version, weaponRecommendations.catalogVersion), eq(catalogWeapons.key, weaponRecommendations.weaponKey))).where(eq(weaponRecommendations.guideId, row.guide.id)).orderBy(asc(weaponRecommendations.sortOrder)),
|
||||
getDb().select({ recommendation: weaponRecommendations, weapon: catalogWeapons }).from(weaponRecommendations).innerJoin(catalogWeapons, eq(catalogWeapons.key, weaponRecommendations.weaponKey)).where(eq(weaponRecommendations.guideId, row.guide.id)).orderBy(asc(weaponRecommendations.sortOrder)),
|
||||
getDb().select().from(artifactGroups).where(eq(artifactGroups.guideId, row.guide.id)).orderBy(asc(artifactGroups.sortOrder)),
|
||||
getDb().select().from(artifactProfiles).where(eq(artifactProfiles.guideId, row.guide.id)).limit(1),
|
||||
getDb().select().from(constellationRecommendations).where(eq(constellationRecommendations.guideId, row.guide.id)).orderBy(asc(constellationRecommendations.level)),
|
||||
getDb().select().from(teamCompositions).where(eq(teamCompositions.guideId, row.guide.id)).orderBy(asc(teamCompositions.sortOrder)),
|
||||
]);
|
||||
|
||||
const artifactRows = groups.length ? await getDb().select({ recommendation: artifactRecommendations, artifact: catalogArtifacts }).from(artifactRecommendations).innerJoin(catalogArtifacts, and(eq(catalogArtifacts.version, artifactRecommendations.catalogVersion), eq(catalogArtifacts.key, artifactRecommendations.firstArtifactKey))).where(inArray(artifactRecommendations.groupId, groups.map((group) => group.id))).orderBy(asc(artifactRecommendations.sortOrder)) : [];
|
||||
const artifactRows = groups.length ? await getDb().select({ recommendation: artifactRecommendations, artifact: catalogArtifacts }).from(artifactRecommendations).innerJoin(catalogArtifacts, eq(catalogArtifacts.key, artifactRecommendations.firstArtifactKey)).where(inArray(artifactRecommendations.groupId, groups.map((group) => group.id))).orderBy(asc(artifactRecommendations.sortOrder)) : [];
|
||||
const secondKeys = artifactRows.map(({ recommendation }) => recommendation.secondArtifactKey).filter((key): key is string => Boolean(key));
|
||||
const secondArtifacts = secondKeys.length ? await getDb().select().from(catalogArtifacts).where(and(eq(catalogArtifacts.version, row.guide.catalogVersion), inArray(catalogArtifacts.key, secondKeys))) : [];
|
||||
const members = teams.length ? await getDb().select({ member: teamMembers, character: catalogCharacterCurrent }).from(teamMembers).innerJoin(catalogCharacterCurrent, eq(catalogCharacterCurrent.key, teamMembers.characterKey)).where(inArray(teamMembers.teamId, teams.map((team) => team.id))).orderBy(asc(teamMembers.position)) : [];
|
||||
const secondArtifacts = secondKeys.length ? await getDb().select().from(catalogArtifacts).where(inArray(catalogArtifacts.key, secondKeys)) : [];
|
||||
const members = teams.length ? await getDb().select({ member: teamMembers, character: catalogCharacters }).from(teamMembers).innerJoin(catalogCharacters, eq(catalogCharacters.key, teamMembers.characterKey)).where(inArray(teamMembers.teamId, teams.map((team) => team.id))).orderBy(asc(teamMembers.position)) : [];
|
||||
const memberWeaponKeys = members.map(({ member }) => member.weaponKey).filter((key): key is string => Boolean(key));
|
||||
const memberArtifactKeys = members.map(({ member }) => member.artifactKey).filter((key): key is string => Boolean(key));
|
||||
const [memberWeapons, memberArtifacts] = await Promise.all([
|
||||
memberWeaponKeys.length ? getDb().select().from(catalogWeapons).where(and(eq(catalogWeapons.version, row.guide.catalogVersion), inArray(catalogWeapons.key, memberWeaponKeys))) : [],
|
||||
memberArtifactKeys.length ? getDb().select().from(catalogArtifacts).where(and(eq(catalogArtifacts.version, row.guide.catalogVersion), inArray(catalogArtifacts.key, memberArtifactKeys))) : [],
|
||||
memberWeaponKeys.length ? getDb().select().from(catalogWeapons).where(inArray(catalogWeapons.key, memberWeaponKeys)) : [],
|
||||
memberArtifactKeys.length ? getDb().select().from(catalogArtifacts).where(inArray(catalogArtifacts.key, memberArtifactKeys)) : [],
|
||||
]);
|
||||
const extraIds = sections.filter((section) => section.kind === "extra").map((section) => section.id);
|
||||
const extraRows = extraIds.length ? await getDb().select({ row: extraSectionRows, asset: media }).from(extraSectionRows).leftJoin(media, eq(media.id, extraSectionRows.mediaId)).where(inArray(extraSectionRows.sectionId, extraIds)).orderBy(asc(extraSectionRows.sortOrder)) : [];
|
||||
@@ -133,11 +129,6 @@ export async function getPublicGuide(slug: string) {
|
||||
};
|
||||
}
|
||||
|
||||
export async function getLatestCatalogSyncJob() {
|
||||
const [job] = await getDb().select().from(catalogSyncJobs).orderBy(desc(catalogSyncJobs.createdAt)).limit(1);
|
||||
return job ?? null;
|
||||
}
|
||||
|
||||
export async function getPublicTeamExport(teamId: string) {
|
||||
const [row] = await getDb()
|
||||
.select({ team: teamCompositions, guide: guides })
|
||||
@@ -156,11 +147,11 @@ export async function getPublicTeamExport(teamId: string) {
|
||||
if (!row) return null;
|
||||
|
||||
const members = await getDb()
|
||||
.select({ member: teamMembers, character: catalogCharacterCurrent })
|
||||
.select({ member: teamMembers, character: catalogCharacters })
|
||||
.from(teamMembers)
|
||||
.innerJoin(
|
||||
catalogCharacterCurrent,
|
||||
eq(catalogCharacterCurrent.key, teamMembers.characterKey),
|
||||
catalogCharacters,
|
||||
eq(catalogCharacters.key, teamMembers.characterKey),
|
||||
)
|
||||
.where(eq(teamMembers.teamId, teamId))
|
||||
.orderBy(asc(teamMembers.position));
|
||||
@@ -168,8 +159,8 @@ export async function getPublicTeamExport(teamId: string) {
|
||||
const weaponKeys = members.map(({ member }) => member.weaponKey).filter((key): key is string => Boolean(key));
|
||||
const artifactKeys = members.map(({ member }) => member.artifactKey).filter((key): key is string => Boolean(key));
|
||||
const [weapons, artifacts] = await Promise.all([
|
||||
weaponKeys.length ? getDb().select().from(catalogWeapons).where(and(eq(catalogWeapons.version, row.guide.catalogVersion), inArray(catalogWeapons.key, weaponKeys))) : [],
|
||||
artifactKeys.length ? getDb().select().from(catalogArtifacts).where(and(eq(catalogArtifacts.version, row.guide.catalogVersion), inArray(catalogArtifacts.key, artifactKeys))) : [],
|
||||
weaponKeys.length ? getDb().select().from(catalogWeapons).where(inArray(catalogWeapons.key, weaponKeys)) : [],
|
||||
artifactKeys.length ? getDb().select().from(catalogArtifacts).where(inArray(catalogArtifacts.key, artifactKeys)) : [],
|
||||
]);
|
||||
|
||||
return {
|
||||
@@ -210,10 +201,7 @@ export async function getPublicWeaponExport(guideId: string) {
|
||||
.from(weaponRecommendations)
|
||||
.innerJoin(
|
||||
catalogWeapons,
|
||||
and(
|
||||
eq(catalogWeapons.version, weaponRecommendations.catalogVersion),
|
||||
eq(catalogWeapons.key, weaponRecommendations.weaponKey),
|
||||
),
|
||||
eq(catalogWeapons.key, weaponRecommendations.weaponKey),
|
||||
)
|
||||
.where(eq(weaponRecommendations.guideId, guideId))
|
||||
.orderBy(asc(weaponRecommendations.sortOrder));
|
||||
@@ -229,7 +217,7 @@ export async function getPublicWeaponExport(guideId: string) {
|
||||
|
||||
export async function getPublicArtifactExport(guideId: string) {
|
||||
const [row] = await getDb()
|
||||
.select({ guide: guides, character: catalogCharacterCurrent })
|
||||
.select({ guide: guides, character: catalogCharacters })
|
||||
.from(guides)
|
||||
.innerJoin(
|
||||
guideSections,
|
||||
@@ -240,8 +228,8 @@ export async function getPublicArtifactExport(guideId: string) {
|
||||
),
|
||||
)
|
||||
.innerJoin(
|
||||
catalogCharacterCurrent,
|
||||
eq(catalogCharacterCurrent.key, guides.characterKey),
|
||||
catalogCharacters,
|
||||
eq(catalogCharacters.key, guides.characterKey),
|
||||
)
|
||||
.where(and(eq(guides.id, guideId), isNull(guides.trashedAt)))
|
||||
.limit(1);
|
||||
@@ -257,10 +245,7 @@ export async function getPublicArtifactExport(guideId: string) {
|
||||
.from(artifactRecommendations)
|
||||
.innerJoin(
|
||||
catalogArtifacts,
|
||||
and(
|
||||
eq(catalogArtifacts.version, artifactRecommendations.catalogVersion),
|
||||
eq(catalogArtifacts.key, artifactRecommendations.firstArtifactKey),
|
||||
),
|
||||
eq(catalogArtifacts.key, artifactRecommendations.firstArtifactKey),
|
||||
)
|
||||
.where(inArray(artifactRecommendations.groupId, groups.map((group) => group.id)))
|
||||
.orderBy(asc(artifactRecommendations.sortOrder))
|
||||
@@ -269,7 +254,7 @@ export async function getPublicArtifactExport(guideId: string) {
|
||||
.map(({ recommendation }) => recommendation.secondArtifactKey)
|
||||
.filter((key): key is string => Boolean(key));
|
||||
const secondArtifacts = secondKeys.length
|
||||
? await getDb().select().from(catalogArtifacts).where(and(eq(catalogArtifacts.version, row.guide.catalogVersion), inArray(catalogArtifacts.key, secondKeys)))
|
||||
? await getDb().select().from(catalogArtifacts).where(inArray(catalogArtifacts.key, secondKeys))
|
||||
: [];
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user