feat: full-screen lock when server access is blocked
Gate admin boot on server_reachable and show server-unavailable overlay on API 503 for SuperAdmin kill switch. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+40
-3
@@ -324,6 +324,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function applyConfig(config) {
|
function applyConfig(config) {
|
||||||
|
if (isServerUnreachable(config)) {
|
||||||
|
showServerUnavailable();
|
||||||
|
throw new Error('خطا در برقراری ارتباط با سرور');
|
||||||
|
}
|
||||||
|
|
||||||
window.__APP_CONFIG__ = config || {};
|
window.__APP_CONFIG__ = config || {};
|
||||||
window.__CONFIG_READY__ = true;
|
window.__CONFIG_READY__ = true;
|
||||||
|
|
||||||
@@ -344,6 +349,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isServerUnreachable(config) {
|
||||||
|
if (!config || typeof config !== 'object') return false;
|
||||||
|
if (config.server_reachable === false) return true;
|
||||||
|
if (config.user && config.user.server_reachable === false) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showServerUnavailable() {
|
||||||
|
var appRoot = document.querySelector('#app') || document.body;
|
||||||
|
appRoot.innerHTML =
|
||||||
|
'<div style="min-height:100vh;display:flex;align-items:center;justify-content:center;direction:rtl;font-family:sans-serif;padding:24px;background:#fff;color:#111827;">' +
|
||||||
|
'<div style="width:min(420px,100%);text-align:center;border:1px solid rgba(15,23,42,0.08);border-radius:24px;padding:28px 22px;">' +
|
||||||
|
'<div style="font-size:42px;margin-bottom:12px;">⚠️</div>' +
|
||||||
|
'<h2 style="margin:0 0 12px;font-size:20px;">خطا در برقراری ارتباط با سرور</h2>' +
|
||||||
|
'<p style="margin:0 0 20px;color:rgba(15,23,42,0.62);line-height:2;font-size:14px;">لطفاً چند لحظه دیگر دوباره تلاش کنید.</p>' +
|
||||||
|
'<button type="button" onclick="location.reload()" style="padding:11px 22px;border:0;border-radius:12px;background:#2563eb;color:#fff;cursor:pointer;font-weight:700;">تلاش مجدد</button>' +
|
||||||
|
'</div></div>';
|
||||||
|
}
|
||||||
|
|
||||||
function finishConfigLoad(config, error) {
|
function finishConfigLoad(config, error) {
|
||||||
if (configResolve) {
|
if (configResolve) {
|
||||||
configResolve(config);
|
configResolve(config);
|
||||||
@@ -406,15 +430,24 @@
|
|||||||
console.log('Config loaded successfully from', url, config);
|
console.log('Config loaded successfully from', url, config);
|
||||||
finishConfigLoad(config);
|
finishConfigLoad(config);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error && String(error.message || '').includes('خطا در برقراری ارتباط با سرور')) {
|
||||||
|
showServerUnavailable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
console.warn('Config loader: Could not fetch config from any endpoint');
|
console.warn('Config loader: Could not fetch config from any endpoint');
|
||||||
console.warn('Error details:', error && error.message ? error.message : error);
|
console.warn('Error details:', error && error.message ? error.message : error);
|
||||||
console.warn('App will continue with empty/cached config.');
|
console.warn('App will continue with empty/cached config.');
|
||||||
|
|
||||||
const cachedConfig = readCachedAppConfig();
|
const cachedConfig = readCachedAppConfig();
|
||||||
if (cachedConfig) {
|
if (cachedConfig) {
|
||||||
applyConfig(cachedConfig);
|
try {
|
||||||
finishConfigLoad(cachedConfig, error);
|
applyConfig(cachedConfig);
|
||||||
return;
|
finishConfigLoad(cachedConfig, error);
|
||||||
|
return;
|
||||||
|
} catch (blockedError) {
|
||||||
|
showServerUnavailable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__APP_CONFIG__ = {};
|
window.__APP_CONFIG__ = {};
|
||||||
@@ -439,6 +472,10 @@
|
|||||||
if (!mainScriptLoaded) {
|
if (!mainScriptLoaded) {
|
||||||
console.warn('Config loader: Timeout reached. Loading main.ts with empty/cached config.');
|
console.warn('Config loader: Timeout reached. Loading main.ts with empty/cached config.');
|
||||||
const cachedConfig = readCachedAppConfig();
|
const cachedConfig = readCachedAppConfig();
|
||||||
|
if (isServerUnreachable(cachedConfig) || isServerUnreachable(window.__APP_CONFIG__)) {
|
||||||
|
showServerUnavailable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
window.__APP_CONFIG__ = cachedConfig || window.__APP_CONFIG__ || {};
|
window.__APP_CONFIG__ = cachedConfig || window.__APP_CONFIG__ || {};
|
||||||
window.__CONFIG_READY__ = true;
|
window.__CONFIG_READY__ = true;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ import axios from 'axios'
|
|||||||
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
|
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||||
// import { useRouter, useRoute } from 'vue-router'
|
// import { useRouter, useRoute } from 'vue-router'
|
||||||
import { getToken, removeToken } from '@/utils/index'
|
import { getToken, removeToken } from '@/utils/index'
|
||||||
|
import {
|
||||||
|
hideServerUnavailableOverlay,
|
||||||
|
showServerUnavailableOverlay,
|
||||||
|
} from '@/utils/serverUnavailable'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
//import { isToken } from '@/utils/is'
|
//import { isToken } from '@/utils/is'
|
||||||
//import router from '@/router'
|
//import router from '@/router'
|
||||||
@@ -69,6 +73,7 @@ _axios.interceptors.response.use(
|
|||||||
// Any status code that lie within the range of 2xx cause this function to trigger
|
// Any status code that lie within the range of 2xx cause this function to trigger
|
||||||
// Do something with response data
|
// Do something with response data
|
||||||
//console.log('this is fulfilled on interceptors.response', response.data)
|
//console.log('this is fulfilled on interceptors.response', response.data)
|
||||||
|
hideServerUnavailableOverlay()
|
||||||
return response.data
|
return response.data
|
||||||
},
|
},
|
||||||
async function (error) {
|
async function (error) {
|
||||||
@@ -79,6 +84,10 @@ _axios.interceptors.response.use(
|
|||||||
}*/
|
}*/
|
||||||
//console.log('this is error on interceptors.response', error.config.meta)
|
//console.log('this is error on interceptors.response', error.config.meta)
|
||||||
//console.log('1111111111', error.response)
|
//console.log('1111111111', error.response)
|
||||||
|
if (error.response && error.response.status == 503) {
|
||||||
|
showServerUnavailableOverlay('خطا در برقراری ارتباط با سرور')
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
if (error.response && error.response.status == 401) {
|
if (error.response && error.response.status == 401) {
|
||||||
///removeToken()
|
///removeToken()
|
||||||
console.log('error')
|
console.log('error')
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
const OVERLAY_ID = 'sa-server-unavailable-overlay'
|
||||||
|
const MESSAGE = 'خطا در برقراری ارتباط با سرور'
|
||||||
|
|
||||||
|
export function isServerUnreachableConfig(config: unknown): boolean {
|
||||||
|
if (!config || typeof config !== 'object') return false
|
||||||
|
const root = config as Record<string, unknown>
|
||||||
|
const user =
|
||||||
|
root.user && typeof root.user === 'object'
|
||||||
|
? (root.user as Record<string, unknown>)
|
||||||
|
: null
|
||||||
|
|
||||||
|
if (root.server_reachable === false) return true
|
||||||
|
if (user && user.server_reachable === false) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showServerUnavailableOverlay(message = MESSAGE) {
|
||||||
|
if (typeof document === 'undefined') return
|
||||||
|
let root = document.getElementById(OVERLAY_ID)
|
||||||
|
if (!root) {
|
||||||
|
root = document.createElement('div')
|
||||||
|
root.id = OVERLAY_ID
|
||||||
|
document.body.appendChild(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
root.innerHTML = `
|
||||||
|
<div style="position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;direction:rtl;font-family:sans-serif;padding:24px;background:#ffffff;color:#111827;">
|
||||||
|
<div style="width:min(420px,100%);text-align:center;background:#fff;border:1px solid rgba(15,23,42,0.08);border-radius:24px;padding:28px 22px;">
|
||||||
|
<div style="font-size:42px;margin-bottom:12px;">⚠️</div>
|
||||||
|
<h2 style="margin:0 0 12px;font-size:20px;">${message}</h2>
|
||||||
|
<p style="margin:0 0 20px;color:rgba(15,23,42,0.62);line-height:2;font-size:14px;">
|
||||||
|
لطفاً چند لحظه دیگر دوباره تلاش کنید.
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick="location.reload()"
|
||||||
|
style="padding:11px 22px;border:0;border-radius:12px;background:#2563eb;color:white;cursor:pointer;font-weight:700;"
|
||||||
|
>
|
||||||
|
تلاش مجدد
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hideServerUnavailableOverlay() {
|
||||||
|
if (typeof document === 'undefined') return
|
||||||
|
const root = document.getElementById(OVERLAY_ID)
|
||||||
|
if (root) root.remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertServerReachableConfig(config: unknown) {
|
||||||
|
if (isServerUnreachableConfig(config)) {
|
||||||
|
showServerUnavailableOverlay()
|
||||||
|
throw new Error(MESSAGE)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user