5fb269cc0a
- 保存先写本地缓存,登录后可选云端同步;退出登录不再因文件操作跳转登录 - 修复 Layui 遮罩残留、design2 保存后线条、toast 被 finally 提前关闭 - 保存过程恢复 loading spinner;成功提示 2.5 秒 - 云端 payload 瘦身与体积超限提示;后端 schema 迁移与 FileService 容错 Co-authored-by: Cursor <cursoragent@cursor.com>
113 lines
3.6 KiB
JavaScript
113 lines
3.6 KiB
JavaScript
(function () {
|
|
'use strict';
|
|
|
|
var refreshPromise = null;
|
|
|
|
function getAccessToken() {
|
|
try { return localStorage.getItem('soon_access') || ''; } catch (e) { return ''; }
|
|
}
|
|
|
|
function getRefreshToken() {
|
|
try { return localStorage.getItem('soon_refresh') || ''; } catch (e) { return ''; }
|
|
}
|
|
|
|
function clearSession() {
|
|
try {
|
|
localStorage.removeItem('soon_access');
|
|
localStorage.removeItem('soon_refresh');
|
|
} catch (e) { /* ignore */ }
|
|
}
|
|
|
|
function getApiBase() {
|
|
var cfg = (typeof window !== 'undefined' && window.SOON_DEPLOY_CONFIG && window.SOON_DEPLOY_CONFIG.api_v1_base);
|
|
if (cfg) return String(cfg).replace(/\/+$/, '');
|
|
if (typeof window !== 'undefined' && window.location) {
|
|
return window.location.origin + '/api/v1';
|
|
}
|
|
return '/api/v1';
|
|
}
|
|
|
|
function redirectToLogin() {
|
|
var path = 'login.web.html';
|
|
try {
|
|
var current = window.location.pathname.replace(/^.*\/pages\//, '').replace(/^\//, '');
|
|
if (current && current.indexOf('login.web.html') !== 0) {
|
|
var redirect = typeof soonSafeRedirect === 'function'
|
|
? soonSafeRedirect(current + window.location.search, 'index.web.html')
|
|
: 'index.web.html';
|
|
path = 'login.web.html?redirect=' + encodeURIComponent(redirect);
|
|
}
|
|
} catch (e) { /* use default */ }
|
|
window.location.href = path;
|
|
}
|
|
|
|
function refreshAccessToken() {
|
|
if (refreshPromise) return refreshPromise;
|
|
var refresh = getRefreshToken();
|
|
if (!refresh) {
|
|
return Promise.reject(new Error('no_refresh_token'));
|
|
}
|
|
refreshPromise = fetch(getApiBase() + '/auth/refresh', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ refresh_token: refresh }),
|
|
}).then(function (r) {
|
|
return r.json().then(function (j) {
|
|
if (!r.ok || !j.ok || !j.data || !j.data.access_token) {
|
|
throw new Error(j.message || 'refresh_failed');
|
|
}
|
|
localStorage.setItem('soon_access', j.data.access_token);
|
|
if (j.data.refresh_token) {
|
|
localStorage.setItem('soon_refresh', j.data.refresh_token);
|
|
}
|
|
return j.data.access_token;
|
|
});
|
|
}).finally(function () {
|
|
refreshPromise = null;
|
|
});
|
|
return refreshPromise;
|
|
}
|
|
|
|
function soonAuthedFetch(url, opts) {
|
|
opts = opts || {};
|
|
var headers = Object.assign({}, opts.headers || {});
|
|
var token = getAccessToken();
|
|
if (token) {
|
|
headers['Authorization'] = 'Bearer ' + token;
|
|
} else {
|
|
delete headers['Authorization'];
|
|
delete headers['authorization'];
|
|
}
|
|
opts.headers = headers;
|
|
|
|
var fullUrl = url;
|
|
if (url.indexOf('http') !== 0) {
|
|
fullUrl = getApiBase() + '/' + String(url).replace(/^\/+/, '');
|
|
}
|
|
|
|
var hadSession = !!(token || getRefreshToken());
|
|
|
|
return fetch(fullUrl, opts).then(function (response) {
|
|
if (response.status !== 401) return response;
|
|
if (!getRefreshToken()) return response;
|
|
return refreshAccessToken().then(function (newToken) {
|
|
var retryHeaders = Object.assign({}, opts.headers || {});
|
|
retryHeaders['Authorization'] = 'Bearer ' + newToken;
|
|
var retryOpts = Object.assign({}, opts, { headers: retryHeaders });
|
|
return fetch(fullUrl, retryOpts);
|
|
}).catch(function () {
|
|
if (hadSession) {
|
|
clearSession();
|
|
redirectToLogin();
|
|
}
|
|
return response;
|
|
});
|
|
});
|
|
}
|
|
|
|
window.soonAuthedFetch = soonAuthedFetch;
|
|
window.soonClearAuthSession = clearSession;
|
|
window.soonGetAccessToken = getAccessToken;
|
|
window.soonRedirectToLogin = redirectToLogin;
|
|
})();
|