import { allowSuperAdminFallback, getSuperAdminBases, isSuperAdminUrl, resolveTenantDomain, SA_RETRY_ROUNDS, } from '@/config/superadmin' type VersionResponse = | string | number | { version?: string | number data?: string | number | { version?: string | number; value?: string | number } value?: string | number user?: unknown } const VERSION_SUCCESS_URL_KEY = 'VERSION_Success_URL' export const VERSION_RETRY_ROUNDS = SA_RETRY_ROUNDS let versionFetchPromise: Promise | null = null function getProtocol() { return window.location.protocol === 'https:' ? 'https:' : 'http:' } function getVersionUrls() { const protocol = getProtocol() const domain = encodeURIComponent(resolveTenantDomain()) const urls = getSuperAdminBases().map((base) => `${base}/get_version?domain=${domain}`) if (allowSuperAdminFallback()) { urls.push( '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`, ) } return urls } function sortVersionUrls(urls: string[]) { const lastSuccessUrl = localStorage.getItem(VERSION_SUCCESS_URL_KEY) if ( !lastSuccessUrl || !urls.includes(lastSuccessUrl) || !isSuperAdminUrl(lastSuccessUrl) ) { if (lastSuccessUrl && !isSuperAdminUrl(lastSuccessUrl)) { localStorage.removeItem(VERSION_SUCCESS_URL_KEY) } return [...urls] } return [lastSuccessUrl, ...urls.filter((url) => url !== lastSuccessUrl)] } function clearStaleVersionSuccessUrl(url: string) { if (localStorage.getItem(VERSION_SUCCESS_URL_KEY) === url) { localStorage.removeItem(VERSION_SUCCESS_URL_KEY) } } function readUserVersion(user: unknown) { if (!user || typeof user !== 'object' || Array.isArray(user)) { return '' } const record = user as { version?: string | number; value?: string | number } const version = record.version ?? record.value return version === undefined || version === null ? '' : String(version).trim() } function normalizeVersionResponse(response: VersionResponse) { if (typeof response === 'string' || typeof response === 'number') { return String(response).trim() } if (!response) return '' if (typeof response.data === 'string' || typeof response.data === 'number') { return String(response.data).trim() } const directVersion = response.version ?? response.value ?? (typeof response.data === 'object' ? response.data?.version ?? response.data?.value : undefined) if (directVersion !== undefined && directVersion !== null) { return String(directVersion).trim() } return readUserVersion(response.user) } async function fetchVersion(url: string, timeout = 3500): Promise { 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(`Version request failed with status ${response.status}`) } const contentType = response.headers.get('content-type') ?? '' if (!contentType.includes('application/json')) { throw new Error('Version response is not JSON') } const data = (await response.json()) as VersionResponse const version = normalizeVersionResponse(data) if (!version) { throw new Error('Version response is empty') } if (isSuperAdminUrl(url)) { localStorage.setItem(VERSION_SUCCESS_URL_KEY, url) } else { clearStaleVersionSuccessUrl(url) } return version } finally { window.clearTimeout(timeoutId) } } async function fetchVersionWithRetry(rounds = VERSION_RETRY_ROUNDS): Promise { 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 { const version = await fetchVersion(url, timeout) if (version) { return version } } catch (error) { clearStaleVersionSuccessUrl(url) console.warn(`Version failed from ${url}. Round: ${round}`, error) } } } return null } export function resetVersionCheckCache() { versionFetchPromise = null } export async function loadRemoteAppVersion(options?: { rounds?: number force?: boolean }): Promise { const rounds = options?.rounds ?? VERSION_RETRY_ROUNDS const force = options?.force ?? false if (force) { resetVersionCheckCache() } if (versionFetchPromise) { return versionFetchPromise } versionFetchPromise = fetchVersionWithRetry(rounds).finally(() => { versionFetchPromise = null }) return versionFetchPromise }