Fetch archived games from api, allow development against production & staging (#2045)

## Description:

Instead of going through the game server to fetch archived games, have
the client fetch from api directly. Also loosen up cors restrictions &
domain checks so localhost:9000 can talk to staging or production
servers

related to  #1571

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

## Please put your Discord username so you can be contacted if a bug or
regression is found:

evan
This commit is contained in:
evanpelle
2025-09-11 14:07:07 -07:00
committed by GitHub
parent 319508c360
commit eec3b0e2bb
7 changed files with 103 additions and 81 deletions
+68 -37
View File
@@ -1,12 +1,13 @@
import { LitElement, html } from "lit";
import { customElement, query, state } from "lit/decorators.js";
import { translateText } from "../client/Utils";
import { GameInfo, GameRecord } from "../core/Schemas";
import { GameInfo, GameRecordSchema } from "../core/Schemas";
import { generateID } from "../core/Util";
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
import { JoinLobbyEvent } from "./Main";
import "./components/baseComponents/Button";
import "./components/baseComponents/Modal";
import { getApiBase } from "./jwt";
@customElement("join-private-lobby-modal")
export class JoinPrivateLobbyModal extends LitElement {
@query("o-modal") private modalEl!: HTMLElement & {
@@ -179,10 +180,19 @@ export class JoinPrivateLobbyModal extends LitElement {
if (gameExists) return;
// If not active, check archived games
const archivedGame = await this.checkArchivedGame(lobbyId);
if (archivedGame) return;
this.message = `${translateText("private_lobby.not_found")}`;
switch (await this.checkArchivedGame(lobbyId)) {
case "success":
return;
case "not_found":
this.message = `${translateText("private_lobby.not_found")}`;
return;
case "version_mismatch":
this.message = `${translateText("private_lobby.version_mismatch")}`;
return;
case "error":
this.message = `${translateText("private_lobby.error")}`;
return;
}
} catch (error) {
console.error("Error checking lobby existence:", error);
this.message = `${translateText("private_lobby.error")}`;
@@ -222,49 +232,70 @@ export class JoinPrivateLobbyModal extends LitElement {
return false;
}
private async checkArchivedGame(lobbyId: string): Promise<boolean> {
const config = await getServerConfigFromClient();
const archiveUrl = `/${config.workerPath(lobbyId)}/api/archived_game/${lobbyId}`;
const archiveResponse = await fetch(archiveUrl, {
private async checkArchivedGame(
lobbyId: string,
): Promise<"success" | "not_found" | "version_mismatch" | "error"> {
const archivePromise = fetch(`${getApiBase()}/game/${lobbyId}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const gitCommitPromise = fetch(`/commit.txt`, {
method: "GET",
headers: { "Content-Type": "application/json" },
});
const archiveData = await archiveResponse.json();
const [archiveResponse, gitCommitResponse] = await Promise.all([
archivePromise,
gitCommitPromise,
]);
if (
archiveData.success === false &&
archiveData.error === "Version mismatch"
) {
if (archiveResponse.status === 404) {
return "not_found";
}
if (archiveResponse.status !== 200) {
return "error";
}
const archiveData = await archiveResponse.json();
const parsed = GameRecordSchema.safeParse(archiveData);
if (!parsed.success) {
return "version_mismatch";
}
let myGitCommit = "";
if (gitCommitResponse.status === 404) {
// commit.txt is not found when running locally
myGitCommit = "DEV";
} else if (gitCommitResponse.status === 200) {
myGitCommit = await gitCommitResponse.text();
} else {
console.error("Error getting git commit:", gitCommitResponse.status);
return "error";
}
// Allow DEV to join games created with a different version for debugging.
if (myGitCommit !== "DEV" && parsed.data.gitCommit !== myGitCommit) {
console.warn(
`Git commit hash mismatch for game ${lobbyId}`,
archiveData.details,
);
this.message =
"This game was created with a different version. Cannot join.";
return true;
return "version_mismatch";
}
if (archiveData.exists) {
const gameRecord = archiveData.gameRecord as GameRecord;
this.dispatchEvent(
new CustomEvent("join-lobby", {
detail: {
gameID: lobbyId,
gameRecord: gameRecord,
clientID: generateID(),
} as JoinLobbyEvent,
bubbles: true,
composed: true,
}),
);
return true;
}
return false;
this.dispatchEvent(
new CustomEvent("join-lobby", {
detail: {
gameID: lobbyId,
gameRecord: parsed.data,
clientID: generateID(),
} as JoinLobbyEvent,
bubbles: true,
composed: true,
}),
);
return "success";
}
private async pollPlayers() {
+12 -4
View File
@@ -17,9 +17,16 @@ function getAudience() {
export function getApiBase() {
const domainname = getAudience();
return domainname === "localhost"
? (localStorage.getItem("apiHost") ?? "http://localhost:8787")
: `https://api.${domainname}`;
if (domainname === "localhost") {
const apiDomain = process?.env?.API_DOMAIN;
if (apiDomain) {
return `https://${apiDomain}`;
}
return localStorage.getItem("apiHost") ?? "http://localhost:8787";
}
return `https://api.${domainname}`;
}
function getToken(): string | null {
@@ -159,7 +166,8 @@ function _isLoggedIn(): IsLoggedInResponse {
logOut();
return false;
}
if (aud !== getAudience()) {
const myAud = getAudience();
if (myAud !== "localhost" && aud !== myAud) {
// JWT was not issued for this website
console.error(
'unexpected "aud" claim value',