import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import fs from 'node:fs'; import path from 'node:path'; import { DatabaseSync } from 'node:sqlite'; const outDir = path.resolve(__dirname, '../internal/web/dist'); const BACKEND_TARGET = 'http://localhost:2053'; function resolveDBPath() { const envFolder = process.env.XUI_DB_FOLDER; if (envFolder) { const abs = path.isAbsolute(envFolder) ? envFolder : path.resolve(__dirname, '..', envFolder); return path.join(abs, 'x-ui.db'); } const repoSubDB = path.resolve(__dirname, '..', 'x-ui', 'x-ui.db'); if (fs.existsSync(repoSubDB)) return repoSubDB; const repoDB = path.resolve(__dirname, '..', 'x-ui.db'); if (fs.existsSync(repoDB)) return repoDB; return '/etc/x-ui/x-ui.db'; } const PANEL_API_PREFIXES = ['panel/api/', 'panel/csrf-token']; let cachedBasePath = '/'; function readBasePathFromDB() { const dbPath = resolveDBPath(); let db; try { db = new DatabaseSync(dbPath, { readOnly: true }); } catch (_e) { return '/'; } try { const row = db.prepare('SELECT value FROM settings WHERE key = ?').get('webBasePath'); let value = row && typeof row.value === 'string' ? row.value : '/'; if (!value.startsWith('/')) value = '/' + value; if (!value.endsWith('/')) value += '/'; return value; } catch (_e) { return '/'; } finally { db.close(); } } function refreshBasePath() { cachedBasePath = readBasePathFromDB(); return cachedBasePath; } function readPanelVersion() { try { const versionFile = path.resolve(__dirname, '..', 'config', 'version'); return fs.readFileSync(versionFile, 'utf8').trim(); } catch (_e) { return ''; } } // `apply: 'serve'` keeps the injection out of `vite build` — dist.go // already injects webBasePath and version at runtime in production. function injectBasePathPlugin() { return { name: 'xui-inject-base-path', apply: 'serve', transformIndexHtml(html) { const basePath = refreshBasePath(); const escaped = basePath.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); const version = readPanelVersion().replace(/\\/g, '\\\\').replace(/"/g, '\\"'); const tag = ``; return html.replace('', `${tag}`); }, }; } // Cloudflare Rocket Loader rewrites script tags and runs bundles through its // own loader, breaking ES-module semantics; data-cfasync="false" opts out. function rocketLoaderOptOutPlugin() { return { name: 'xui-rocket-loader-opt-out', apply: 'build', transformIndexHtml(html) { return html.replaceAll('