doxa-maps/deployment/cloud/vite.cloud.config.mjs
Doxa Maps 18152f0387
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
Cloud container: the site serves at once while Rudder sets up, and its model is in the image
Rudder's setup ran before Vite and the sign-in proxy, so the whole site answered 502 for minutes on every
start while the engine built and ~742 MB downloaded; on the first start the download was reset mid-file and
Rudder stayed offline. Now the runtime and pinned model are baked into an early image layer (SHA-256
checked, resumed and retried on a stall), setup runs in the background, /api/rudder answers "starting"
meanwhile, and the dev server restarts once when setup ends so the engine starts.
2026-09-27 22:32:22 +00:00

59 lines
2.5 KiB
JavaScript

// Overlay for the cloud dev server: the repo's own vite.config.js plus what a shared, public
// host needs. Every value comes from the environment (see deployment/cloud/README.md).
import base from '/app/repo/vite.config.js'
import { mergeConfig } from 'vite'
import { readFileSync } from 'node:fs'
const env = process.env
const SETUP_STATE = '/tmp/rudder-setup.state' // written by entry.sh: running -> done | failed
const setupState = () => { try { return readFileSync(SETUP_STATE, 'utf8').trim() } catch { return '' } }
// Rudder's setup runs in the background (entry.sh), so the site serves before the engine exists. While it
// runs, /api/rudder answers "starting" (the widget shows "Loading speech model…"; /events is held open so it
// does not retry in a loop). When setup ends, the dev server restarts ONCE: the repo's backend plugin only
// looks for the engine binary at start, and the restart is what starts it. 'pre' puts this ahead of that
// plugin's own "engine offline" answer.
const rudderStarting = {
name: 'cloud-rudder-starting',
enforce: 'pre',
apply: 'serve',
configureServer(server) {
if (setupState() !== 'running') return
server.middlewares.use('/api/rudder', (req, res, next) => {
if (setupState() !== 'running') return next()
const status = { engine: 'starting', error: '', audio: 'browser' }
if ((req.url || '').startsWith('/events')) {
res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', Connection: 'keep-alive' })
res.write(`data: ${JSON.stringify({ type: 'status', status })}\n\n`)
return
}
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify(status))
})
const poll = setInterval(() => {
if (setupState() === 'running') return
clearInterval(poll)
server.config.logger.info(`[rudder] setup ${setupState() || 'ended'}: restarting the dev server to start the engine`)
server.restart()
}, 2000)
server.httpServer?.once('close', () => clearInterval(poll))
},
}
export default async (e) => {
const b = typeof base === 'function' ? await base(e) : base
const hmr = env.HMR_CLIENT_PORT
? { clientPort: Number(env.HMR_CLIENT_PORT), protocol: env.HMR_PROTOCOL || 'wss' }
: undefined
return mergeConfig(b, {
plugins: [rudderStarting],
cacheDir: '/tmp/vite-cache',
server: {
host: env.VITE_HOST || '0.0.0.0',
port: Number(env.VITE_PORT || 5173),
strictPort: true,
allowedHosts: true,
...(hmr ? { hmr } : {}),
},
})
}