feat(waiter): configurable server/venue layer for native app and plain-HTTP browser mode
New waiter_pwa/src/config/server.js is the single place that knows where the backend is. Served by the venue's server (https domain or http://<LAN IP>) nothing changes: same-origin URLs and the original storage keys, so existing installs keep their token and unsynced offline queue. With an active venue (native app, or dev builds with VITE_SERVER_URL) URLs become absolute and all venue data is namespaced by siteId: token/savedUsername keys, the Dexie DB (pos_snapshot__<siteId>, which also covers the WS cursor), favorites and table-view prefs. Switching venue reloads the app. - api client baseURL, WebSocket and SSE URLs routed through the layer - product images / waiter avatars rendered via assetUrl() - service-worker update prompt skipped in native builds - InstallAppBanner: shown only in plain-HTTP browser mode and only when VITE_APP_DOWNLOAD_URL is set at build time (dismiss for 7 days) - VITE_SERVER_URL override is DEV-only (a URL-controlled server in prod would let a crafted link capture PINs) - pack README: rule CS-8 on never assuming same-origin Verified with Playwright/Edge against a local backend: prod build same-origin, prod build via LAN IP over plain HTTP (insecure context, no SW, banner shown), and dev build pointed at the backend by URL - all three log in, reach /tables and receive the WebSocket 'ready' frame; storage keys and IndexedDB names are as expected. Lint: no new problems (103 before/after). Build passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { apiBase } from '../config/server'
|
||||
|
||||
const INITIAL_RECONNECT_DELAY = 3000
|
||||
const MAX_RECONNECT_DELAY = 30000
|
||||
@@ -38,7 +39,7 @@ export function useSSE({ token, onEvent, onConnect, onDisconnect, enabled = true
|
||||
esRef.current = null
|
||||
}
|
||||
|
||||
const url = `/api/sse/stream?token=${encodeURIComponent(token)}`
|
||||
const url = `${apiBase()}/api/sse/stream?token=${encodeURIComponent(token)}`
|
||||
const es = new EventSource(url)
|
||||
esRef.current = es
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import db from '../db/posdb'
|
||||
import { wsUrl } from '../config/server'
|
||||
|
||||
const INITIAL_RECONNECT_DELAY = 2_000
|
||||
const MAX_RECONNECT_DELAY = 30_000
|
||||
@@ -71,9 +72,8 @@ export function useWebSocket({ token, onEvent, onConnect, onDisconnect, enabled
|
||||
wsRef.current = null
|
||||
}
|
||||
|
||||
// Build WS URL — same host, swap http(s) → ws(s)
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||
const url = `${protocol}//${window.location.host}/api/ws/connect?token=${encodeURIComponent(token)}`
|
||||
// Same host as the page (web) or the active venue's server (native)
|
||||
const url = wsUrl(`/api/ws/connect?token=${encodeURIComponent(token)}`)
|
||||
|
||||
const ws = new WebSocket(url)
|
||||
wsRef.current = ws
|
||||
|
||||
Reference in New Issue
Block a user