From 315a77c7d3dec69077805ec28cb03aac4be960a2 Mon Sep 17 00:00:00 2001 From: oleksandr-shysh Date: Fri, 20 Jun 2025 12:57:24 +0300 Subject: [PATCH] Replace fetch with CapacitorHTTP --- package-lock.json | 26 +----------- package.json | 2 - src/client/HostLobbyModal.ts | 27 +++++++------ src/client/JoinPrivateLobbyModal.ts | 19 ++++----- src/client/NewsModal.ts | 9 ++++- src/client/PublicLobby.ts | 12 +++--- src/client/jwt.ts | 28 ++++++------- src/core/configuration/Config.ts | 1 - src/core/configuration/ConfigLoader.ts | 54 ++++++++++++++++++++----- src/core/configuration/DefaultConfig.ts | 19 +++------ src/server/Master.ts | 2 - src/server/Worker.ts | 2 - src/server/cors.ts | 45 --------------------- tests/util/TestServerConfig.ts | 3 -- 14 files changed, 101 insertions(+), 148 deletions(-) delete mode 100644 src/server/cors.ts diff --git a/package-lock.json b/package-lock.json index 72950dc09..16d6e57ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,6 @@ "binary-loader": "^0.0.1", "colord": "^2.9.3", "copy-webpack-plugin": "^13.0.0", - "cors": "^2.8.5", "d3": "^7.9.0", "dompurify": "^3.1.7", "dotenv": "^16.5.0", @@ -73,7 +72,6 @@ "@eslint/compat": "^1.2.7", "@eslint/js": "^9.21.0", "@types/chai": "^4.3.17", - "@types/cors": "^2.8.19", "@types/d3": "^7.4.3", "@types/jest": "^30.0.0", "@types/jquery": "^3.5.31", @@ -9018,16 +9016,6 @@ "@types/node": "*" } }, - "node_modules/@types/cors": { - "version": "2.8.19", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", - "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/d3": { "version": "7.4.3", "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", @@ -12047,19 +12035,6 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "license": "MIT" }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/cosmiconfig": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", @@ -18942,6 +18917,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" diff --git a/package.json b/package.json index 8b084fa64..94ece838b 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "@eslint/compat": "^1.2.7", "@eslint/js": "^9.21.0", "@types/chai": "^4.3.17", - "@types/cors": "^2.8.19", "@types/d3": "^7.4.3", "@types/jest": "^30.0.0", "@types/jquery": "^3.5.31", @@ -106,7 +105,6 @@ "binary-loader": "^0.0.1", "colord": "^2.9.3", "copy-webpack-plugin": "^13.0.0", - "cors": "^2.8.5", "d3": "^7.9.0", "dompurify": "^3.1.7", "dotenv": "^16.5.0", diff --git a/src/client/HostLobbyModal.ts b/src/client/HostLobbyModal.ts index b079925ee..b543afd6a 100644 --- a/src/client/HostLobbyModal.ts +++ b/src/client/HostLobbyModal.ts @@ -1,3 +1,4 @@ +import { CapacitorHttp } from "@capacitor/core"; import { LitElement, html } from "lit"; import { customElement, query, state } from "lit/decorators.js"; import randomMap from "../../resources/images/RandomMap.webp"; @@ -472,12 +473,12 @@ export class HostLobbyModal extends LitElement { private async putGameConfig() { const url = await buildGameUrl(this.lobbyId, "game"); - const response = await fetch(url, { - method: "PUT", + const response = await CapacitorHttp.put({ + url: url, headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ + data: JSON.stringify({ gameMap: this.selectedMap, difficulty: this.selectedDifficulty, disableNPCs: this.disableNPCs, @@ -519,8 +520,8 @@ export class HostLobbyModal extends LitElement { ); this.close(); const url = await buildGameUrl(this.lobbyId, "start_game"); - const response = await fetch(url, { - method: "POST", + const response = await CapacitorHttp.post({ + url, headers: { "Content-Type": "application/json", }, @@ -545,13 +546,13 @@ export class HostLobbyModal extends LitElement { private async pollPlayers() { const url = await buildGameUrl(this.lobbyId, "game"); - fetch(url, { - method: "GET", + CapacitorHttp.get({ + url, headers: { "Content-Type": "application/json", }, }) - .then((response) => response.json()) + .then((response) => response.data) .then((data: GameInfo) => { console.log(`got game info response: ${JSON.stringify(data)}`); this.players = data.clients?.map((p) => p.username) ?? []; @@ -563,19 +564,19 @@ async function createLobby(): Promise { try { const id = generateID(); const url = await buildGameUrl(id, "create_game"); - const response = await fetch(url, { - method: "POST", + const response = await CapacitorHttp.post({ + url, headers: { "Content-Type": "application/json", }, - // body: JSON.stringify(data), // Include this if you need to send data + // data: JSON.stringify(data), // Include this if you need to send data }); - if (!response.ok) { + if (!response.data) { throw new Error(`HTTP error! status: ${response.status}`); } - const data = await response.json(); + const data = response.data; console.log("Success:", data); return data as GameInfo; diff --git a/src/client/JoinPrivateLobbyModal.ts b/src/client/JoinPrivateLobbyModal.ts index 3b50238a5..d541b1100 100644 --- a/src/client/JoinPrivateLobbyModal.ts +++ b/src/client/JoinPrivateLobbyModal.ts @@ -1,3 +1,4 @@ +import { CapacitorHttp } from "@capacitor/core"; import { LitElement, html } from "lit"; import { customElement, query, state } from "lit/decorators.js"; import { translateText } from "../client/Utils"; @@ -173,12 +174,12 @@ export class JoinPrivateLobbyModal extends LitElement { private async checkActiveLobby(lobbyId: string): Promise { const url = await buildGameUrl(lobbyId, "exists"); - const response = await fetch(url, { - method: "GET", + const response = await CapacitorHttp.get({ + url, headers: { "Content-Type": "application/json" }, }); - const gameInfo = await response.json(); + const gameInfo = response.data; if (gameInfo.exists) { this.message = translateText("private_lobby.joined_waiting"); @@ -205,12 +206,12 @@ export class JoinPrivateLobbyModal extends LitElement { private async checkArchivedGame(lobbyId: string): Promise { const url = await buildGameUrl(lobbyId, "archived_game"); - const archiveResponse = await fetch(url, { - method: "GET", + const archiveResponse = await CapacitorHttp.get({ + url, headers: { "Content-Type": "application/json" }, }); - const archiveData = await archiveResponse.json(); + const archiveData = archiveResponse.data; if ( archiveData.success === false && @@ -250,13 +251,13 @@ export class JoinPrivateLobbyModal extends LitElement { if (!this.lobbyIdInput?.value) return; const url = await buildGameUrl(this.lobbyIdInput.value, "game"); - fetch(url, { - method: "GET", + CapacitorHttp.get({ + url, headers: { "Content-Type": "application/json", }, }) - .then((response) => response.json()) + .then((response) => JSON.parse(response.data)) .then((data: GameInfo) => { this.players = data.clients?.map((p) => p.username) ?? []; }) diff --git a/src/client/NewsModal.ts b/src/client/NewsModal.ts index 5469bf093..074012d39 100644 --- a/src/client/NewsModal.ts +++ b/src/client/NewsModal.ts @@ -1,3 +1,4 @@ +import { CapacitorHttp } from "@capacitor/core"; import { LitElement, css, html } from "lit"; import { resolveMarkdown } from "lit-markdown"; import { customElement, property, query } from "lit/decorators.js"; @@ -87,8 +88,12 @@ export class NewsModal extends LitElement { public open() { if (!this.initialized) { this.initialized = true; - fetch(changelog) - .then((response) => (response.ok ? response.text() : "Failed to load")) + CapacitorHttp.get({ + url: `${process.env.APP_BASE_URL || ""}${changelog}`, + }) + .then((response) => + response.status === 200 ? response.data : "Failed to load", + ) .then((markdown) => (this.markdown = markdown)); } this.requestUpdate(); diff --git a/src/client/PublicLobby.ts b/src/client/PublicLobby.ts index 7f981d2a2..1d464e080 100644 --- a/src/client/PublicLobby.ts +++ b/src/client/PublicLobby.ts @@ -1,3 +1,4 @@ +import { CapacitorHttp } from "@capacitor/core"; import { LitElement, html } from "lit"; import { customElement, state } from "lit/decorators.js"; import { translateText } from "../client/Utils"; @@ -56,12 +57,13 @@ export class PublicLobby extends LitElement { async fetchLobbies(): Promise { try { - const response = await fetch( - `${process.env.APP_BASE_URL || ""}/api/public_lobbies`, - ); - if (!response.ok) + const response = await CapacitorHttp.get({ + url: `${process.env.APP_BASE_URL || ""}/api/public_lobbies`, + }); + + if (response.status !== 200) throw new Error(`HTTP error! status: ${response.status}`); - const data = await response.json(); + const data = JSON.parse(response.data); return data.lobbies; } catch (error) { console.error("Error fetching lobbies:", error); diff --git a/src/client/jwt.ts b/src/client/jwt.ts index a84464f48..c3fcc79dc 100644 --- a/src/client/jwt.ts +++ b/src/client/jwt.ts @@ -1,6 +1,6 @@ import { App } from "@capacitor/app"; import { Browser } from "@capacitor/browser"; -import { Capacitor } from "@capacitor/core"; +import { Capacitor, CapacitorHttp } from "@capacitor/core"; import { decodeJwt } from "jose"; import { z } from "zod/v4"; import { @@ -139,17 +139,14 @@ export async function logOut(allSessions: boolean = false) { localStorage.removeItem("token"); __isLoggedIn = false; - const response = await fetch( - getApiBase() + (allSessions ? "/revoke" : "/logout"), - { - method: "POST", - headers: { - authorization: `Bearer ${token}`, - }, + const response = await CapacitorHttp.post({ + url: getApiBase() + (allSessions ? "/revoke" : "/logout"), + headers: { + authorization: `Bearer ${token}`, }, - ); + }); - if (response.ok === false) { + if (response.status !== 200) { console.error("Logout failed", response); return false; } @@ -254,8 +251,8 @@ export async function postRefresh(): Promise { if (!token) return false; // Refresh the JWT - const response = await fetch(getApiBase() + "/refresh", { - method: "POST", + const response = await CapacitorHttp.post({ + url: getApiBase() + "/refresh", headers: { authorization: `Bearer ${token}`, }, @@ -266,7 +263,7 @@ export async function postRefresh(): Promise { return false; } if (response.status !== 200) return false; - const body = await response.json(); + const body = response.data; const result = RefreshResponseSchema.safeParse(body); if (!result.success) { const error = z.prettifyError(result.error); @@ -287,7 +284,8 @@ export async function getUserMe(): Promise { if (!token) return false; // Get the user object - const response = await fetch(getApiBase() + "/users/@me", { + const response = await CapacitorHttp.get({ + url: getApiBase() + "/users/@me", headers: { authorization: `Bearer ${token}`, }, @@ -298,7 +296,7 @@ export async function getUserMe(): Promise { return false; } if (response.status !== 200) return false; - const body = await response.json(); + const body = response.data; const result = UserMeResponseSchema.safeParse(body); if (!result.success) { const error = z.prettifyError(result.error); diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts index e77f96d0b..8f4cca4c5 100644 --- a/src/core/configuration/Config.ts +++ b/src/core/configuration/Config.ts @@ -62,7 +62,6 @@ export interface ServerConfig { cloudflareApiToken(): string; cloudflareConfigPath(): string; cloudflareCredsPath(): string; - origin(): string; } export interface NukeMagnitude { diff --git a/src/core/configuration/ConfigLoader.ts b/src/core/configuration/ConfigLoader.ts index 989bff484..dd8e70912 100644 --- a/src/core/configuration/ConfigLoader.ts +++ b/src/core/configuration/ConfigLoader.ts @@ -1,3 +1,4 @@ +import { CapacitorHttp } from "@capacitor/core"; import { UserSettings } from "../game/UserSettings"; import { GameConfig } from "../Schemas"; import { Config, GameEnv, ServerConfig } from "./Config"; @@ -29,19 +30,52 @@ export async function getServerConfigFromClient(): Promise { if (cachedSC) { return cachedSC; } - const response = await fetch(`${process.env.APP_BASE_URL || ""}/api/env`); - if (!response.ok) { - throw new Error( - `Failed to fetch server config: ${response.status} ${response.statusText}`, + try { + const response = await CapacitorHttp.get({ + url: `${process.env.APP_BASE_URL || ""}/api/env`, + headers: { + "Content-Type": "application/json", + }, + }); + + if (!response.data) { + throw new Error(`Failed to fetch server config: ${response.status}`); + } + + // Check if response is HTML (error case) + const dataStr = + typeof response.data === "string" + ? response.data + : JSON.stringify(response.data); + if (dataStr.includes("") || dataStr.includes("([origin]); - -switch (config.env()) { - case GameEnv.Prod: - allowedOriginsSet.add("capacitor://openfront.io"); - allowedOriginsSet.add("https://openfront.io"); - break; - case GameEnv.Preprod: - allowedOriginsSet.add("capacitor://openfront.dev"); - allowedOriginsSet.add("https://openfront.dev"); - break; - case GameEnv.Dev: { - allowedOriginsSet.add("capacitor://localhost"); - allowedOriginsSet.add("http://localhost"); - allowedOriginsSet.add("http://localhost:8787"); - break; - } -} - -const allowedOrigins = Array.from(allowedOriginsSet); - -const corsOptions = { - origin: ( - origin: string | undefined, - callback: (err: Error | null, allow?: boolean) => void, - ) => { - // allow requests with no origin (like mobile apps or curl requests) - if (!origin) return callback(null, true); - if (allowedOrigins.some((o) => origin.startsWith(o))) { - callback(null, true); - } else { - callback(new Error("Not allowed by CORS")); - } - }, - credentials: true, -}; - -export const corsMiddleware = cors(corsOptions); diff --git a/tests/util/TestServerConfig.ts b/tests/util/TestServerConfig.ts index b011430ec..be5155d5a 100644 --- a/tests/util/TestServerConfig.ts +++ b/tests/util/TestServerConfig.ts @@ -4,9 +4,6 @@ import { GameMapType } from "../../src/core/game/Game"; import { GameID } from "../../src/core/Schemas"; export class TestServerConfig implements ServerConfig { - origin(): string { - return "unused"; - } cloudflareConfigPath(): string { throw new Error("Method not implemented."); }