From 8b6364c53a7fd488a33580fed7b633ecd8ef1ac8 Mon Sep 17 00:00:00 2001 From: GuavaPopper Date: Fri, 5 Jun 2026 13:21:37 +0700 Subject: [PATCH 1/2] Aktifkan fallback proxy paralel publikasi --- groups/RODA/index.html | 51 +++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/groups/RODA/index.html b/groups/RODA/index.html index dfcf655..71a8e09 100644 --- a/groups/RODA/index.html +++ b/groups/RODA/index.html @@ -78,7 +78,9 @@ dashboardRuntimeFetch: 'always', dashboardTargetUrl: 'https://dashboard.informatika.untan.ac.id/index.php?penelitian', dashboardProxyTemplates: [ - 'https://api.codetabs.com/v1/proxy?quest={{url}}' + 'https://api.codetabs.com/v1/proxy?quest={{url}}', + 'https://api.allorigins.win/get?url={{url}}&ts={{ts}}', + 'https://thingproxy.freeboard.io/fetch/{{rawUrl}}' ] }; @@ -1631,6 +1633,22 @@ updateStatPub(newData.length); } + async function fetchProxyRows(proxyUrl) { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 15000); + + try { + const response = await fetch(proxyUrl, { signal: controller.signal }); + if (!response.ok) throw new Error(`HTTP_ERROR_${response.status}`); + + const responseData = await response.text(); + const html = extractDashboardHtml(responseData); + return parseDashboardRows(html); + } finally { + clearTimeout(timeoutId); + } + } + async function fetchDashboardData() { const grid = document.getElementById('riset-grid'); if (grid) { @@ -1681,28 +1699,15 @@ return; } - for (const getProxyUrl of proxyServers) { - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), 15000); - - try { - const proxyUrl = getProxyUrl(targetUrl); - - const response = await fetch(proxyUrl, { signal: controller.signal }); - clearTimeout(timeoutId); - - if (!response.ok) throw new Error(`HTTP_ERROR_${response.status}`); - - const responseData = await response.text(); - const html = extractDashboardHtml(responseData); - const rows = parseDashboardRows(html); - applyDashboardRows(rows); - return; // Berhasil! Keluar dari loop - } catch (error) { - clearTimeout(timeoutId); - lastError = error; - // Lanjut ke proxy berikutnya... - } + try { + const proxyUrls = proxyServers.map((getProxyUrl) => getProxyUrl(targetUrl)); + const rows = await Promise.any(proxyUrls.map((proxyUrl) => fetchProxyRows(proxyUrl))); + applyDashboardRows(rows); + return; + } catch (error) { + lastError = error instanceof AggregateError && error.errors?.length + ? error.errors[error.errors.length - 1] + : error; } // Jika semua proxy gagal -- 2.52.0 From 35f20b86f79a5099fb17bbd65e9adabdb7308d09 Mon Sep 17 00:00:00 2001 From: GuavaPopper Date: Fri, 5 Jun 2026 14:38:35 +0700 Subject: [PATCH 2/2] 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), -- 2.52.0