Merge pull request #200 from KoalaTrapLord/fix/serve-lan-client-from-backend

fix: Serve lan-client from backend at /lan (same-origin)
This commit is contained in:
KoalaTrapLord authored and GitHub committed 2026-04-03 10:48:01 -04:00
commit 370d4cd92f
3 files changed
+38 -30

No files matched your search

+10
View File
@@ -152,8 +152,18 @@ async def health_check():
# ── Static UI (MUST come after all @app.get routes) ──────────────────────────────────────────
from pathlib import Path as _Path
_legacy_ui_dir = _Path(__file__).parent / "ui"
_lan_client_dir = _Path(__file__).parent / "lan-client"
_precon_decks_dir = _Path(__file__).parent / "precon-decks"
_spa_dir = _Path(__file__).parent / "frontend" / "commander-ai-lab-ui" / "dist"
# Mount LAN client at /lan (served before the root catch-all)
if _lan_client_dir.exists():
app.mount("/lan", StaticFiles(directory=str(_lan_client_dir), html=True), name="lan-client")
# Mount precon-decks/ so .dck files are fetchable at /precon-decks/{file}.dck
if _precon_decks_dir.exists():
app.mount("/precon-decks", StaticFiles(directory=str(_precon_decks_dir)), name="precon-decks")
if _legacy_ui_dir.exists():
app.mount("/", StaticFiles(directory=str(_legacy_ui_dir), html=True), name="ui")
elif _spa_dir.exists():
+14 -3
View File
@@ -10,9 +10,10 @@ const AI_BRIDGE_VERSION = '1.0.0';
// ============================================================
const AI_BRIDGE_CONFIG = {
// Backend URL — defaults to localhost:8080, configurable via settings
backendUrl: 'http://localhost:8080',
wsUrl: 'ws://localhost:8080',
// Backend URL — empty string = same origin (recommended when served from backend)
// Falls back to localhost:8080 if served from a different origin
backendUrl: '',
wsUrl: '',
// Timeouts (ms)
healthCheckTimeout: 3000,
@@ -39,6 +40,16 @@ const AI_BRIDGE_CONFIG = {
class BackendConnector {
constructor(config = {}) {
this.config = { ...AI_BRIDGE_CONFIG, ...config };
// Derive URLs from current page origin when empty (same-origin mode)
if (!this.config.backendUrl) {
this.config.backendUrl = window.location.origin; // e.g. http://localhost:8080
}
if (!this.config.wsUrl) {
const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
this.config.wsUrl = `${proto}//${window.location.host}`;
}
this._connected = false;
this._modelLoaded = false;
this._ws = null;
+14 -27
View File
@@ -12723,39 +12723,26 @@ async function loadPreconForPlayer(playerIdx, preconId) {
var player = gameState.players[playerIdx];
if (!player) return;
// Try to get decklist text: embedded first, then fetch from backend .dck file
// Try to get decklist text: embedded first, then fetch .dck from server
var decklistText = precon.decklist || '';
if (!decklistText && precon.fileName) {
// Fetch .dck file from backend if available
if (typeof AIBridge !== 'undefined' && AIBridge.connector.connected) {
try {
var deckData = await AIBridge.deckSource.fetchPreconDeck(precon.fileName);
if (deckData && deckData.decklist) {
decklistText = deckData.decklist;
} else if (deckData && typeof deckData === 'string') {
decklistText = deckData;
}
} catch (e) {
console.warn('[loadPreconForPlayer] Backend deck fetch failed:', e);
// 1. Try the API endpoint (returns JSON with decklist field)
try {
var apiResp = await fetch('/api/lab/precons/deck?fileName=' + encodeURIComponent(precon.fileName));
if (apiResp.ok) {
var deckData = await apiResp.json();
if (deckData && deckData.decklist) decklistText = deckData.decklist;
}
} catch (e) {
console.warn('[loadPreconForPlayer] API deck fetch failed:', e);
}
// Fallback: try to fetch .dck file directly from precon-decks/ (same origin)
// 2. Fallback: fetch .dck file directly from static mount
if (!decklistText) {
try {
var resp = await fetch('precon-decks/' + precon.fileName);
if (resp.ok) {
decklistText = await resp.text();
}
} catch (e) {
// Fallback: try relative to backend
try {
var resp2 = await fetch('/precon-decks/' + precon.fileName);
if (resp2.ok) {
decklistText = await resp2.text();
}
} catch (e2) {
console.warn('[loadPreconForPlayer] Could not fetch .dck file:', precon.fileName);
}
var staticResp = await fetch('/precon-decks/' + encodeURIComponent(precon.fileName));
if (staticResp.ok) decklistText = await staticResp.text();
} catch (e2) {
console.warn('[loadPreconForPlayer] Static .dck fetch failed:', precon.fileName);
}
}
}