fix: run RODA with Vite dev

This commit is contained in:
GuavaPopper
2026-06-05 17:55:51 +07:00
parent f5dfb68bcc
commit e18641ff52
6 changed files with 116 additions and 82 deletions
-13
View File
@@ -1,13 +0,0 @@
{
"name": "roda",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "roda",
"version": "1.0.0",
"license": "ISC"
}
}
}
+2 -13
View File
@@ -1,14 +1,3 @@
{
"name": "roda",
"version": "1.0.0",
"description": "Project website untuk menampilkan sorotan dan prestasi karya mahasiswa program studi Informatika Universitas Tanjungpura.",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js",
"dev": "node server.js"
},
"keywords": [],
"author": "",
"license": "ISC"
}
"type": "commonjs"
}
+1 -1
View File
@@ -5,7 +5,7 @@
"type": "module",
"description": "Student-safe static page assignment kit for Informatika UNTAN",
"scripts": {
"dev": "node groups/RODA/server.js",
"dev": "node scripts/dev.js",
"dev:vite": "vite",
"dev:roda": "node groups/RODA/server.js",
"build": "vite build",
+94
View File
@@ -0,0 +1,94 @@
import { spawn } from 'node:child_process';
export function createDevProcessSpecs({
nodeCommand = process.execPath,
env = process.env,
} = {}) {
return {
roda: {
command: nodeCommand,
args: ['groups/RODA/server.js'],
options: {
env: {
...env,
HOST: env.RODA_HOST || '127.0.0.1',
PORT: env.RODA_PORT || '3000',
},
stdio: ['ignore', 'ignore', 'pipe'],
},
},
vite: {
command: nodeCommand,
args: ['node_modules/vite/bin/vite.js'],
options: {
env: { ...env },
stdio: 'inherit',
},
},
};
}
export function startDevServers({
spawnProcess = spawn,
specs = createDevProcessSpecs(),
} = {}) {
const roda = spawnProcess(specs.roda.command, specs.roda.args, specs.roda.options);
const vite = spawnProcess(specs.vite.command, specs.vite.args, specs.vite.options);
let shuttingDown = false;
let rodaErrorOutput = '';
roda.stderr?.on('data', (chunk) => {
const message = String(chunk);
rodaErrorOutput += message;
if (!message.includes('EADDRINUSE')) {
process.stderr.write(`[roda] ${message}`);
}
});
function stopChildren() {
shuttingDown = true;
if (!roda.killed) {
roda.kill();
}
if (!vite.killed) {
vite.kill();
}
}
vite.on('exit', (code, signal) => {
shuttingDown = true;
if (!roda.killed) {
roda.kill();
}
if (signal) {
process.exitCode = 1;
return;
}
process.exitCode = code ?? 0;
});
roda.on('exit', (code, signal) => {
if (shuttingDown) {
return;
}
if (rodaErrorOutput.includes('EADDRINUSE')) {
process.stderr.write('[roda] Port 3000 is already in use; continuing with the existing RODA server.\n');
return;
}
const reason = signal ? `signal ${signal}` : `exit code ${code}`;
process.stderr.write(`[roda] server stopped unexpectedly (${reason})\n`);
if (!vite.killed) {
vite.kill();
}
process.exitCode = code || 1;
});
process.once('SIGINT', stopChildren);
process.once('SIGTERM', stopChildren);
return { roda, vite };
}
+3
View File
@@ -0,0 +1,3 @@
import { startDevServers } from './dev-runner.js';
startDevServers();
+16 -55
View File
@@ -10,9 +10,11 @@ const rodaDashboardCacheTtlMs = 5 * 60 * 1000;
const dashboardRequestHeaders = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36',
};
const rodaHost = process.env.RODA_HOST || '127.0.0.1';
const rodaPort = process.env.RODA_PORT || '3000';
const rodaTarget = `http://${rodaHost}:${rodaPort}`;
let rodaDashboardHtmlCache;
let rodaDashboardDevCache = { html: '', timestamp: 0 };
function collectHtmlInputs(dir) {
const inputs = {};
@@ -124,61 +126,20 @@ function embedRodaDashboardData() {
};
}
function serveRodaDashboardApi() {
return {
name: 'serve-roda-dashboard-api',
configureServer(server) {
server.middlewares.use(async (req, res, next) => {
const requestUrl = new URL(req.url || '/', 'http://localhost');
if (requestUrl.pathname !== '/api/dashboard') {
next();
return;
}
if (req.method !== 'GET' && req.method !== 'HEAD') {
res.writeHead(405, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('METHOD_NOT_ALLOWED');
return;
}
const now = Date.now();
try {
let html = rodaDashboardDevCache.html;
let cacheStatus = 'HIT';
if (!html || (now - rodaDashboardDevCache.timestamp) >= rodaDashboardCacheTtlMs) {
html = await requestRodaDashboardHtml();
rodaDashboardDevCache = { html, timestamp: Date.now() };
cacheStatus = 'MISS';
}
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8',
'X-Cache': cacheStatus,
});
res.end(req.method === 'HEAD' ? '' : html);
} catch (error) {
if (rodaDashboardDevCache.html) {
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8',
'X-Cache': 'STALE',
});
res.end(req.method === 'HEAD' ? '' : rodaDashboardDevCache.html);
return;
}
const message = error instanceof Error ? error.message : String(error);
console.warn(`[roda-dashboard] Dev dashboard fetch failed: ${message}`);
res.writeHead(502, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('DASHBOARD_FETCH_FAILED');
}
});
},
};
}
export default defineConfig({
plugins: [serveRodaDashboardApi(), embedRodaDashboardData(), copyStaticAssets()],
plugins: [embedRodaDashboardData(), copyStaticAssets()],
server: {
proxy: {
'/groups/RODA/api/dashboard': {
target: rodaTarget,
changeOrigin: true,
},
'/api/dashboard': {
target: rodaTarget,
changeOrigin: true,
},
},
},
build: {
rollupOptions: {
input: collectHtmlInputs(root),