Loopback Fastify auth-broker + esbuild SPA. Operator password login (constant-time, signed HttpOnly session cookie, per-forwarded-IP rate-limit) → session-gated proxy that mints a fresh 60s manage capability token per call to the control-plane admin API: list hosts, mint pairing codes (with QR + pair command), revoke hosts. Security headers + CSP, CP_URL pinned loopback (anti-SSRF), hostId dot-segment guard. 55 tests pass; security-reviewed. Deployed behind nginx panel.terminal.yaojia.wang.
38 lines
1.2 KiB
JavaScript
38 lines
1.2 KiB
JavaScript
/**
|
|
* Frontend build — mirrors the base app's esbuild style (vanilla TS → ESM bundle). Emits
|
|
* public/build/{app.js, index.html, styles.css}. index.html + styles.css are copied verbatim
|
|
* (index.html already references ./app.js and ./styles.css, both siblings in the build dir).
|
|
*/
|
|
import { build } from 'esbuild'
|
|
import { mkdir, copyFile } from 'node:fs/promises'
|
|
import { dirname, resolve } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
const pub = resolve(here, 'public')
|
|
const out = resolve(pub, 'build')
|
|
|
|
async function main() {
|
|
await mkdir(out, { recursive: true })
|
|
|
|
await build({
|
|
entryPoints: [resolve(pub, 'app.ts')],
|
|
bundle: true,
|
|
format: 'esm',
|
|
target: 'es2022',
|
|
minify: true,
|
|
sourcemap: true,
|
|
outfile: resolve(out, 'app.js'),
|
|
logLevel: 'info',
|
|
})
|
|
|
|
await copyFile(resolve(pub, 'index.html'), resolve(out, 'index.html'))
|
|
await copyFile(resolve(pub, 'styles.css'), resolve(out, 'styles.css'))
|
|
process.stdout.write('[control-panel] frontend build → public/build\n')
|
|
}
|
|
|
|
main().catch((err) => {
|
|
process.stderr.write(`[control-panel] build failed: ${err instanceof Error ? err.message : String(err)}\n`)
|
|
process.exit(1)
|
|
})
|