Files
boursecu_admin/src/config-loader.js
T

442 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(function() {
'use strict';
window.__APP_CONFIG__ = window.__APP_CONFIG__ || {};
window.__CONFIG_READY__ = false;
let configResolve;
let configReject;
window.__CONFIG_PROMISE__ = new Promise((resolve, reject) => {
configResolve = resolve;
configReject = reject;
});
const VERSION_SUCCESS_URL_KEY = 'VERSION_Success_URL';
const CONFIG_SUCCESS_URL_KEY = 'CONFIG_Success_URL';
const CONFIG_CACHE_KEY = 'APP_CONFIG_CACHE';
const VERSION_STORAGE_KEY = 'version';
const VERSION_UPDATE_LOCK_KEY = 'version_update_lock';
const VERSION_LAST_RELOAD_KEY = 'version_last_reload';
const AUTH_TOKEN_KEY = 'peransa_token';
const VERSION_RETRY_ROUNDS = 5;
const CONFIG_RETRY_ROUNDS = 5;
const CONFIG_TIMEOUT = 12000;
let configTimeoutId;
let mainScriptLoaded = false;
function getProtocol() {
return window.location.protocol === 'https:' ? 'https:' : 'http:';
}
function getVersionUrls() {
const protocol = getProtocol();
return [
'https://nerkh.gold/get_version',
'https://tablo.talasevin.ir/get_version',
'https://abshode-service.montgit.ir/server/get_version',
protocol + '//212.23.201.173:8381/get_version',
protocol + '//91.212.174.137:4040/server/get_version'
];
}
function getConfigUrls() {
const protocol = getProtocol();
return [
protocol + '//nerkh.gold/get_user',
protocol + '//tablo.talasevin.ir/get_user',
protocol + '//abshode-service.montgit.ir/server/get_user',
protocol + '//212.23.201.173:8381/get_user',
protocol + '//91.212.174.137:4040/server/get_user'
];
}
function sortUrls(urls, successKey) {
const lastSuccessUrl = localStorage.getItem(successKey);
if (!lastSuccessUrl) return urls.slice();
return [lastSuccessUrl].concat(urls.filter((url) => url !== lastSuccessUrl));
}
function clearStaleSuccessUrl(key, url) {
if (localStorage.getItem(key) === url) {
localStorage.removeItem(key);
}
}
function normalizeVersion(value) {
if (value === null || value === undefined) return '';
return String(value).trim();
}
function readUserVersion(user) {
if (!user || typeof user !== 'object' || Array.isArray(user)) return '';
const version = user.version ?? user.value;
return version === undefined || version === null ? '' : String(version).trim();
}
function normalizeVersionResponse(response) {
if (typeof response === 'string' || typeof response === 'number') {
return String(response).trim();
}
if (!response || typeof response !== 'object') return '';
if (typeof response.data === 'string' || typeof response.data === 'number') {
return String(response.data).trim();
}
const directVersion =
response.version ??
response.value ??
(response.data && typeof response.data === 'object'
? response.data.version ?? response.data.value
: undefined);
if (directVersion !== undefined && directVersion !== null) {
return String(directVersion).trim();
}
return readUserVersion(response.user);
}
function readCachedAppConfig() {
try {
const raw = localStorage.getItem(CONFIG_CACHE_KEY);
if (!raw) return null;
const payload = JSON.parse(raw);
if (!payload || typeof payload !== 'object' || !payload.config) return null;
return payload.config;
} catch (error) {
console.warn('Failed to read cached get_user:', error);
localStorage.removeItem(CONFIG_CACHE_KEY);
return null;
}
}
function writeCachedAppConfig(config) {
try {
localStorage.setItem(
CONFIG_CACHE_KEY,
JSON.stringify({
cached_at: Date.now(),
config: config
})
);
} catch (error) {
console.warn('Failed to write get_user cache:', error);
}
}
function getStoredAppVersion() {
return localStorage.getItem(VERSION_STORAGE_KEY);
}
function writeStoredAppVersion(version) {
localStorage.setItem(VERSION_STORAGE_KEY, normalizeVersion(version));
}
function shouldUpdateForVersion(serverVersion) {
const normalized = normalizeVersion(serverVersion);
if (!normalized) return false;
const stored = getStoredAppVersion();
if (!stored) {
writeStoredAppVersion(normalized);
return false;
}
return stored !== normalized;
}
function canStartVersionUpdate(serverVersion) {
const version = normalizeVersion(serverVersion);
if (!version) return false;
if (sessionStorage.getItem(VERSION_LAST_RELOAD_KEY) === version) {
return false;
}
const rawLock = sessionStorage.getItem(VERSION_UPDATE_LOCK_KEY);
if (rawLock) {
try {
const lock = JSON.parse(rawLock);
const isSameVersion = lock.version === version;
const isFreshLock = Date.now() - lock.started_at < 60_000;
if (isSameVersion && isFreshLock) return false;
} catch {
sessionStorage.removeItem(VERSION_UPDATE_LOCK_KEY);
}
}
return true;
}
function startVersionUpdateLock(serverVersion) {
sessionStorage.setItem(
VERSION_UPDATE_LOCK_KEY,
JSON.stringify({
version: normalizeVersion(serverVersion),
started_at: Date.now()
})
);
}
function finishVersionUpdateBeforeReload(serverVersion) {
const version = normalizeVersion(serverVersion);
writeStoredAppVersion(version);
sessionStorage.setItem(VERSION_LAST_RELOAD_KEY, version);
sessionStorage.removeItem(VERSION_UPDATE_LOCK_KEY);
}
function cancelVersionUpdateLock() {
sessionStorage.removeItem(VERSION_UPDATE_LOCK_KEY);
}
function preserveAuthToken() {
return localStorage.getItem(AUTH_TOKEN_KEY);
}
function restoreAuthToken(token) {
if (token) {
localStorage.setItem(AUTH_TOKEN_KEY, token);
}
}
function clearBootstrapCachesKeepToken() {
const token = preserveAuthToken();
localStorage.removeItem(CONFIG_CACHE_KEY);
localStorage.removeItem(CONFIG_SUCCESS_URL_KEY);
localStorage.removeItem(VERSION_SUCCESS_URL_KEY);
restoreAuthToken(token);
}
function hardReloadApp() {
const refreshUrl =
window.location.origin +
window.location.pathname +
'?__app_refresh=' +
Date.now() +
(window.location.hash || '');
window.location.replace(refreshUrl);
}
async function fetchJson(url, timeout) {
const controller = new AbortController();
const timeoutId = window.setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {
method: 'GET',
headers: {
Accept: 'application/json'
},
cache: 'no-store',
signal: controller.signal
});
if (!response.ok) {
throw new Error('Request failed: ' + response.status);
}
const contentType = response.headers.get('content-type') || '';
if (!contentType.includes('application/json')) {
const text = await response.text();
throw new Error('Response is not JSON. Received: ' + text.substring(0, 100));
}
return await response.json();
} finally {
window.clearTimeout(timeoutId);
}
}
async function loadVersion(rounds) {
rounds = rounds || VERSION_RETRY_ROUNDS;
const urls = sortUrls(getVersionUrls(), VERSION_SUCCESS_URL_KEY);
for (let round = 1; round <= rounds; round++) {
for (let index = 0; index < urls.length; index++) {
const url = urls[index];
const timeout = index === urls.length - 1 ? 6000 : 3500;
try {
const data = await fetchJson(url, timeout);
const version = normalizeVersionResponse(data);
if (!version) {
throw new Error('Version response is empty');
}
localStorage.setItem(VERSION_SUCCESS_URL_KEY, url);
console.log('Version loaded from:', url, version);
return version;
} catch (error) {
clearStaleSuccessUrl(VERSION_SUCCESS_URL_KEY, url);
console.warn('Version failed from', url, 'round:', round, error);
}
}
}
console.warn('Version check failed from all endpoints');
return null;
}
async function fetchConfig(rounds) {
rounds = rounds || CONFIG_RETRY_ROUNDS;
const urls = sortUrls(getConfigUrls(), CONFIG_SUCCESS_URL_KEY);
for (let round = 1; round <= rounds; round++) {
for (let index = 0; index < urls.length; index++) {
const url = urls[index];
const timeout = index === urls.length - 1 ? 7000 : 4000;
try {
const config = await fetchJson(url, timeout);
if (!config || typeof config !== 'object') {
throw new Error('Config response is invalid');
}
localStorage.setItem(CONFIG_SUCCESS_URL_KEY, url);
writeCachedAppConfig(config);
return { config: config, url: url };
} catch (error) {
clearStaleSuccessUrl(CONFIG_SUCCESS_URL_KEY, url);
console.warn('Config failed from', url, 'round:', round, error);
}
}
}
throw new Error('Cannot load config from any server');
}
function applyConfig(config) {
window.__APP_CONFIG__ = config || {};
window.__CONFIG_READY__ = true;
if (config && config.user && config.user.name_fa) {
document.title = 'پنل مدیریت ' + '-' + config.user.name_fa;
}
if (config && config.user && config.user.avatar) {
const faviconUrl = config.user.avatar;
const existingIcons = document.querySelectorAll('link[rel*="icon"]');
existingIcons.forEach((icon) => icon.remove());
const link = document.createElement('link');
link.rel = 'icon';
link.type = 'image/png';
link.href = faviconUrl + '?v=' + Date.now();
document.head.appendChild(link);
}
}
function finishConfigLoad(config, error) {
if (configResolve) {
configResolve(config);
}
if (error) {
window.dispatchEvent(new CustomEvent('configError', { detail: error }));
} else {
window.dispatchEvent(new CustomEvent('configReady', { detail: config }));
}
if (configTimeoutId) {
clearTimeout(configTimeoutId);
configTimeoutId = null;
}
loadMainScript();
}
async function applyVersionUpdate(serverVersion) {
startVersionUpdateLock(serverVersion);
try {
const { config } = await fetchConfig();
clearBootstrapCachesKeepToken();
writeCachedAppConfig(config);
applyConfig(config);
finishVersionUpdateBeforeReload(serverVersion);
console.log('App version changed — hard refresh', serverVersion);
hardReloadApp();
return true;
} catch (error) {
console.error('Version update failed:', error);
cancelVersionUpdateLock();
return false;
}
}
async function loadConfig() {
try {
// ۱) کش get_user — اگر بود سریع اعمال شود
const cachedConfig = readCachedAppConfig();
if (cachedConfig) {
applyConfig(cachedConfig);
console.log('Applied cached get_user');
}
// ۲) get_version — اگر تغییر کرده hard refresh
const remoteVersion = await loadVersion();
if (remoteVersion && shouldUpdateForVersion(remoteVersion) && canStartVersionUpdate(remoteVersion)) {
const updating = await applyVersionUpdate(remoteVersion);
if (updating) {
return;
}
}
// ۳) تازه کردن get_user و کش
const { config, url } = await fetchConfig();
applyConfig(config);
console.log('Config loaded successfully from', url, config);
finishConfigLoad(config);
} catch (error) {
console.warn('Config loader: Could not fetch config from any endpoint');
console.warn('Error details:', error && error.message ? error.message : error);
console.warn('App will continue with empty/cached config.');
const cachedConfig = readCachedAppConfig();
if (cachedConfig) {
applyConfig(cachedConfig);
finishConfigLoad(cachedConfig, error);
return;
}
window.__APP_CONFIG__ = {};
window.__CONFIG_READY__ = true;
finishConfigLoad({}, error);
}
}
function loadMainScript() {
if (mainScriptLoaded || document.querySelector('script[src="/src/main.ts"]')) {
return;
}
mainScriptLoaded = true;
import('/src/main.ts').then(() => {
console.log('Main script loaded and executed');
});
}
configTimeoutId = setTimeout(function() {
if (!mainScriptLoaded) {
console.warn('Config loader: Timeout reached. Loading main.ts with empty/cached config.');
const cachedConfig = readCachedAppConfig();
window.__APP_CONFIG__ = cachedConfig || window.__APP_CONFIG__ || {};
window.__CONFIG_READY__ = true;
if (configResolve) {
configResolve(window.__APP_CONFIG__);
}
loadMainScript();
}
}, CONFIG_TIMEOUT);
loadConfig();
})();