Files
BackOne/app-shell/dist/auth-store.js
T
2026-06-30 13:10:35 +07:00

196 lines
4.6 KiB
JavaScript

/**
* BackOne API auth — same token as backone-cli (~/.backOneOneAuthToken / X-ZT1-Auth).
*/
window.BackOneAuth = (function () {
const LS_KEY = 'backone.authToken';
let cache = null;
function trimToken(s) {
return String(s || '').replace(/\s+/g, '');
}
function tokenFromUrl() {
try {
const params = new URLSearchParams(location.search);
const fromQuery = trimToken(params.get('auth') || params.get('token'));
if (fromQuery) return fromQuery;
const hash = (location.hash || '').replace(/^#\/?/, '');
const hashParams = new URLSearchParams(hash.includes('?') ? hash.split('?')[1] : '');
return trimToken(hashParams.get('auth') || hashParams.get('token'));
} catch (_) {
return '';
}
}
async function probeToken(token) {
if (!token) return false;
try {
const res = await fetch('/network', {
cache: 'no-store',
headers: { 'X-ZT1-Auth': token }
});
return res.ok;
} catch (_) {
return false;
}
}
async function fetchAuthtoken() {
for (const path of ['/api/backone/authtoken', '/app/api/authtoken']) {
try {
const res = await fetch(path, { cache: 'no-store' });
if (res.ok) {
const data = await res.json();
const token = trimToken(data && data.token);
if (token) return token;
}
} catch (_) {
/* try next */
}
}
return '';
}
async function resolveToken() {
const urlToken = tokenFromUrl();
if (urlToken) {
if (await probeToken(urlToken)) {
return urlToken;
}
}
const stored = trimToken(localStorage.getItem(LS_KEY));
if (stored && await probeToken(stored)) {
return stored;
}
const apiToken = await fetchAuthtoken();
if (apiToken && await probeToken(apiToken)) {
return apiToken;
}
if (stored) return stored;
if (apiToken) return apiToken;
if (urlToken) return urlToken;
return '';
}
async function getToken() {
if (cache) return cache;
cache = await resolveToken();
return cache || '';
}
async function authHeaders(extra) {
const token = await getToken();
const headers = Object.assign({}, extra || {});
if (token) {
headers['X-ZT1-Auth'] = token;
}
return headers;
}
async function fetchWithAuth(path, options) {
options = options || {};
const headers = await authHeaders(options.headers || {});
return fetch(path, Object.assign({}, options, { headers }));
}
async function apiGet(path) {
const res = await fetchWithAuth(path, { cache: 'no-store' });
if (!res.ok) throw new Error(`${path} returned ${res.status}`);
return res.json();
}
async function apiGetFirst(paths) {
let lastErr = null;
for (const path of paths) {
try {
return await apiGet(path);
} catch (err) {
lastErr = err;
}
}
throw lastErr || new Error('API request failed');
}
async function apiSend(path, method, body) {
const headers = await authHeaders(
body != null ? { 'Content-Type': 'application/json' } : {}
);
const res = await fetch(path, {
method,
headers,
body: body != null ? JSON.stringify(body) : undefined
});
if (!res.ok) {
const text = await res.text().catch(() => '');
throw new Error(text || `${method} ${path} returned ${res.status}`);
}
if (res.status === 204) {
return { result: true };
}
const text = await res.text();
if (!text) {
return { result: true };
}
try {
return JSON.parse(text);
} catch (_) {
return { result: true };
}
}
async function apiSendFirst(paths, method, body) {
let lastErr = null;
for (const path of paths) {
try {
return await apiSend(path, method, body);
} catch (err) {
lastErr = err;
}
}
throw lastErr || new Error(`${method} failed`);
}
/** Store token from ~/.backOneOneAuthToken (same as backone-cli). */
async function setUserToken(token) {
cache = trimToken(token);
if (cache) {
localStorage.setItem(LS_KEY, cache);
} else {
localStorage.removeItem(LS_KEY);
}
return cache;
}
async function validateCurrentToken() {
cache = null;
const token = await getToken();
if (!token) return false;
return probeToken(token);
}
function invalidate() {
cache = null;
}
async function init() {
return getToken();
}
return {
getToken,
authHeaders,
fetchWithAuth,
apiGet,
apiGetFirst,
apiSend,
apiSendFirst,
setUserToken,
validateCurrentToken,
invalidate,
init
};
})();