Compare commits
4 Commits
1dcf227bcb
...
52f6588316
| Author | SHA1 | Date | |
|---|---|---|---|
| 52f6588316 | |||
| 35f20b86f7 | |||
| 5c50801bd5 | |||
| 8b6364c53a |
+28
-23
@@ -78,7 +78,9 @@
|
|||||||
dashboardRuntimeFetch: 'always',
|
dashboardRuntimeFetch: 'always',
|
||||||
dashboardTargetUrl: 'https://dashboard.informatika.untan.ac.id/index.php?penelitian',
|
dashboardTargetUrl: 'https://dashboard.informatika.untan.ac.id/index.php?penelitian',
|
||||||
dashboardProxyTemplates: [
|
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}}'
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -1631,6 +1633,22 @@
|
|||||||
updateStatPub(newData.length);
|
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() {
|
async function fetchDashboardData() {
|
||||||
const grid = document.getElementById('riset-grid');
|
const grid = document.getElementById('riset-grid');
|
||||||
if (grid) {
|
if (grid) {
|
||||||
@@ -1681,28 +1699,15 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const getProxyUrl of proxyServers) {
|
try {
|
||||||
const controller = new AbortController();
|
const proxyUrls = proxyServers.map((getProxyUrl) => getProxyUrl(targetUrl));
|
||||||
const timeoutId = setTimeout(() => controller.abort(), 15000);
|
const rows = await Promise.any(proxyUrls.map((proxyUrl) => fetchProxyRows(proxyUrl)));
|
||||||
|
applyDashboardRows(rows);
|
||||||
try {
|
return;
|
||||||
const proxyUrl = getProxyUrl(targetUrl);
|
} catch (error) {
|
||||||
|
lastError = error instanceof AggregateError && error.errors?.length
|
||||||
const response = await fetch(proxyUrl, { signal: controller.signal });
|
? error.errors[error.errors.length - 1]
|
||||||
clearTimeout(timeoutId);
|
: error;
|
||||||
|
|
||||||
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...
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jika semua proxy gagal
|
// Jika semua proxy gagal
|
||||||
|
|||||||
+74
-12
@@ -6,8 +6,13 @@ import { defineConfig } from 'vite';
|
|||||||
const root = path.dirname(fileURLToPath(import.meta.url));
|
const root = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const excludedDirs = new Set(['.git', 'dist', 'node_modules']);
|
const excludedDirs = new Set(['.git', 'dist', 'node_modules']);
|
||||||
const rodaDashboardUrl = 'https://dashboard.informatika.untan.ac.id/index.php?penelitian';
|
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 rodaDashboardHtmlCache;
|
||||||
|
let rodaDashboardDevCache = { html: '', timestamp: 0 };
|
||||||
|
|
||||||
function collectHtmlInputs(dir) {
|
function collectHtmlInputs(dir) {
|
||||||
const inputs = {};
|
const inputs = {};
|
||||||
@@ -50,33 +55,37 @@ function copyStaticAssets() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchRodaDashboardHtml() {
|
async function requestRodaDashboardHtml() {
|
||||||
if (rodaDashboardHtmlCache !== undefined) {
|
|
||||||
return rodaDashboardHtmlCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeoutId = setTimeout(() => controller.abort(), 12000);
|
const timeoutId = setTimeout(() => controller.abort(), 12000);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(rodaDashboardUrl, {
|
const response = await fetch(rodaDashboardUrl, {
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
headers: {
|
headers: dashboardRequestHeaders,
|
||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP ${response.status}`);
|
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) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : String(error);
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
console.warn(`[roda-dashboard] Build-time dashboard fetch skipped: ${message}`);
|
console.warn(`[roda-dashboard] Build-time dashboard fetch skipped: ${message}`);
|
||||||
rodaDashboardHtmlCache = '';
|
rodaDashboardHtmlCache = '';
|
||||||
} finally {
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rodaDashboardHtmlCache;
|
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({
|
export default defineConfig({
|
||||||
plugins: [embedRodaDashboardData(), copyStaticAssets()],
|
plugins: [serveRodaDashboardApi(), embedRodaDashboardData(), copyStaticAssets()],
|
||||||
build: {
|
build: {
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
input: collectHtmlInputs(root),
|
input: collectHtmlInputs(root),
|
||||||
|
|||||||
Reference in New Issue
Block a user