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:
2026-08-11 15:51:13 +03:30
co-authored by Cursor
parent 7c58501268
commit 512756348b
3 changed files with 106 additions and 3 deletions
+40 -3
View File
@@ -324,6 +324,11 @@
}
function applyConfig(config) {
if (isServerUnreachable(config)) {
showServerUnavailable();
throw new Error('خطا در برقراری ارتباط با سرور');
}
window.__APP_CONFIG__ = config || {};
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) {
if (configResolve) {
configResolve(config);
@@ -406,15 +430,24 @@
console.log('Config loaded successfully from', url, config);
finishConfigLoad(config);
} catch (error) {
if (error && String(error.message || '').includes('خطا در برقراری ارتباط با سرور')) {
showServerUnavailable();
return;
}
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;
try {
applyConfig(cachedConfig);
finishConfigLoad(cachedConfig, error);
return;
} catch (blockedError) {
showServerUnavailable();
return;
}
}
window.__APP_CONFIG__ = {};
@@ -439,6 +472,10 @@
if (!mainScriptLoaded) {
console.warn('Config loader: Timeout reached. Loading main.ts with empty/cached config.');
const cachedConfig = readCachedAppConfig();
if (isServerUnreachable(cachedConfig) || isServerUnreachable(window.__APP_CONFIG__)) {
showServerUnavailable();
return;
}
window.__APP_CONFIG__ = cachedConfig || window.__APP_CONFIG__ || {};
window.__CONFIG_READY__ = true;
+9
View File
@@ -4,6 +4,10 @@ import axios from 'axios'
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
// import { useRouter, useRoute } from 'vue-router'
import { getToken, removeToken } from '@/utils/index'
import {
hideServerUnavailableOverlay,
showServerUnavailableOverlay,
} from '@/utils/serverUnavailable'
import { message } from 'ant-design-vue'
//import { isToken } from '@/utils/is'
//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
// Do something with response data
//console.log('this is fulfilled on interceptors.response', response.data)
hideServerUnavailableOverlay()
return response.data
},
async function (error) {
@@ -79,6 +84,10 @@ _axios.interceptors.response.use(
}*/
//console.log('this is error on interceptors.response', error.config.meta)
//console.log('1111111111', error.response)
if (error.response && error.response.status == 503) {
showServerUnavailableOverlay('خطا در برقراری ارتباط با سرور')
return Promise.reject(error)
}
if (error.response && error.response.status == 401) {
///removeToken()
console.log('error')
+57
View File
@@ -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)
}
}