OOKNET                             [ /  search the index  ]  
──────────────────────────────────────────────────────────────────────────────────────
══════════════════════════════════════════════════════════════════════════════════════
OOKNET   [ /  search  ]  
────────────────────────────────────────────────
════════════════════════════════════════════════
 
 
master @ 214 LINES
 
[ HISTORY ]  [ UP ]
 

import type { LeaderboardData, PlayerProfileData, BestRun } from "./types.js";
import {
  buildLeaderboardURL,
  buildPlayerProfileURL,
  buildStaticLeaderboardPath,
  buildStaticPlayerLeaderboardPath,
  buildStaticPlayerProfilePath,
  buildStaticTotalRunsLeaderboardPath,
  buildStaticAccountLeaderboardPath,
  buildStaticAccountTotalRunsPath,
  buildUnifiedLeaderboardPath,
} from "./utils.js";

// api base configuration (always use relative paths for Netlify/SSR)
const API_BASE = "";

// generic api request handler with error handling
async function apiRequest<T>(url: string, origin?: string): Promise<T> {
  let fullUrl: string;
  if (url.startsWith("http")) {
    fullUrl = url;
  } else if (origin) {
    // SSR context - need full URL
    fullUrl = `${origin}${url}`;
  } else {
    // Client-side context - relative URL works
    fullUrl = `${API_BASE}${url}`;
  }
  const response = await fetch(fullUrl);

  if (!response.ok) {
    if (response.status === 404) {
      throw new Error("Player not found");
    }

    throw new Error(
      `Failed to load data: ${response.status} ${response.statusText}`,
    );
  }

  return response.json();
}

// leaderboard api functions
export async function fetchGlobalLeaderboard(
  dungeonId: number,
  page: number = 1,
  teamFilter: boolean = true,
  origin?: string,
  seasonId: number = 1,
): Promise<LeaderboardData> {
  const url = `${API_BASE}${buildStaticLeaderboardPath("global", "", dungeonId, pa
ge, seasonId)}`;
  console.log("Fetching global leaderboard:", url);
  return apiRequest<LeaderboardData>(url, origin);
}

export async function fetchRegionalLeaderboard(
  region: string,
  dungeonId: number,
  page: number = 1,
  teamFilter: boolean = true,
  origin?: string,
  seasonId: number = 1,
): Promise<LeaderboardData> {
  const url = `${API_BASE}${buildStaticLeaderboardPath(region, "all", dungeonId, p
age, seasonId)}`;
  console.log("Fetching regional leaderboard:", url);
  return apiRequest<LeaderboardData>(url, origin);
}

export async function fetchRealmLeaderboard(
  region: string,
  realmSlug: string,
  dungeonId: number,
  page: number = 1,
  teamFilter: boolean = true,
  origin?: string,
  seasonId: number = 1,
): Promise<LeaderboardData> {
  const url = `${API_BASE}${buildStaticLeaderboardPath(region, realmSlug, dungeonI
d, page, seasonId)}`;
  console.log("Fetching realm leaderboard:", url);
  return apiRequest<LeaderboardData>(url, origin);
}

export async function fetchPlayerLeaderboard(
  scope: "global" | "regional" | "realm" = "global",
  region?: string,
  page: number = 1,
  pageSize: number = 25,
  opts?: { realmSlug?: string; classKey?: string; seasonId?: number },
  origin?: string,
): Promise<any> {
  const url = `${API_BASE}${buildStaticPlayerLeaderboardPath(scope, region, page, 
opts)}`;
  console.log("Fetching player leaderboard:", url);
  return apiRequest(url, origin);
}

export async function fetchTotalRunsLeaderboard(
  scope: "global" | "regional" | "realm" = "global",
  region?: string,
  page: number = 1,
  pageSize: number = 25,
  opts?: { realmSlug?: string; classKey?: string },
  origin?: string,
): Promise<any> {
  const url = `${API_BASE}${buildStaticTotalRunsLeaderboardPath(scope, region, pag
e, opts)}`;
  console.log("Fetching total runs leaderboard:", url);
  return apiRequest(url, origin);
}

// Unified leaderboard fetcher for the merged Players page. Picks the right
// JSON based on (view, sort, season, scope) and lets the caller render.
export async function fetchUnifiedLeaderboard(
  view: "player" | "character",
  sort: "time" | "runs",
  season: number | "all-time",
  scope: "global" | "regional" | "realm" | "class",
  opts?: {
    region?: string;
    realmSlug?: string;
    classKey?: string;
    page?: number;
  },
  origin?: string,
): Promise<any> {
  const url = `${API_BASE}${buildUnifiedLeaderboardPath(view, sort, season, scope,
 opts)}`;
  console.log("Fetching unified leaderboard:", url);
  return apiRequest(url, origin);
}

