From 35f20b86f79a5099fb17bbd65e9adabdb7308d09 Mon Sep 17 00:00:00 2001 From: GuavaPopper Date: Fri, 5 Jun 2026 14:38:35 +0700 Subject: [PATCH] Aktifkan proxy dashboard di Vite dev --- vite.config.js | 86 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 12 deletions(-) diff --git a/vite.config.js b/vite.config.js index e65cacf..f076183 100644 --- a/vite.config.js +++ b/vite.config.js @@ -6,8 +6,13 @@ import { defineConfig } from 'vite'; const root = path.dirname(fileURLToPath(import.meta.url)); const excludedDirs = new Set(['.git', 'dist', 'node_modules']); const rodaDashboardUrl = 'https://dashboard.informatika.untan.ac.id/index.php?penelitian'; +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', +}; let rodaDashboardHtmlCache; +let rodaDashboardDevCache = { html: '', timestamp: 0 }; function collectHtmlInputs(dir) { const inputs = {}; @@ -50,33 +55,37 @@ function copyStaticAssets() { }; } -async function fetchRodaDashboardHtml() { - if (rodaDashboardHtmlCache !== undefined) { - return rodaDashboardHtmlCache; - } - +async function requestRodaDashboardHtml() { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 12000); try { const response = await fetch(rodaDashboardUrl, { signal: controller.signal, - headers: { - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36', - }, + headers: dashboardRequestHeaders, }); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } - rodaDashboardHtmlCache = await response.text(); + return response.text(); + } finally { + clearTimeout(timeoutId); + } +} + +async function fetchRodaDashboardHtml() { + if (rodaDashboardHtmlCache !== undefined) { + return rodaDashboardHtmlCache; + } + + try { + rodaDashboardHtmlCache = await requestRodaDashboardHtml(); } catch (error) { const message = error instanceof Error ? error.message : String(error); console.warn(`[roda-dashboard] Build-time dashboard fetch skipped: ${message}`); rodaDashboardHtmlCache = ''; - } finally { - clearTimeout(timeoutId); } return rodaDashboardHtmlCache; @@ -115,8 +124,61 @@ 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: [embedRodaDashboardData(), copyStaticAssets()], + plugins: [serveRodaDashboardApi(), embedRodaDashboardData(), copyStaticAssets()], build: { rollupOptions: { input: collectHtmlInputs(root),