Files
dodo/frontend/scripts/playwright-mobile-server.mjs
T
bboysoul 6c234d7d82
ci / gitleaks (push) Successful in 1m19s
ci / docker (push) Successful in 5m48s
feat: strengthen backup and mobile workflows
2026-09-16 21:12:52 +08:00

58 lines
2.5 KiB
JavaScript

import { spawn } from 'node:child_process'
import { createWriteStream, mkdirSync, writeFileSync } from 'node:fs'
import path from 'node:path'
const frontend = process.cwd()
const root = path.resolve(frontend, '..')
const project = process.env.DODO_E2E_PROJECT
if (!project) throw new Error('DODO_E2E_PROJECT is required')
const runtimeBase = path.join(frontend, 'playwright-runtime', project)
const runtime = path.join(runtimeBase, `run-${new Date().toISOString().replace(/[:.]/g, '-')}-${process.pid}`)
const attachments = path.join(runtime, 'attachments')
const staging = path.join(runtime, 'backup-staging')
mkdirSync(attachments, { recursive: true })
mkdirSync(staging, { recursive: true })
writeFileSync(path.join(runtimeBase, 'latest.json'), JSON.stringify({ runtime, database: path.join(runtime, 'dodo.sqlite3'), attachments, staging }, null, 2))
writeFileSync(path.join(runtime, 'runtime.json'), JSON.stringify({ database: path.join(runtime, 'dodo.sqlite3'), attachments, staging }, null, 2))
const logs = {
backend: createWriteStream(path.join(runtime, 'backend.log'), { flags: 'a' }),
frontend: createWriteStream(path.join(runtime, 'frontend.log'), { flags: 'a' }),
}
const children = []
function start(command, args, options, log) {
const child = spawn(command, args, { ...options, stdio: ['ignore', 'pipe', 'pipe'] })
child.stdout.pipe(log)
child.stderr.pipe(log)
children.push(child)
return child
}
start(path.join(root, '.venv/bin/uvicorn'), ['backend.main:app', '--host', '127.0.0.1', '--port', '8781'], {
cwd: root,
env: {
...process.env,
DODO_DATABASE_URL: `sqlite+aiosqlite:///${path.join(runtime, 'dodo.sqlite3')}`,
DODO_AUTO_CREATE_SCHEMA: 'true',
DODO_COOKIE_SECURE: 'false',
DODO_ATTACHMENT_DIR: attachments,
DODO_BACKUP_STAGING_DIR: staging,
},
}, logs.backend)
start('pnpm', ['exec', 'vite', '--host', '127.0.0.1', '--port', '5173', '--strictPort'], { cwd: frontend, env: process.env }, logs.frontend)
let stopping = false
function stop(signal = 'SIGTERM') {
if (stopping) return
stopping = true
for (const child of children) if (!child.killed) child.kill(signal)
setTimeout(() => { for (const child of children) if (!child.killed) child.kill('SIGKILL') }, 3000).unref()
}
process.on('SIGTERM', () => stop())
process.on('SIGINT', () => stop())
process.on('exit', () => stop())
await Promise.all(children.map(child => new Promise((resolve, reject) => {
child.once('exit', (code, signal) => stopping ? resolve() : reject(new Error(`server exited code=${code} signal=${signal}`)))
})))