mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-22 10:08:11 +00:00
Integrated capacitor, adjusted the fetch urls and UI elements position to fit the safe area on mobile
This commit is contained in:
Generated
+25
-1
@@ -29,6 +29,7 @@
|
||||
"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",
|
||||
@@ -69,6 +70,7 @@
|
||||
"@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",
|
||||
@@ -8986,6 +8988,16 @@
|
||||
"@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",
|
||||
@@ -12005,6 +12017,19 @@
|
||||
"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",
|
||||
@@ -18887,7 +18912,6 @@
|
||||
"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"
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"@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",
|
||||
@@ -102,6 +103,7 @@
|
||||
"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",
|
||||
|
||||
@@ -20,7 +20,7 @@ export class DarkModeButton extends LitElement {
|
||||
return html`
|
||||
<button
|
||||
title="Toggle Dark Mode"
|
||||
class="absolute top-0 right-0 md:top-[10px] md:right-[10px] border-none bg-none cursor-pointer text-2xl"
|
||||
class="absolute top-0 safe-top right-0 md:top-[10px] md:right-[10px] border-none bg-none cursor-pointer text-2xl"
|
||||
@click=${() => this.toggleDarkMode()}
|
||||
>
|
||||
${this.darkMode ? "☀️" : "🌙"}
|
||||
|
||||
@@ -342,7 +342,7 @@ export class HostLobbyModal extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="start-game-button-container">
|
||||
<div class="start-game-button-container">
|
||||
<button
|
||||
@click=${this.startGame}
|
||||
?disabled=${this.players.length < 2}
|
||||
@@ -471,28 +471,25 @@ export class HostLobbyModal extends LitElement {
|
||||
}
|
||||
|
||||
private async putGameConfig() {
|
||||
const config = await getServerConfigFromClient();
|
||||
const response = await fetch(
|
||||
`${window.location.origin}/${config.workerPath(this.lobbyId)}/api/game/${this.lobbyId}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
gameMap: this.selectedMap,
|
||||
difficulty: this.selectedDifficulty,
|
||||
disableNPCs: this.disableNPCs,
|
||||
bots: this.bots,
|
||||
infiniteGold: this.infiniteGold,
|
||||
infiniteTroops: this.infiniteTroops,
|
||||
instantBuild: this.instantBuild,
|
||||
gameMode: this.gameMode,
|
||||
disabledUnits: this.disabledUnits,
|
||||
playerTeams: this.teamCount,
|
||||
} satisfies Partial<GameConfig>),
|
||||
const url = await buildGameUrl(this.lobbyId, "game");
|
||||
const response = await fetch(url, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
);
|
||||
body: JSON.stringify({
|
||||
gameMap: this.selectedMap,
|
||||
difficulty: this.selectedDifficulty,
|
||||
disableNPCs: this.disableNPCs,
|
||||
bots: this.bots,
|
||||
infiniteGold: this.infiniteGold,
|
||||
infiniteTroops: this.infiniteTroops,
|
||||
instantBuild: this.instantBuild,
|
||||
gameMode: this.gameMode,
|
||||
disabledUnits: this.disabledUnits,
|
||||
playerTeams: this.teamCount,
|
||||
} satisfies Partial<GameConfig>),
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -521,16 +518,13 @@ export class HostLobbyModal extends LitElement {
|
||||
`Starting private game with map: ${GameMapType[this.selectedMap]} ${this.useRandomMap ? " (Randomly selected)" : ""}`,
|
||||
);
|
||||
this.close();
|
||||
const config = await getServerConfigFromClient();
|
||||
const response = await fetch(
|
||||
`${window.location.origin}/${config.workerPath(this.lobbyId)}/api/start_game/${this.lobbyId}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
const url = await buildGameUrl(this.lobbyId, "start_game");
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
);
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -550,8 +544,8 @@ export class HostLobbyModal extends LitElement {
|
||||
}
|
||||
|
||||
private async pollPlayers() {
|
||||
const config = await getServerConfigFromClient();
|
||||
fetch(`/${config.workerPath(this.lobbyId)}/api/game/${this.lobbyId}`, {
|
||||
const url = await buildGameUrl(this.lobbyId, "game");
|
||||
fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -566,19 +560,16 @@ export class HostLobbyModal extends LitElement {
|
||||
}
|
||||
|
||||
async function createLobby(): Promise<GameInfo> {
|
||||
const config = await getServerConfigFromClient();
|
||||
try {
|
||||
const id = generateID();
|
||||
const response = await fetch(
|
||||
`/${config.workerPath(id)}/api/create_game/${id}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
// body: JSON.stringify(data), // Include this if you need to send data
|
||||
const url = await buildGameUrl(id, "create_game");
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
);
|
||||
// body: JSON.stringify(data), // Include this if you need to send data
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
@@ -593,3 +584,14 @@ async function createLobby(): Promise<GameInfo> {
|
||||
throw error; // Re-throw the error so the caller can handle it
|
||||
}
|
||||
}
|
||||
|
||||
export async function buildGameUrl(
|
||||
gameID: string,
|
||||
path: string,
|
||||
): Promise<string> {
|
||||
const config = await getServerConfigFromClient();
|
||||
|
||||
const apiPath = `/api/${path}/${gameID}`;
|
||||
const baseUrl = process.env.API_BASE_URL || "/";
|
||||
return `${baseUrl}${config.workerPath(gameID)}${apiPath}`;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,11 @@ import { customElement, query, state } from "lit/decorators.js";
|
||||
import { translateText } from "../client/Utils";
|
||||
import { GameInfo, GameRecord } 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 { buildGameUrl } from "./HostLobbyModal";
|
||||
import { JoinLobbyEvent } from "./Main";
|
||||
|
||||
@customElement("join-private-lobby-modal")
|
||||
export class JoinPrivateLobbyModal extends LitElement {
|
||||
@query("o-modal") private modalEl!: HTMLElement & {
|
||||
@@ -170,8 +171,7 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
}
|
||||
|
||||
private async checkActiveLobby(lobbyId: string): Promise<boolean> {
|
||||
const config = await getServerConfigFromClient();
|
||||
const url = `/${config.workerPath(lobbyId)}/api/game/${lobbyId}/exists`;
|
||||
const url = await buildGameUrl(lobbyId, "game/exists");
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
@@ -203,10 +203,9 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
}
|
||||
|
||||
private async checkArchivedGame(lobbyId: string): Promise<boolean> {
|
||||
const config = await getServerConfigFromClient();
|
||||
const archiveUrl = `/${config.workerPath(lobbyId)}/api/archived_game/${lobbyId}`;
|
||||
const url = await buildGameUrl(lobbyId, "archived_game");
|
||||
|
||||
const archiveResponse = await fetch(archiveUrl, {
|
||||
const archiveResponse = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
@@ -249,17 +248,14 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
|
||||
private async pollPlayers() {
|
||||
if (!this.lobbyIdInput?.value) return;
|
||||
const config = await getServerConfigFromClient();
|
||||
|
||||
fetch(
|
||||
`/${config.workerPath(this.lobbyIdInput.value)}/api/game/${this.lobbyIdInput.value}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
const url = await buildGameUrl(this.lobbyIdInput.value, "game");
|
||||
fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
)
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data: GameInfo) => {
|
||||
this.players = data.clients?.map((p) => p.username) ?? [];
|
||||
|
||||
@@ -56,7 +56,9 @@ export class PublicLobby extends LitElement {
|
||||
|
||||
async fetchLobbies(): Promise<GameInfo[]> {
|
||||
try {
|
||||
const response = await fetch(`/api/public_lobbies`);
|
||||
const response = await fetch(
|
||||
`${process.env.API_BASE_URL}/api/public_lobbies`,
|
||||
);
|
||||
if (!response.ok)
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
const data = await response.json();
|
||||
|
||||
@@ -204,7 +204,7 @@ export class ControlPanel extends LitElement implements Layer {
|
||||
</style>
|
||||
<div
|
||||
class="${this._isVisible
|
||||
? "w-[320px] text-sm lg:text-m bg-gray-800/70 p-2 pr-3 lg:p-4 shadow-lg lg:rounded-lg backdrop-blur"
|
||||
? "w-[320px] text-sm lg:text-m bg-gray-800/70 p-2 pr-3 lg:p-4 shadow-lg lg:rounded-lg backdrop-blur safe-pb"
|
||||
: "hidden"}"
|
||||
@contextmenu=${(e) => e.preventDefault()}
|
||||
>
|
||||
|
||||
@@ -153,7 +153,7 @@ export class OptionsMenu extends LitElement implements Layer {
|
||||
}
|
||||
return html`
|
||||
<div
|
||||
class="top-0 lg:top-4 right-0 lg:right-4 z-50 pointer-events-auto"
|
||||
class="top-0 lg:top-4 right-0 lg:right-4 z-50 pointer-events-auto safe-pt"
|
||||
@contextmenu=${(e) => e.preventDefault()}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -54,7 +54,7 @@ export class TopBar extends LitElement implements Layer {
|
||||
|
||||
return html`
|
||||
<div
|
||||
class="fixed top-0 z-50 bg-slate-800/40 backdrop-blur-sm shadow-xs text-white text-sm p-1 rounded-ee-sm lg:rounded grid grid-cols-1 sm:grid-cols-2 w-1/2 sm:w-2/3 md:w-1/2 lg:hidden"
|
||||
class="fixed top-0 safe-top z-50 bg-slate-800/40 backdrop-blur-sm shadow-xs text-white text-sm p-1 rounded-ee-sm lg:rounded grid grid-cols-1 sm:grid-cols-2 w-1/2 sm:w-2/3 md:w-1/2 lg:hidden"
|
||||
>
|
||||
<!-- Pop section (takes 2 columns on desktop) -->
|
||||
<div
|
||||
|
||||
+12
-9
@@ -2,7 +2,10 @@
|
||||
<html lang="en" class="h-full">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, viewport-fit=cover"
|
||||
/>
|
||||
<title>OpenFront (ALPHA)</title>
|
||||
<link rel="manifest" href="../../resources/manifest.json" />
|
||||
|
||||
@@ -97,7 +100,7 @@
|
||||
<body
|
||||
class="h-full select-none font-sans min-h-screen bg-opacity-0 bg-cover bg-center bg-fixed transition-opacity duration-300 ease-in-out flex flex-col"
|
||||
>
|
||||
<header class="l-header">
|
||||
<header class="l-header safe-pt">
|
||||
<div class="l-header__content">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -209,11 +212,11 @@
|
||||
secondary
|
||||
></o-button>
|
||||
<!-- <o-button
|
||||
id="chat-button"
|
||||
title="Chat Test"
|
||||
block
|
||||
secondary
|
||||
></o-button> -->
|
||||
id="chat-button"
|
||||
title="Chat Test"
|
||||
block
|
||||
secondary
|
||||
></o-button> -->
|
||||
</div>
|
||||
|
||||
<o-button
|
||||
@@ -240,7 +243,7 @@
|
||||
<button
|
||||
id="settings-button"
|
||||
title="Settings"
|
||||
class="fixed bottom-4 right-4 z-50 rounded-full p-2 shadow-lg transition-colors duration-300 flex items-center justify-center"
|
||||
class="fixed bottom-4 right-4 z-50 rounded-full p-2 shadow-lg transition-colors duration-300 flex items-center justify-center safe-mb"
|
||||
style="width: 80px; height: 80px; background-color: #0075ff"
|
||||
>
|
||||
<img
|
||||
@@ -287,7 +290,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Footer section -->
|
||||
<footer class="l-footer">
|
||||
<footer class="l-footer safe-pb">
|
||||
<div class="l-footer__content">
|
||||
<div class="l-footer__col">
|
||||
<a
|
||||
|
||||
@@ -17,6 +17,22 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Add safe-area offsets for iOS native mobile device */
|
||||
@supports (-webkit-touch-callout: none) {
|
||||
.safe-pt {
|
||||
padding-top: env(safe-area-inset-top);
|
||||
}
|
||||
.safe-pb {
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
.safe-top {
|
||||
top: env(safe-area-inset-top);
|
||||
}
|
||||
.safe-mb {
|
||||
margin-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add custom scrollbar styles */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
|
||||
@@ -62,6 +62,7 @@ export interface ServerConfig {
|
||||
cloudflareApiToken(): string;
|
||||
cloudflareConfigPath(): string;
|
||||
cloudflareCredsPath(): string;
|
||||
origin(): string;
|
||||
}
|
||||
|
||||
export interface NukeMagnitude {
|
||||
|
||||
@@ -29,7 +29,7 @@ export async function getServerConfigFromClient(): Promise<ServerConfig> {
|
||||
if (cachedSC) {
|
||||
return cachedSC;
|
||||
}
|
||||
const response = await fetch("/api/env");
|
||||
const response = await fetch(`${process.env.API_BASE_URL}/api/env`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
|
||||
@@ -85,6 +85,18 @@ export abstract class DefaultServerConfig implements ServerConfig {
|
||||
return process.env.CF_CREDS_PATH ?? "";
|
||||
}
|
||||
|
||||
origin(): string {
|
||||
const audience = this.jwtAudience();
|
||||
const subdomain = this.subdomain();
|
||||
if (audience === "localhost") {
|
||||
return "http://localhost:9000";
|
||||
}
|
||||
if (subdomain === "") {
|
||||
return `https://${audience}`;
|
||||
}
|
||||
return `https://${subdomain}.${audience}`;
|
||||
}
|
||||
|
||||
private publicKey: JWK;
|
||||
abstract jwtAudience(): string;
|
||||
jwtIssuer(): string {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { fileURLToPath } from "url";
|
||||
import { getServerConfigFromServer } from "../core/configuration/ConfigLoader";
|
||||
import { GameInfo } from "../core/Schemas";
|
||||
import { generateID } from "../core/Util";
|
||||
import { corsMiddleware } from "./cors";
|
||||
import { gatekeeper, LimiterType } from "./Gatekeeper";
|
||||
import { logger } from "./Logger";
|
||||
import { MapPlaylist } from "./MapPlaylist";
|
||||
@@ -22,7 +23,8 @@ const log = logger.child({ comp: "m" });
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
app.use(express.json());
|
||||
|
||||
app.use(corsMiddleware);
|
||||
app.use(
|
||||
express.static(path.join(__dirname, "../../static"), {
|
||||
maxAge: "1y", // Set max-age to 1 year for all static assets
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
import { CreateGameInputSchema, GameInputSchema } from "../core/WorkerSchemas";
|
||||
import { archive, readGameRecord } from "./Archive";
|
||||
import { Client } from "./Client";
|
||||
import { corsMiddleware } from "./cors";
|
||||
import { GameManager } from "./GameManager";
|
||||
import { gatekeeper, LimiterType } from "./Gatekeeper";
|
||||
import { getUserMe, verifyClientToken } from "./jwt";
|
||||
@@ -71,6 +72,7 @@ export function startWorker() {
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(corsMiddleware);
|
||||
app.set("trust proxy", 3);
|
||||
app.use(express.json());
|
||||
app.use(express.static(path.join(__dirname, "../../out")));
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import cors from "cors";
|
||||
import { getLocalIP } from "../../webpack.config";
|
||||
import { GameEnv } from "../core/configuration/Config";
|
||||
import { getServerConfigFromServer } from "../core/configuration/ConfigLoader";
|
||||
|
||||
const config = getServerConfigFromServer();
|
||||
const origin = config.origin();
|
||||
|
||||
const allowedOrigins = [
|
||||
origin,
|
||||
"capacitor://openfront.io",
|
||||
"https://openfront.io",
|
||||
];
|
||||
|
||||
if (config.env() === GameEnv.Dev) {
|
||||
const localIp = getLocalIP();
|
||||
if (localIp) {
|
||||
allowedOrigins.push(`http://${localIp}:9000`);
|
||||
}
|
||||
}
|
||||
|
||||
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.indexOf(origin) !== -1) {
|
||||
callback(null, true);
|
||||
} else {
|
||||
callback(new Error("Not allowed by CORS"));
|
||||
}
|
||||
},
|
||||
credentials: true,
|
||||
};
|
||||
|
||||
export const corsMiddleware = cors(corsOptions);
|
||||
@@ -4,6 +4,9 @@ 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.");
|
||||
}
|
||||
|
||||
@@ -2,10 +2,25 @@ import { execSync } from "child_process";
|
||||
import CopyPlugin from "copy-webpack-plugin";
|
||||
import ESLintPlugin from "eslint-webpack-plugin";
|
||||
import HtmlWebpackPlugin from "html-webpack-plugin";
|
||||
import os from "os";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import webpack from "webpack";
|
||||
|
||||
export function getLocalIP() {
|
||||
const interfaces = os.networkInterfaces();
|
||||
for (const interfaceName in interfaces) {
|
||||
const networkInterface = interfaces[interfaceName];
|
||||
if (!networkInterface) continue;
|
||||
for (const address of networkInterface) {
|
||||
if (address.family === "IPv4" && !address.internal) {
|
||||
return address.address;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "localhost";
|
||||
}
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
@@ -15,6 +30,12 @@ const gitCommit =
|
||||
export default async (env, argv) => {
|
||||
const isProduction = argv.mode === "production";
|
||||
|
||||
const apiBaseUrl = process.env.CAPACITOR_BUILD
|
||||
? isProduction && process.env.CAPACITOR_PRODUCTION_HOSTNAME
|
||||
? `https://${process.env.CAPACITOR_PRODUCTION_HOSTNAME}`
|
||||
: `http://${getLocalIP()}:9000`
|
||||
: "";
|
||||
|
||||
return {
|
||||
entry: "./src/client/Main.ts",
|
||||
output: {
|
||||
@@ -126,6 +147,10 @@ export default async (env, argv) => {
|
||||
),
|
||||
"process.env.GAME_ENV": JSON.stringify(isProduction ? "prod" : "dev"),
|
||||
"process.env.GIT_COMMIT": JSON.stringify(gitCommit),
|
||||
"process.env.API_BASE_URL": JSON.stringify(apiBaseUrl),
|
||||
"process.env.CAPACITOR_PRODUCTION_HOSTNAME": JSON.stringify(
|
||||
process.env.CAPACITOR_PRODUCTION_HOSTNAME,
|
||||
),
|
||||
}),
|
||||
new CopyPlugin({
|
||||
patterns: [
|
||||
@@ -134,6 +159,14 @@ export default async (env, argv) => {
|
||||
to: path.resolve(__dirname, "static"),
|
||||
noErrorOnMissing: true,
|
||||
},
|
||||
{
|
||||
from: path.resolve(
|
||||
__dirname,
|
||||
"node_modules/@capacitor/android/capacitor.js",
|
||||
),
|
||||
to: path.resolve(__dirname, "static/capacitor.js"),
|
||||
noErrorOnMissing: true,
|
||||
},
|
||||
],
|
||||
options: { concurrency: 100 },
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user