Env-driven dev ports + splash as the front door

vite.config.ts now reads WEB_PORT (Vite) and PORT (API) from .env via
loadEnv, with the /api proxy following PORT and strictPort on — so two
branch checkouts can each run `npm run dev` on their own ports.

Play now always lands on the splash at the bare root instead of silently
resuming an active playthrough; a Resume action continues one when present.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-22 15:43:08 +02:00
co-authored by Claude Opus 4.8
parent 34aa23237e
commit cd4b8bf4fa
3 changed files with 32 additions and 10 deletions
+9
View File
@@ -1,9 +1,18 @@
DATABASE_URL=postgres://osint:osint_secret@localhost:5433/osint_dev
# Dev ports — give each branch checkout distinct values to run them side by side.
# PORT is the Express API; WEB_PORT is the Vite dev server, which proxies /api to PORT.
PORT=8787
WEB_PORT=5173
CORS_ORIGIN=http://localhost:5173
LEVEL_EDITING_ENABLED=true
JWT_SECRET=osint-local-dev-secret
MAX_DOCUMENT_BYTES=26214400
OCR_ENABLED=true
OCR_LANGUAGES=nor+eng
OCR_TIMEOUT_MS=20000
MAX_OCR_BYTES=15728640
MAX_EXTRACTED_TEXT_CHARACTERS=200000
# Game asset storage (MinIO). Start it with: docker compose -f docker-compose.dev.yml up -d minio createbuckets
S3_ENDPOINT=http://localhost:9000
+7 -3
View File
@@ -7,6 +7,7 @@ import { CutsceneHost, DialoguePlayer, SplashScreen, type PlaythroughState } fro
// board route (/level/<clone id>), which App renders.
export function Play() {
const [state, setState] = useState<PlaythroughState | null>(null)
const [resumable, setResumable] = useState<PlaythroughState | null>(null)
const [splash, setSplash] = useState(false)
const [busy, setBusy] = useState(false)
const [status, setStatus] = useState('')
@@ -37,10 +38,13 @@ export function Play() {
if (!cancelled) res.ok ? apply(await res.json()) : setStatus('NODE NOT FOUND')
return
}
// The bare root is always the front door: show the splash, and offer Resume
// when a playthrough is already in progress rather than auto-resuming into it.
const res = await fetch('/api/playthroughs/current')
if (cancelled) return
if (res.status === 204) { setSplash(true); setStatus('AWAITING PRINCIPAL INVESTIGATOR'); return }
res.ok ? apply(await res.json()) : setSplash(true)
if (res.ok && res.status !== 204) setResumable(await res.json())
setSplash(true)
setStatus('AWAITING PRINCIPAL INVESTIGATOR')
})()
return () => { cancelled = true }
}, [apply])
@@ -62,7 +66,7 @@ export function Play() {
if (res.ok) apply(await res.json())
}
if (splash) return <SplashScreen hasResume={false} busy={busy} status={status} onNewGame={newGame} onResume={() => {}} />
if (splash) return <SplashScreen hasResume={!!resumable} busy={busy} status={status} onNewGame={newGame} onResume={() => { if (resumable) apply(resumable) }} />
const node = state?.node
if (!node) return <div className="boot"><div className="seal">GU</div><p>GLITCH UNIVERSITY NETWORK TERMINAL</p><small>{status || 'OPENING CASE FILE…'}</small></div>
+13 -4
View File
@@ -1,10 +1,19 @@
import { defineConfig } from 'vite'
import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
// Ports are read from .env so two branch checkouts can run `npm run dev` side by
// side: give each folder its own WEB_PORT (Vite) and PORT (API). The /api proxy
// follows PORT so the web server always talks to its own backend.
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '')
const webPort = Number(env.WEB_PORT || 5173)
const apiPort = Number(env.PORT || 8787)
return {
plugins: [react()],
server: {
port: 5173,
proxy: { '/api': 'http://127.0.0.1:8787' },
port: webPort,
strictPort: true, // fail loudly on a clash instead of silently picking another port
proxy: { '/api': `http://127.0.0.1:${apiPort}` },
},
}
})