core/frontend/build.mjs
enzo 97212b7ffa feat: sessions management, web manifest, square icon-only buttons, remove lang select
- Backend: migration 002 adds user_agent/ip/last_used_at to refresh_tokens
- Backend: GET /api/auth/sessions + DELETE /api/auth/sessions/{id} endpoints
- Frontend: profile page — sessions section (browser, IP, datetime, revoke)
- Frontend: web manifest + SVG icon for PWA support
- Frontend: remove language selector from all navbars (moved to profile page)
- Frontend: neu-btn--icon-sm class for square icon-only buttons (theme/logout/edit)
- Frontend: manifest link added to all 9 HTML pages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 20:14:11 +01:00

111 lines
3.1 KiB
JavaScript

/**
* Build script for ProxmoxPanel Alpine frontend.
* - Bundles Swup into an IIFE (browser-loadable)
* - Bundles xterm.js + addon-fit into IIFEs
* - Copies all static assets to dist/
*/
import * as esbuild from 'esbuild'
import * as fs from 'fs'
import * as path from 'path'
const dist = 'dist'
// Clean dist
fs.rmSync(dist, { recursive: true, force: true })
fs.mkdirSync(`${dist}/js/vendors`, { recursive: true })
fs.mkdirSync(`${dist}/js`, { recursive: true })
fs.mkdirSync(`${dist}/css`, { recursive: true })
fs.mkdirSync(`${dist}/locales`, { recursive: true })
// 1. Bundle Swup into IIFE
console.log('Bundling Swup...')
await esbuild.build({
entryPoints: ['swup-bundle.entry.mjs'],
bundle: true,
format: 'iife',
globalName: '_swupExports',
outfile: `${dist}/js/vendors/swup.iife.js`,
minify: true,
})
// Expose Swup on window
const swupOut = fs.readFileSync(`${dist}/js/vendors/swup.iife.js`, 'utf8')
fs.writeFileSync(`${dist}/js/vendors/swup.iife.js`,
swupOut + '\nwindow.Swup=_swupExports.Swup;')
// 2. Bundle xterm.js
console.log('Bundling xterm...')
await esbuild.build({
entryPoints: ['xterm-bundle.entry.mjs'],
bundle: true,
format: 'iife',
globalName: '_xtermExports',
outfile: `${dist}/js/vendors/xterm.iife.js`,
minify: true,
})
const xtermOut = fs.readFileSync(`${dist}/js/vendors/xterm.iife.js`, 'utf8')
fs.writeFileSync(`${dist}/js/vendors/xterm.iife.js`,
xtermOut + '\nwindow.Terminal=_xtermExports.Terminal;window.FitAddon=_xtermExports.FitAddon;')
// xterm CSS
const xtermCss = 'node_modules/@xterm/xterm/css/xterm.css'
if (fs.existsSync(xtermCss)) {
fs.copyFileSync(xtermCss, `${dist}/css/xterm.css`)
}
// 3. Copy pre-downloaded vendors (Alpine, HTMX)
for (const f of ['alpine.min.js', 'htmx.min.js']) {
const src = `vendors/${f}`
if (fs.existsSync(src)) {
fs.copyFileSync(src, `${dist}/js/vendors/${f}`)
console.log(`Copied ${f}`)
} else {
console.warn(`WARN: ${src} not found`)
}
}
// 4. Copy app JS files
for (const f of fs.readdirSync('js')) {
if (f.endsWith('.js')) {
fs.mkdirSync(`${dist}/js`, { recursive: true })
fs.copyFileSync(`js/${f}`, `${dist}/js/${f}`)
}
}
// Service Worker doit être servi depuis la racine pour avoir le bon scope
if (fs.existsSync('js/ws.sw.js')) {
fs.copyFileSync('js/ws.sw.js', `${dist}/ws.sw.js`)
}
// 5. Copy CSS
for (const f of fs.readdirSync('css')) {
fs.copyFileSync(`css/${f}`, `${dist}/css/${f}`)
}
// 6. Copy locales
for (const f of fs.readdirSync('locales')) {
fs.copyFileSync(`locales/${f}`, `${dist}/locales/${f}`)
}
// 7. Copy HTML pages
for (const f of fs.readdirSync('.')) {
if (f.endsWith('.html')) {
fs.copyFileSync(f, `${dist}/${f}`)
}
}
// 8. Copy manifest.json
if (fs.existsSync('manifest.json')) {
fs.copyFileSync('manifest.json', `${dist}/manifest.json`)
console.log('Copied manifest.json')
}
// 9. Copy icons/
if (fs.existsSync('icons')) {
fs.mkdirSync(`${dist}/icons`, { recursive: true })
for (const f of fs.readdirSync('icons')) {
fs.copyFileSync(`icons/${f}`, `${dist}/icons/${f}`)
}
console.log('Copied icons/')
}
console.log('✓ Build complete → dist/')