diff --git a/index.html b/index.html
index 8dd3789..82cd2ef 100644
--- a/index.html
+++ b/index.html
@@ -341,34 +341,47 @@
]
}
+ function isDevHost() {
+ var host = String(window.location.hostname || '').trim()
+ return host === 'localhost' || host === '127.0.0.1'
+ }
+
function getVersionUrls() {
- var protocol = getProtocol()
var domain = encodeURIComponent(resolveTenantDomain())
var urls = getSuperAdminBases().map(function (base) {
return base + '/get_version?domain=' + domain
})
- return urls.concat([
- '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'
- ])
+ if (isDevHost()) {
+ var protocol = getProtocol()
+ urls = urls.concat([
+ '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 getConfigUrls() {
- var protocol = getProtocol()
var domain = encodeURIComponent(resolveTenantDomain())
var urls = getSuperAdminBases().map(function (base) {
return base + '/get_user?domain=' + domain
})
- return urls.concat([
- 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'
- ])
+ if (isDevHost()) {
+ var protocol = getProtocol()
+ urls = urls.concat([
+ 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'
+ ])
+ }
+
+ return urls
}
function sortVersionUrls(urls) {
@@ -646,10 +659,10 @@
'
⚠️
' +
'خطا در بارگذاری برنامه ' +
'' +
- 'ارتباط با سرور برقرار نشد یا فایلهای برنامه بهدرستی دریافت نشدند.' +
+ 'برقراری ارتباط با سرور امکانپذیر نیست. لطفاً اتصال اینترنت خود را بررسی کرده و صفحه را تازهسازی نمایید.' +
'
' +
'' +
- 'تلاش مجدد' +
+ 'تازهسازی صفحه' +
' ' +
'' +
'پاکسازی و تلاش مجدد' +
@@ -661,8 +674,8 @@
async function cleanFetchAndReload() {
renderRecovering('در حال دریافت تنظیمات جدید برنامه...')
- var version = await loadRemoteVersion(5)
- var freshConfigResult = await fetchFreshConfig(5)
+ var version = await loadRemoteVersion(3)
+ var freshConfigResult = await fetchFreshConfig(3)
clearReadableCookies()
// clearLocalStorageExceptToken()
diff --git a/src/api/superadminSettings.ts b/src/api/superadminSettings.ts
new file mode 100644
index 0000000..f30e67b
--- /dev/null
+++ b/src/api/superadminSettings.ts
@@ -0,0 +1,41 @@
+import {
+ fetchSuperAdminJson,
+ resolveTenantDomain,
+ SA_PROGRAM_SETTINGS_PATH,
+} from '@/config/superadmin'
+
+export type SuperAdminSettingRow = {
+ id?: number
+ key?: string
+ value?: string | number
+ title_fa?: string
+ description?: string
+ is_active?: boolean | number | string
+ type?: string
+ can_edit?: number | boolean | string
+}
+
+function parseSettingsPayload(data: unknown): SuperAdminSettingRow[] {
+ if (Array.isArray(data)) return data as SuperAdminSettingRow[]
+ if (data && typeof data === 'object') {
+ const obj = data as Record
+ if (Array.isArray(obj.settings)) return obj.settings as SuperAdminSettingRow[]
+ if (Array.isArray(obj.items)) return obj.items as SuperAdminSettingRow[]
+ if (Array.isArray(obj.data)) return obj.data as SuperAdminSettingRow[]
+ }
+ return []
+}
+
+/** تنظیمات پیشرفته نسخه از سوپرادمین — فقط GET /api/public/program-settings?domain= */
+export async function fetchSuperAdminProgramSettings(): Promise {
+ const domain = encodeURIComponent(resolveTenantDomain())
+ try {
+ const data = await fetchSuperAdminJson(
+ (base) => `${base}${SA_PROGRAM_SETTINGS_PATH}?domain=${domain}`,
+ )
+ return parseSettingsPayload(data)
+ } catch (error) {
+ console.warn('SuperAdmin program-settings failed after retries:', error)
+ return []
+ }
+}
diff --git a/src/app/bootstrap.ts b/src/app/bootstrap.ts
index d36ec1a..74e9cdf 100644
--- a/src/app/bootstrap.ts
+++ b/src/app/bootstrap.ts
@@ -48,13 +48,13 @@ function showBootstrapError(error: unknown) {
⚠️
خطا در راهاندازی برنامه
- لطفاً اتصال اینترنت خود را بررسی کنید و دوباره تلاش کنید.
+ برقراری ارتباط با سرور امکانپذیر نیست. لطفاً اتصال اینترنت خود را بررسی کرده و صفحه را تازهسازی نمایید.
- تلاش مجدد
+ تازهسازی صفحه
diff --git a/src/config/load-config.ts b/src/config/load-config.ts
index 340c037..e5f47d3 100644
--- a/src/config/load-config.ts
+++ b/src/config/load-config.ts
@@ -1,44 +1,49 @@
import type { AppConfig } from '@/config/types'
import { normalizeAppConfig } from '@/config/app-config'
import { readCachedAppConfig, writeCachedAppConfig } from '@/config/config-cache'
+import {
+ allowSuperAdminFallback,
+ getSuperAdminBase,
+ getSuperAdminBases,
+ isSuperAdminUrl,
+ resolveTenantDomain,
+ SA_RETRY_ROUNDS,
+} from '@/config/superadmin'
const CONFIG_SUCCESS_URL_KEY = 'CONFIG_Success_URL'
-export const CONFIG_RETRY_ROUNDS = 5
-
-const SUPERADMIN_BASES = [
- 'https://adm.montzar.ir',
- 'https://adm.montech.ir',
- 'https://adm.montest.ir',
-] as const
+export const CONFIG_RETRY_ROUNDS = SA_RETRY_ROUNDS
function getProtocol() {
return window.location.protocol === 'https:' ? 'https:' : 'http:'
}
-function resolveTenantDomain() {
- const host = String(window.location.hostname || '').trim()
- if (!host || host === 'localhost' || host === '127.0.0.1') {
- return 'k.montzar.ir'
- }
- return host
-}
-
export function getConfigUrls() {
const protocol = getProtocol()
const domain = encodeURIComponent(resolveTenantDomain())
+ const urls = getSuperAdminBases().map((base) => `${base}/get_user?domain=${domain}`)
- return [
- ...SUPERADMIN_BASES.map((base) => `${base}/get_user?domain=${domain}`),
- `${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`,
- ]
+ if (allowSuperAdminFallback()) {
+ urls.push(
+ `${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`,
+ )
+ }
+
+ return urls
}
export function sortConfigUrls(urls: string[]) {
const lastSuccessUrl = localStorage.getItem(CONFIG_SUCCESS_URL_KEY)
- if (!lastSuccessUrl || !urls.includes(lastSuccessUrl)) {
+ if (
+ !lastSuccessUrl ||
+ !urls.includes(lastSuccessUrl) ||
+ !isSuperAdminUrl(lastSuccessUrl)
+ ) {
+ if (lastSuccessUrl && !isSuperAdminUrl(lastSuccessUrl)) {
+ localStorage.removeItem(CONFIG_SUCCESS_URL_KEY)
+ }
return [...urls]
}
return [lastSuccessUrl, ...urls.filter((url) => url !== lastSuccessUrl)]
@@ -91,7 +96,11 @@ export async function fetchFreshAppConfig(rounds = CONFIG_RETRY_ROUNDS): Promise
const timeout = index === urls.length - 1 ? 7000 : 4000
const config = await fetchConfig(url, timeout)
- localStorage.setItem(CONFIG_SUCCESS_URL_KEY, url)
+ if (isSuperAdminUrl(url)) {
+ localStorage.setItem(CONFIG_SUCCESS_URL_KEY, url)
+ } else {
+ clearStaleConfigSuccessUrl(url)
+ }
writeCachedAppConfig(config)
return config
@@ -102,7 +111,11 @@ export async function fetchFreshAppConfig(rounds = CONFIG_RETRY_ROUNDS): Promise
}
}
- throw new Error('Cannot load app config from any server')
+ throw new Error(
+ getSuperAdminBase()
+ ? `Cannot load from SuperAdmin (${getSuperAdminBase()}).`
+ : 'Cannot load app config from any server',
+ )
}
export async function loadAppConfig(): Promise {
diff --git a/src/config/load-version.ts b/src/config/load-version.ts
index bed66e0..9ef753f 100644
--- a/src/config/load-version.ts
+++ b/src/config/load-version.ts
@@ -1,3 +1,11 @@
+import {
+ allowSuperAdminFallback,
+ getSuperAdminBases,
+ isSuperAdminUrl,
+ resolveTenantDomain,
+ SA_RETRY_ROUNDS,
+} from '@/config/superadmin'
+
type VersionResponse =
| string
| number
@@ -9,10 +17,9 @@ type VersionResponse =
}
const VERSION_SUCCESS_URL_KEY = 'VERSION_Success_URL'
-export const VERSION_RETRY_ROUNDS = 5
+export const VERSION_RETRY_ROUNDS = SA_RETRY_ROUNDS
let versionFetchPromise: Promise | null = null
-let cachedVersionResult: string | null | undefined
function getProtocol() {
return window.location.protocol === 'https:' ? 'https:' : 'http:'
@@ -20,31 +27,34 @@ function getProtocol() {
function getVersionUrls() {
const protocol = getProtocol()
- const host = String(window.location.hostname || '').trim()
- const domain = encodeURIComponent(
- !host || host === 'localhost' || host === '127.0.0.1' ? 'k.montzar.ir' : host,
- )
+ const domain = encodeURIComponent(resolveTenantDomain())
+ const urls = getSuperAdminBases().map((base) => `${base}/get_version?domain=${domain}`)
- return [
- `https://adm.montzar.ir/get_version?domain=${domain}`,
- `https://adm.montech.ir/get_version?domain=${domain}`,
- `https://adm.montest.ir/get_version?domain=${domain}`,
- '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`,
- ]
+ 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) {
- return urls
+ if (
+ !lastSuccessUrl ||
+ !urls.includes(lastSuccessUrl) ||
+ !isSuperAdminUrl(lastSuccessUrl)
+ ) {
+ if (lastSuccessUrl && !isSuperAdminUrl(lastSuccessUrl)) {
+ localStorage.removeItem(VERSION_SUCCESS_URL_KEY)
+ }
+ return [...urls]
}
-
- const others = urls.filter((url) => url !== lastSuccessUrl)
- return [lastSuccessUrl, ...others]
+ return [lastSuccessUrl, ...urls.filter((url) => url !== lastSuccessUrl)]
}
function clearStaleVersionSuccessUrl(url: string) {
@@ -95,10 +105,10 @@ async function fetchVersion(url: string, timeout = 3500): Promise
const response = await fetch(url, {
method: 'GET',
headers: {
- Accept: 'application/json'
+ Accept: 'application/json',
},
cache: 'no-store',
- signal: controller.signal
+ signal: controller.signal,
})
if (!response.ok) {
@@ -106,18 +116,22 @@ async function fetchVersion(url: string, timeout = 3500): Promise
}
const contentType = response.headers.get('content-type') ?? ''
+ if (!contentType.includes('application/json')) {
+ throw new Error('Version response is not JSON')
+ }
- const data = contentType.includes('application/json')
- ? ((await response.json()) as VersionResponse)
- : ((await response.text()) as VersionResponse)
-
+ const data = (await response.json()) as VersionResponse
const version = normalizeVersionResponse(data)
if (!version) {
throw new Error('Version response is empty')
}
- localStorage.setItem(VERSION_SUCCESS_URL_KEY, url)
+ if (isSuperAdminUrl(url)) {
+ localStorage.setItem(VERSION_SUCCESS_URL_KEY, url)
+ } else {
+ clearStaleVersionSuccessUrl(url)
+ }
return version
} finally {
@@ -149,7 +163,6 @@ async function fetchVersionWithRetry(rounds = VERSION_RETRY_ROUNDS): Promise {
- cachedVersionResult = version
- return version
- })
- .finally(() => {
- versionFetchPromise = null
- })
+ versionFetchPromise = fetchVersionWithRetry(rounds).finally(() => {
+ versionFetchPromise = null
+ })
return versionFetchPromise
}
diff --git a/src/config/superadmin.ts b/src/config/superadmin.ts
new file mode 100644
index 0000000..6efb26d
--- /dev/null
+++ b/src/config/superadmin.ts
@@ -0,0 +1,83 @@
+/** سوپرادمین — مستر، بعد دو اسلیو */
+export const SUPERADMIN_BASES = [
+ 'https://adm.montzar.ir',
+ 'https://adm.montech.ir',
+ 'https://adm.montest.ir',
+] as const
+
+/** هر API: سه دامنه × سه دور */
+export const SA_RETRY_ROUNDS = 3
+
+/** تنظیمات پیشرفته نسخه — همان دادهٔ /get_settings بدون نیاز به nginx جدا */
+export const SA_PROGRAM_SETTINGS_PATH = '/api/public/program-settings'
+
+function normalizeBase(url: string) {
+ return String(url || '').trim().replace(/\/$/, '')
+}
+
+export function getSuperAdminBases(): string[] {
+ const fromEnv = normalizeBase(String(import.meta.env.VITE_SUPERADMIN_URL || ''))
+ const bases = [...SUPERADMIN_BASES]
+ if (fromEnv) {
+ return [fromEnv, ...bases.filter((base) => base !== fromEnv)]
+ }
+ return bases
+}
+
+export function getSuperAdminBase(): string {
+ return getSuperAdminBases()[0] || ''
+}
+
+export function resolveTenantDomain(): string {
+ const override = String(import.meta.env.VITE_TENANT_DOMAIN || '').trim()
+ if (override) return override
+
+ const host = String(window.location.hostname || '').trim()
+ if (!host || host === 'localhost' || host === '127.0.0.1') {
+ return 'k.montzar.ir'
+ }
+ return host
+}
+
+export function isSuperAdminUrl(url: string): boolean {
+ return getSuperAdminBases().some((base) => url.startsWith(base))
+}
+
+export function allowSuperAdminFallback(): boolean {
+ return Boolean(import.meta.env.DEV)
+}
+
+export async function fetchSuperAdminJson(
+ buildUrl: (base: string) => string,
+ timeout = 5000,
+): Promise {
+ let lastError: unknown
+ const bases = getSuperAdminBases()
+ for (let round = 1; round <= SA_RETRY_ROUNDS; round++) {
+ for (const base of bases) {
+ const controller = new AbortController()
+ const timer = window.setTimeout(() => controller.abort(), timeout)
+ try {
+ const res = await fetch(buildUrl(base), {
+ method: 'GET',
+ cache: 'no-store',
+ signal: controller.signal,
+ headers: { Accept: 'application/json' },
+ })
+ if (!res.ok) {
+ throw new Error(`HTTP ${res.status}`)
+ }
+ const contentType = res.headers.get('content-type') || ''
+ if (!contentType.includes('application/json')) {
+ throw new Error('SuperAdmin response is not JSON')
+ }
+ return (await res.json()) as T
+ } catch (error) {
+ lastError = error
+ } finally {
+ window.clearTimeout(timer)
+ }
+ }
+ }
+ throw lastError instanceof Error ? lastError : new Error('SuperAdmin unreachable')
+}
diff --git a/src/store/settingsStore.ts b/src/store/settingsStore.ts
index 3760fa3..91d63cb 100644
--- a/src/store/settingsStore.ts
+++ b/src/store/settingsStore.ts
@@ -1,6 +1,7 @@
import { computed, ref } from 'vue'
import { defineStore } from 'pinia'
import { getSettings } from '@/api/configuration'
+import { fetchSuperAdminProgramSettings } from '@/api/superadminSettings'
import { resolveMeltedGoldLabel, resolveMetalTypeLabel } from '@/utils/metalLabels'
import { resolveTradeWeightUnit } from '@/utils/weightUnit'
@@ -65,9 +66,15 @@ export const useSettingsStore = defineStore('settings', () => {
fetchPromise = (async () => {
try {
- const data = await getSettings()
- if (Array.isArray(data)) {
- mergeSettings(data)
+ const [local, remote] = await Promise.all([
+ getSettings().catch(() => []),
+ fetchSuperAdminProgramSettings(),
+ ])
+ if (Array.isArray(local)) {
+ mergeSettings(local)
+ }
+ if (remote.length) {
+ mergeSettings(remote as AppSetting[])
}
} catch (error) {
console.log(error)