better backend comms

This commit is contained in:
johba 2025-09-24 09:41:28 +02:00
parent d0e8623cf9
commit 02a057622c
17 changed files with 1054 additions and 134 deletions

View file

@ -8,6 +8,10 @@ import type { WatchBlocksReturnType } from "viem";
import { bigInt2Number } from "@/utils/helper";
const demo = sessionStorage.getItem("demo") === "true";
const GRAPHQL_TIMEOUT_MS = 15_000;
const RETRY_BASE_DELAY_MS = 1_500;
const RETRY_MAX_DELAY_MS = 60_000;
interface StatsRecord {
burnNextHourProjected: string;
burnedLastDay: string;
@ -27,14 +31,54 @@ interface StatsRecord {
const rawStatsCollections = ref<Array<StatsRecord>>([]);
const loading = ref(false);
const initialized = ref(false);
const statsError = ref<string | null>(null);
const statsRetryDelayMs = ref(RETRY_BASE_DELAY_MS);
let statsRetryTimer: number | null = null;
export async function loadStatsCollection() {
logger.info(`loadStatsCollection for chain: ${chainData.value?.path}`);
if (!chainData.value?.graphql) {
return [];
function formatGraphqlError(error: unknown): string {
if (axios.isAxiosError(error)) {
const responseErrors = (error.response?.data as any)?.errors;
if (Array.isArray(responseErrors) && responseErrors.length > 0) {
return responseErrors.map((err: any) => err?.message ?? "GraphQL error").join(", ");
}
if (error.response?.status) {
return `GraphQL request failed with status ${error.response.status}`;
}
if (error.message) {
return error.message;
}
}
if (error instanceof Error && error.message) {
return error.message;
}
return "Unknown GraphQL error";
}
const res = await axios.post(chainData.value?.graphql, {
function clearStatsRetryTimer() {
if (statsRetryTimer !== null) {
clearTimeout(statsRetryTimer);
statsRetryTimer = null;
}
}
function scheduleStatsRetry() {
if (typeof window === "undefined") {
return;
}
if (statsRetryTimer !== null) {
return;
}
const delay = statsRetryDelayMs.value;
statsRetryTimer = window.setTimeout(async () => {
statsRetryTimer = null;
await loadStats();
}, delay);
statsRetryDelayMs.value = Math.min(statsRetryDelayMs.value * 2, RETRY_MAX_DELAY_MS);
}
export async function loadStatsCollection(endpoint: string) {
logger.info(`loadStatsCollection for chain: ${chainData.value?.path}`);
const res = await axios.post(endpoint, {
query: `query StatsQuery {
stats(id: "0x01") {
burnNextHourProjected
@ -52,11 +96,16 @@ export async function loadStatsCollection() {
totalMinted
}
}`,
});
}, { timeout: GRAPHQL_TIMEOUT_MS });
const errors = res.data?.errors;
if (Array.isArray(errors) && errors.length > 0) {
throw new Error(errors.map((err: any) => err?.message ?? "GraphQL error").join(", "));
}
const stats = res.data?.data?.stats as StatsRecord | undefined;
if (!stats) {
return [];
throw new Error("Stats entity not found in GraphQL response");
}
return [{ ...stats, kraikenTotalSupply: stats.kraikenTotalSupply }];
@ -178,10 +227,31 @@ const claimedSlots = computed(() => {
export async function loadStats() {
loading.value = true;
rawStatsCollections.value = await loadStatsCollection();
const endpoint = chainData.value?.graphql?.trim();
if (!endpoint) {
rawStatsCollections.value = [];
statsError.value = "GraphQL endpoint not configured for this chain.";
clearStatsRetryTimer();
statsRetryDelayMs.value = RETRY_BASE_DELAY_MS;
loading.value = false;
initialized.value = true;
return;
}
loading.value = false;
initialized.value = true;
try {
rawStatsCollections.value = await loadStatsCollection(endpoint);
statsError.value = null;
statsRetryDelayMs.value = RETRY_BASE_DELAY_MS;
clearStatsRetryTimer();
} catch (error) {
console.warn("[stats] loadStats() failed", error);
rawStatsCollections.value = [];
statsError.value = formatGraphqlError(error);
scheduleStatsRetry();
} finally {
loading.value = false;
initialized.value = true;
}
}
let unwatch: any = null;
@ -212,6 +282,7 @@ export function useStatCollection() {
// })
}
onUnmounted(() => {
clearStatsRetryTimer();
unwatch();
});
@ -227,5 +298,7 @@ export function useStatCollection() {
maxSlots,
stakeableSupply,
claimedSlots,
statsError,
loading,
});
}