// account-grouped cross-season total runs (Player toggle on total-runs page)
export async function fetchAccountTotalRunsLeaderboard(
  scope: "global" | "regional" | "realm" = "global",
  region?: string,
  page: number = 1,
  pageSize: number = 25,
  opts?: { realmSlug?: string },
  origin?: string,
): Promise<any> {
  const url = `${API_BASE}${buildStaticAccountTotalRunsPath(scope, region, page, o
pts)}`;
  console.log("Fetching account total-runs leaderboard:", url);
  return apiRequest(url, origin);
}

// account-grouped player leaderboard
export async function fetchAccountLeaderboard(
  scope: "global" | "regional" | "realm" = "global",
  region?: string,
  page: number = 1,
  pageSize: number = 25,
  opts?: { seasonId?: number; realmSlug?: string },
  origin?: string,
): Promise<any> {
  const url = `${API_BASE}${buildStaticAccountLeaderboardPath(scope, region, page,
 opts)}`;
  console.log("Fetching account leaderboard:", url);
  return apiRequest(url, origin);
}

// player profile API functions
export async function fetchPlayerProfile(
  region: string,
  realmSlug: string,
  playerName: string,
  origin?: string,
): Promise<PlayerProfileData> {
  const url = `${API_BASE}${buildStaticPlayerProfilePath(region, realmSlug, player
Name)}`;
  console.log("Fetching player profile:", url);
  return apiRequest<PlayerProfileData>(url, origin);
}

// fetchPlayerBestRuns removed - best runs are now included in fetchPlayerProfile 
response

// leaderboard router function - determines which API to call based on filters
export async function fetchLeaderboard(
  region: string,
  realm: string,
  dungeonId: number,
  page: number = 1,
  teamFilter: boolean = true,
  origin?: string,
  seasonId: number = 1,
): Promise<LeaderboardData> {
  if (region === "global") {
    return fetchGlobalLeaderboard(
      dungeonId,
      page,
      teamFilter,
      origin,
      seasonId,
    );
  } else if (realm === "all") {
    return fetchRegionalLeaderboard(
      region,
      dungeonId,
      page,
      teamFilter,
      origin,
      seasonId,
    );
  } else {
    return fetchRealmLeaderboard(
      region,
      realm,
      dungeonId,
      page,
      teamFilter,
      origin,
      seasonId,
    );
  }
}

// url helpers are centralized in lib/utils.ts and re-exported here for convenienc
e
export { buildLeaderboardURL, buildPlayerProfileURL };

import type { LeaderboardData, PlayerProfile
Data, BestRun } from "./types.js";
import {
  buildLeaderboardURL,
  buildPlayerProfileURL,
  buildStaticLeaderboardPath,
  buildStaticPlayerLeaderboardPath,
  buildStaticPlayerProfilePath,
  buildStaticTotalRunsLeaderboardPath,
  buildStaticAccountLeaderboardPath,
  buildStaticAccountTotalRunsPath,
  buildUnifiedLeaderboardPath,
} from "./utils.js";

// api base configuration (always use relati
ve paths for Netlify/SSR)
const API_BASE = "";

// generic api request handler with error ha
ndling
async function apiRequest<T>(url: string, or
igin?: string): Promise<T> {
  let fullUrl: string;
  if (url.startsWith("http")) {
    fullUrl = url;
  } else if (origin) {
    // SSR context - need full URL
    fullUrl = `${origin}${url}`;
  } else {
    // Client-side context - relative URL wo
rks
    fullUrl = `${API_BASE}${url}`;
  }
  const response = await fetch(fullUrl);

  if (!response.ok) {
    if (response.status === 404) {
      throw new Error("Player not found");
    }

    throw new Error(
      `Failed to load data: ${response.statu
s} ${response.statusText}`,
    );
  }

  return response.json();
}

// leaderboard api functions
export async function fetchGlobalLeaderboard
(
  dungeonId: number,
  page: number = 1,
  teamFilter: boolean = true,
  origin?: string,
  seasonId: number = 1,
): Promise<LeaderboardData> {
  const url = `${API_BASE}${buildStaticLeade
rboardPath("global", "", dungeonId, page, se
asonId)}`;
  console.log("Fetching global leaderboard:"
, url);
  return apiRequest<LeaderboardData>(url, or
igin);
}

