196 lines
4.6 KiB
JavaScript
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
|
|
};
|
|
})();
|