refactor: Perbaiki Proxy Pipeline dan simplify dashboard configuration

This commit is contained in:
GuavaPopper
2026-06-05 18:57:23 +07:00
parent e18641ff52
commit 3d2f0b23ad
3 changed files with 24 additions and 130 deletions
-4
View File
@@ -1,4 +0,0 @@
DASHBOARD_TARGET_URL=https://dashboard.informatika.untan.ac.id/index.php?penelitian
DASHBOARD_PROXY_1=https://api.codetabs.com/v1/proxy?quest={{url}}
DASHBOARD_PROXY_2=https://api.allorigins.win/get?url={{url}}&ts={{ts}}
DASHBOARD_PROXY_3=https://thingproxy.freeboard.io/fetch/{{rawUrl}}
+17 -46
View File
@@ -77,12 +77,6 @@
debugLogs: false,
dashboardRuntimeFetch: 'always',
dashboardApiUrl: 'api/dashboard',
dashboardTargetUrl: 'https://dashboard.informatika.untan.ac.id/index.php?penelitian',
dashboardProxyTemplates: [
'https://api.codetabs.com/v1/proxy?quest={{url}}',
'https://api.allorigins.win/get?url={{url}}&ts={{ts}}',
'https://thingproxy.freeboard.io/fetch/{{rawUrl}}'
]
};
</script>
<!-- RODA_DASHBOARD_DATA -->
@@ -1662,56 +1656,33 @@
}
let lastError = null;
if (isDashboardRuntimeFetchAllowed()) {
const dashboardApiUrl = window.APP_CONFIG?.dashboardApiUrl?.trim();
if (dashboardApiUrl) {
try {
const rows = await fetchProxyRows(dashboardApiUrl);
applyDashboardRows(rows);
return;
} catch (error) {
lastError = error;
}
}
}
// Fallback ke data yang di-embed saat build jika runtime fetch gagal atau dinonaktifkan
const embeddedHtml = readEmbeddedDashboardHtml();
if (embeddedHtml) {
try {
applyDashboardRows(parseDashboardRows(embeddedHtml));
return;
} catch (error) {
lastError = error;
lastError = lastError || error;
}
}
if (!isDashboardRuntimeFetchAllowed()) {
renderRisetFeedback('Pemuatan Data Gagal', getDashboardErrorMessage(lastError || new Error('DASHBOARD_RUNTIME_FETCH_DISABLED')), 'Coba Lagi', 'fetchDashboardData()');
return;
}
const targetUrl = window.APP_CONFIG?.dashboardTargetUrl?.trim();
const dashboardApiUrl = window.APP_CONFIG?.dashboardApiUrl?.trim();
const proxyTemplates = Array.isArray(window.APP_CONFIG?.dashboardProxyTemplates)
? window.APP_CONFIG.dashboardProxyTemplates.filter(Boolean)
: [];
const proxyServers = dashboardApiUrl ? [() => dashboardApiUrl] : [];
if (targetUrl) {
proxyTemplates.forEach((template) => {
proxyServers.push((url) => template
.replaceAll('{{url}}', encodeURIComponent(url))
.replaceAll('{{rawUrl}}', url)
.replaceAll('{{ts}}', String(Date.now())));
});
}
if (!proxyServers.length) {
renderRisetFeedback('Pemuatan Data Gagal', getDashboardErrorMessage(new Error('DASHBOARD_PROXY_NOT_CONFIGURED')), 'Coba Lagi', 'fetchDashboardData()');
return;
}
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
renderRisetFeedback('Pemuatan Data Gagal', getDashboardErrorMessage(lastError), 'Coba Lagi', 'fetchDashboardData()');
}
+6 -79
View File
@@ -6,69 +6,21 @@ const { URL } = require('url');
const ROOT_DIR = __dirname;
const PARENT_DIR = path.join(__dirname, '..', '..');
const INDEX_PATH = path.join(ROOT_DIR, 'index.html');
const ENV_PATH = path.join(ROOT_DIR, '.env');
const HOST = process.env.HOST || '127.0.0.1';
const PORT = Number(process.env.PORT || 3000);
const DEFAULT_DASHBOARD_TARGET_URL = 'https://dashboard.informatika.untan.ac.id/index.php?penelitian';
const DASHBOARD_TARGET_URL = 'https://dashboard.informatika.untan.ac.id/index.php?penelitian';
const RODA_PATH_PREFIX = '/groups/RODA';
function logServerError(scope, error, extra = {}) {
console.error(`[${scope}]`, error && error.stack ? error.stack : error, extra);
}
function parseEnvFile(filePath) {
if (!fs.existsSync(filePath)) {
return {};
}
const env = {};
const lines = fs.readFileSync(filePath, 'utf8').split(/\r?\n/);
for (const rawLine of lines) {
const line = rawLine.trim();
if (!line || line.startsWith('#')) {
continue;
}
const separatorIndex = line.indexOf('=');
if (separatorIndex === -1) {
continue;
}
const key = line.slice(0, separatorIndex).trim();
let value = line.slice(separatorIndex + 1).trim();
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
}
env[key] = value;
}
return env;
}
function getAppConfig() {
const env = {
...parseEnvFile(ENV_PATH),
...process.env
};
const dashboardProxyTemplates = [
env.DASHBOARD_PROXY_1,
env.DASHBOARD_PROXY_2,
env.DASHBOARD_PROXY_3
].filter(Boolean);
return {
debugLogs: env.DEBUG_LOGS === 'true',
dashboardRuntimeFetch: env.DASHBOARD_RUNTIME_FETCH || 'always',
dashboardApiUrl: env.DASHBOARD_API_URL || 'api/dashboard',
dashboardTargetUrl: env.DASHBOARD_TARGET_URL || DEFAULT_DASHBOARD_TARGET_URL,
dashboardProxyTemplates
debugLogs: false,
dashboardRuntimeFetch: 'always',
dashboardApiUrl: 'api/dashboard',
dashboardTargetUrl: DASHBOARD_TARGET_URL,
};
}
@@ -76,10 +28,6 @@ const DASHBOARD_CACHE_TTL_MS = 5 * 60 * 1000;
let dashboardCache = { html: null, timestamp: 0 };
async function fetchDashboardHtml() {
const targetUrl = getAppConfig().dashboardTargetUrl;
if (!targetUrl) {
throw new Error('DASHBOARD_TARGET_URL kosong');
}
const now = Date.now();
if (dashboardCache.html && (now - dashboardCache.timestamp) < DASHBOARD_CACHE_TTL_MS) {
@@ -89,7 +37,7 @@ async function fetchDashboardHtml() {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 12000);
try {
const response = await fetch(targetUrl, {
const response = await fetch(DASHBOARD_TARGET_URL, {
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'
@@ -111,35 +59,14 @@ async function fetchDashboardHtml() {
}
}
function validateAppConfig(config) {
const errors = [];
if (!config.dashboardTargetUrl) {
errors.push('DASHBOARD_TARGET_URL belum diisi.');
}
return errors;
}
function injectConfig(html) {
const configScript = `<script>window.APP_CONFIG = ${JSON.stringify(getAppConfig())};</script>`;
const defaultConfigScript = '<script>\n window.APP_CONFIG = window.APP_CONFIG || {\n dashboardTargetUrl: \'\',\n dashboardProxyTemplates: []\n };\n </script>';
if (html.includes(defaultConfigScript)) {
return html.replace(defaultConfigScript, configScript);
}
return html.replace('</head>', ` ${configScript}\n</head>`);
}
function sendHtml(res) {
try {
const html = fs.readFileSync(INDEX_PATH, 'utf8');
const configErrors = validateAppConfig(getAppConfig());
if (configErrors.length) {
logServerError('config', new Error('Invalid app config'), { configErrors });
}
const withConfig = injectConfig(html);
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(withConfig);