export async function fetchRegionalLeaderboa
rd(
  region: string,
  dungeonId: number,
  page: number = 1,
  teamFilter: boolean = true,
  origin?: string,
  seasonId: number = 1,
): Promise<LeaderboardData> {
  const url = `${API_BASE}${buildStaticLeade
rboardPath(region, "all", dungeonId, page, s
easonId)}`;
  console.log("Fetching regional leaderboard
:", url);
  return apiRequest<LeaderboardData>(url, or
igin);
}

export async function fetchRealmLeaderboard(
  region: string,
  realmSlug: string,
  dungeonId: number,
  page: number = 1,
  teamFilter: boolean = true,
  origin?: string,
  seasonId: number = 1,
): Promise<LeaderboardData> {
  const url = `${API_BASE}${buildStaticLeade
rboardPath(region, realmSlug, dungeonId, pag
e, seasonId)}`;
  console.log("Fetching realm leaderboard:",
 url);
  return apiRequest<LeaderboardData>(url, or
igin);
}

export async function fetchPlayerLeaderboard
(
  scope: "global" | "regional" | "realm" = "
global",
  region?: string,
  page: number = 1,
  pageSize: number = 25,
  opts?: { realmSlug?: string; classKey?: st
ring; seasonId?: number },
  origin?: string,
): Promise<any> {
  const url = `${API_BASE}${buildStaticPlaye
rLeaderboardPath(scope, region, page, opts)}
`;
  console.log("Fetching player leaderboard:"
, url);
  return apiRequest(url, origin);
}

export async function fetchTotalRunsLeaderbo
ard(
  scope: "global" | "regional" | "realm" = "
global",
  region?: string,
  page: number = 1,
  pageSize: number = 25,
  opts?: { realmSlug?: string; classKey?: st
ring },
  origin?: string,
): Promise<any> {
  const url = `${API_BASE}${buildStaticTotal
RunsLeaderboardPath(scope, region, page, opt
s)}`;
  console.log("Fetching total runs leaderboa
rd:", url);
  return apiRequest(url, origin);
}

// Unified leaderboard fetcher for the merge
d Players page. Picks the right
// JSON based on (view, sort, season, scope)
 and lets the caller render.
export async function fetchUnifiedLeaderboar
d(
  view: "player" | "character",
  sort: "time" | "runs",
  season: number | "all-time",
  scope: "global" | "regional" | "realm" | "
class",
  opts?: {
    region?: string;
    realmSlug?: string;
    classKey?: string;
    page?: number;
  },
  origin?: string,
): Promise<any> {
  const url = `${API_BASE}${buildUnifiedLead
erboardPath(view, sort, season, scope, opts)
}`;
  console.log("Fetching unified leaderboard:
", url);
  return apiRequest(url, origin);
}

// account-grouped cross-season total runs (
Player toggle on total-runs page)
export async function fetchAccountTotalRunsL
eaderboard(
  scope: "global" | "regional" | "realm" = "
global",
  region?: string,
  page: number = 1,
  pageSize: number = 25,
  opts?: { realmSlug?: string },
  origin?: string,
): Promise<any> {
  const url = `${API_BASE}${buildStaticAccou
ntTotalRunsPath(scope, region, page, opts)}`
;
  console.log("Fetching account total-runs l
eaderboard:", url);
  return apiRequest(url, origin);
}

// account-grouped player leaderboard
export async function fetchAccountLeaderboar
d(
  scope: "global" | "regional" | "realm" = "
global",
  region?: string,
  page: number = 1,
  pageSize: number = 25,
  opts?: { seasonId?: number; realmSlug?: st
ring },
  origin?: string,
): Promise<any> {
  const url = `${API_BASE}${buildStaticAccou
ntLeaderboardPath(scope, region, page, opts)
}`;
  console.log("Fetching account leaderboard:
", url);
  return apiRequest(url, origin);
}

// player profile API functions
export async function fetchPlayerProfile(
  region: string,
  realmSlug: string,
  playerName: string,
  origin?: string,
): Promise<PlayerProfileData> {
  const url = `${API_BASE}${buildStaticPlaye
rProfilePath(region, realmSlug, playerName)}
`;
  console.log("Fetching player profile:", ur
l);
  return apiRequest<PlayerProfileData>(url, 
origin);
}

// fetchPlayerBestRuns removed - best runs a
re now included in fetchPlayerProfile respon
se

// leaderboard router function - determines 
which API to call based on filters
export async function fetchLeaderboard(
  region: string,
  realm: string,
  dungeonId: number,
  page: number = 1,
  teamFilter: boolean = true,
  origin?: string,
  seasonId: number = 1,
): Promise<LeaderboardData> {
  if (region === "global") {
    return fetchGlobalLeaderboard(
      dungeonId,
      page,
      teamFilter,
      origin,
      seasonId,
    );
  } else if (realm === "all") {
    return fetchRegionalLeaderboard(
      region,
      dungeonId,
      page,
      teamFilter,
      origin,
      seasonId,
    );
  } else {
    return fetchRealmLeaderboard(
      region,
      realm,
      dungeonId,
      page,
      teamFilter,
      origin,
      seasonId,
    );
  }
}

// url helpers are centralized in lib/utils.
ts and re-exported here for convenience
export { buildLeaderboardURL, buildPlayerPro
fileURL };
 
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET