2026-01-24 17:45:54 +03:30
|
|
|
(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;
|
|
|
|
|
});
|
|
|
|
|
|
2026-07-01 22:28:09 +03:30
|
|
|
const VERSION_SUCCESS_URL_KEY = 'VERSION_Success_URL';
|
2026-06-24 01:46:30 +03:30
|
|
|
const CONFIG_SUCCESS_URL_KEY = 'CONFIG_Success_URL';
|
2026-07-01 22:28:09 +03:30
|
|
|
const VERSION_RETRY_ROUNDS = 5;
|
|
|
|
|
const CONFIG_RETRY_ROUNDS = 5;
|
2026-01-24 17:45:54 +03:30
|
|
|
|
2026-06-24 01:46:30 +03:30
|
|
|
const CONFIG_TIMEOUT = 12000;
|
2026-01-24 17:45:54 +03:30
|
|
|
let configTimeoutId;
|
|
|
|
|
let mainScriptLoaded = false;
|
|
|
|
|
|
2026-06-24 01:46:30 +03:30
|
|
|
function getProtocol() {
|
|
|
|
|
return window.location.protocol === 'https:' ? 'https:' : 'http:';
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-01 22:28:09 +03:30
|
|
|
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 sortVersionUrls(urls) {
|
|
|
|
|
const lastSuccessUrl = localStorage.getItem(VERSION_SUCCESS_URL_KEY);
|
|
|
|
|
|
|
|
|
|
if (!lastSuccessUrl) {
|
|
|
|
|
return urls.slice();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [lastSuccessUrl].concat(urls.filter((url) => url !== lastSuccessUrl));
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-24 01:46:30 +03:30
|
|
|
function sortConfigUrls(urls) {
|
|
|
|
|
const lastSuccessUrl = localStorage.getItem(CONFIG_SUCCESS_URL_KEY);
|
|
|
|
|
|
|
|
|
|
return [...urls].sort((a, b) => {
|
|
|
|
|
if (a === lastSuccessUrl) return -1;
|
|
|
|
|
if (b === lastSuccessUrl) return 1;
|
|
|
|
|
return 0;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-01 22:28:09 +03:30
|
|
|
function clearStaleSuccessUrl(key, url) {
|
|
|
|
|
if (localStorage.getItem(key) === url) {
|
|
|
|
|
localStorage.removeItem(key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-24 01:46:30 +03:30
|
|
|
async function fetchJson(url, timeout = 3500) {
|
|
|
|
|
const controller = new AbortController();
|
|
|
|
|
const timeoutId = window.setTimeout(() => controller.abort(), timeout);
|
|
|
|
|
|
2026-01-24 17:45:54 +03:30
|
|
|
try {
|
2026-06-24 01:46:30 +03:30
|
|
|
const response = await fetch(url, {
|
2026-01-24 17:45:54 +03:30
|
|
|
method: 'GET',
|
|
|
|
|
headers: {
|
2026-06-24 01:46:30 +03:30
|
|
|
Accept: 'application/json'
|
2026-01-24 17:45:54 +03:30
|
|
|
},
|
2026-06-24 01:46:30 +03:30
|
|
|
cache: 'no-store',
|
|
|
|
|
signal: controller.signal
|
2026-01-24 17:45:54 +03:30
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
2026-06-24 01:46:30 +03:30
|
|
|
throw new Error('Request failed: ' + response.status);
|
2026-01-24 17:45:54 +03:30
|
|
|
}
|
|
|
|
|
|
2026-06-24 01:46:30 +03:30
|
|
|
const contentType = response.headers.get('content-type') || '';
|
|
|
|
|
|
|
|
|
|
if (!contentType.includes('application/json')) {
|
2026-01-24 17:45:54 +03:30
|
|
|
const text = await response.text();
|
|
|
|
|
throw new Error('Response is not JSON. Received: ' + text.substring(0, 100));
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-24 01:46:30 +03:30
|
|
|
return await response.json();
|
|
|
|
|
} finally {
|
|
|
|
|
window.clearTimeout(timeoutId);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-24 17:45:54 +03:30
|
|
|
|
2026-07-01 22:28:09 +03:30
|
|
|
async function loadVersion(rounds = VERSION_RETRY_ROUNDS) {
|
|
|
|
|
const urls = sortVersionUrls(getVersionUrls());
|
|
|
|
|
|
|
|
|
|
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 {
|
|
|
|
|
await fetchJson(url, timeout);
|
|
|
|
|
localStorage.setItem(VERSION_SUCCESS_URL_KEY, url);
|
|
|
|
|
console.log('Version loaded from:', url);
|
|
|
|
|
return;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
clearStaleSuccessUrl(VERSION_SUCCESS_URL_KEY, url);
|
|
|
|
|
console.warn('Version failed from', url, 'round:', round, error);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-06-24 01:46:30 +03:30
|
|
|
}
|
2026-07-01 22:28:09 +03:30
|
|
|
|
|
|
|
|
console.warn('Version check failed from all endpoints');
|
2026-06-24 01:46:30 +03:30
|
|
|
}
|
|
|
|
|
|
2026-07-01 22:28:09 +03:30
|
|
|
async function fetchConfig(rounds = CONFIG_RETRY_ROUNDS) {
|
|
|
|
|
const urls = sortConfigUrls(getConfigUrls());
|
2026-06-24 01:46:30 +03:30
|
|
|
|
2026-07-01 22:28:09 +03:30
|
|
|
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;
|
2026-06-24 01:46:30 +03:30
|
|
|
|
2026-07-01 22:28:09 +03:30
|
|
|
try {
|
|
|
|
|
const config = await fetchJson(url, timeout);
|
2026-06-24 01:46:30 +03:30
|
|
|
|
2026-07-01 22:28:09 +03:30
|
|
|
if (!config || typeof config !== 'object') {
|
|
|
|
|
throw new Error('Config response is invalid');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
localStorage.setItem(CONFIG_SUCCESS_URL_KEY, url);
|
|
|
|
|
return { config, url };
|
|
|
|
|
} catch (error) {
|
|
|
|
|
clearStaleSuccessUrl(CONFIG_SUCCESS_URL_KEY, url);
|
|
|
|
|
console.warn('Config failed from', url, 'round:', round, error);
|
2026-06-24 01:46:30 +03:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new Error('Cannot load config from any server');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function applyConfig(config) {
|
|
|
|
|
window.__APP_CONFIG__ = config;
|
|
|
|
|
window.__CONFIG_READY__ = true;
|
|
|
|
|
|
|
|
|
|
if (config?.user?.name_fa) {
|
|
|
|
|
document.title = 'پنل مدیریت ' + '-' + config.user.name_fa;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (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 loadConfig() {
|
|
|
|
|
try {
|
|
|
|
|
await loadVersion();
|
|
|
|
|
|
|
|
|
|
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');
|
2026-01-24 17:45:54 +03:30
|
|
|
console.warn('Error details:', error.message);
|
2026-06-24 01:46:30 +03:30
|
|
|
console.warn('App will continue with empty config.');
|
|
|
|
|
|
2026-01-24 17:45:54 +03:30
|
|
|
window.__APP_CONFIG__ = {};
|
|
|
|
|
window.__CONFIG_READY__ = true;
|
2026-06-24 01:46:30 +03:30
|
|
|
finishConfigLoad({}, error);
|
2026-01-24 17:45:54 +03:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 config.');
|
|
|
|
|
window.__APP_CONFIG__ = window.__APP_CONFIG__ || {};
|
|
|
|
|
window.__CONFIG_READY__ = true;
|
|
|
|
|
|
|
|
|
|
if (configResolve) {
|
|
|
|
|
configResolve({});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loadMainScript();
|
|
|
|
|
}
|
|
|
|
|
}, CONFIG_TIMEOUT);
|
|
|
|
|
|
|
|
|
|
loadConfig();
|
|
|
|
|
})();
|