many edits
This commit is contained in:
+7
-3
@@ -63,7 +63,9 @@ const fetchJson = async (url: string, token: string) => {
|
||||
});
|
||||
if (!res.ok) {
|
||||
const text = await res.text();
|
||||
throw new Error(`Graph ${res.status}: ${text}`);
|
||||
const err: any = new Error(`Graph ${res.status}: ${text}`);
|
||||
err.status = res.status;
|
||||
throw err;
|
||||
}
|
||||
return res.json();
|
||||
};
|
||||
@@ -158,8 +160,9 @@ app.get('/api/job-files', async (c) => {
|
||||
return c.json({ items: mapped, total: mapped.length, source: q ? 'search' : 'walk', driveId });
|
||||
} catch (err) {
|
||||
const message = (err as Error)?.message || String(err);
|
||||
const status = (err as any)?.status || 500;
|
||||
logLine('logs/error.log', `/api/job-files error: ${message}`);
|
||||
return c.json({ error: message }, 500);
|
||||
return c.json({ error: message }, status);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -187,8 +190,9 @@ app.get('/api/job-file-content', async (c) => {
|
||||
return new Response(res.body, { status: 200, headers });
|
||||
} catch (err) {
|
||||
const message = (err as Error)?.message || String(err);
|
||||
const status = (err as any)?.status || 500;
|
||||
logLine('logs/error.log', `/api/job-file-content error: ${message}`);
|
||||
return c.json({ error: message }, 500);
|
||||
return c.json({ error: message }, status);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+223
-28
@@ -77,8 +77,11 @@
|
||||
<div id="results" aria-live="polite" class="flex flex-col gap-3 flex-1 overflow-auto p-0"></div>
|
||||
<div class="text-xs text-gray-500 mt-5 pt-3 border-t border-gray-200 flex items-center justify-between">
|
||||
<span id="userEmail" class="truncate"></span>
|
||||
<div class="flex items-center gap-2">
|
||||
<button id="signOutBtn" class="px-2 py-1 text-[11px] bg-gray-200 hover:bg-gray-300 rounded border border-gray-300 text-gray-700">Sign out</button>
|
||||
<span id="versionLabel">v1.0.0-beta2</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Detail Modal -->
|
||||
@@ -150,9 +153,9 @@
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-1 relative bg-white">
|
||||
<div id="fileListContainer" class="hidden h-full overflow-y-scroll p-4 pr-5 bg-white" style="scrollbar-gutter: stable both-edges; overscroll-behavior: contain;"></div>
|
||||
<div id="pdfViewer" class="hidden h-full flex flex-col bg-gray-50">
|
||||
<div class="flex-1 relative bg-white flex flex-col overflow-hidden">
|
||||
<div id="fileListContainer" class="hidden flex-1 flex flex-col p-0 bg-white overflow-hidden"></div>
|
||||
<div id="pdfViewer" class="hidden flex-1 flex flex-col bg-gray-50">
|
||||
<div class="flex items-center gap-2 px-3 py-2 border-b border-gray-200 bg-white shadow-sm flex-shrink-0">
|
||||
<div class="flex items-center gap-1">
|
||||
<button id="pdfZoomOut" class="px-2 py-1 bg-gray-200 hover:bg-gray-300 rounded">−</button>
|
||||
@@ -194,12 +197,31 @@
|
||||
const authed = await ensureAuth();
|
||||
if (!authed) return;
|
||||
|
||||
// Make sure a Graph token is available before proceeding; this may refresh silently
|
||||
try {
|
||||
await ensureGraphToken();
|
||||
} catch (err) {
|
||||
console.warn('Graph token missing after refresh; continuing. Preview may prompt for auth.', err?.message || err);
|
||||
}
|
||||
|
||||
// --- Utility ---
|
||||
const fetchNoCache = (url, options={}) => {
|
||||
const headers = { ...(options.headers||{}), ...authHeaders() };
|
||||
return fetch(url + `&_ts=${Date.now()}`, { ...options, headers });
|
||||
};
|
||||
const GRAPH_TOKEN_KEY = 'graphAccessToken';
|
||||
const GRAPH_REAUTH_FLAG = 'graphReauthAttempted';
|
||||
const extractGraphToken = (metaObj={}) => (
|
||||
metaObj.graphAccessToken ||
|
||||
metaObj.graph_token ||
|
||||
metaObj.graphToken ||
|
||||
metaObj.accessToken ||
|
||||
metaObj.access_token ||
|
||||
metaObj.token ||
|
||||
metaObj.rawToken ||
|
||||
metaObj?.authData?.access_token ||
|
||||
''
|
||||
);
|
||||
const getGraphToken = () => {
|
||||
// Prefer runtime token already present in PB model/meta, then localStorage
|
||||
const model = pb?.authStore?.model || {};
|
||||
@@ -240,6 +262,7 @@
|
||||
const filtersPanel = document.getElementById('filtersPanel');
|
||||
const filterToggle = document.getElementById('filterToggle');
|
||||
const userEmailEl = document.getElementById('userEmail');
|
||||
const signOutBtn = document.getElementById('signOutBtn');
|
||||
|
||||
const detailModal = document.getElementById('detailModal');
|
||||
const detailTable = document.getElementById('detailTable');
|
||||
@@ -263,6 +286,53 @@
|
||||
const currentUserEmail = getEmail();
|
||||
if(userEmailEl && currentUserEmail) userEmailEl.textContent = currentUserEmail;
|
||||
|
||||
|
||||
// Ensure Graph token is present; refresh if missing; redirect to signin if still absent
|
||||
async function refreshGraphToken(){
|
||||
try {
|
||||
const authData = await pb.collection('users').authRefresh();
|
||||
const meta = authData?.meta || pb?.authStore?.model?.meta || {};
|
||||
const token = extractGraphToken(meta);
|
||||
if (token) localStorage.setItem(GRAPH_TOKEN_KEY, token);
|
||||
return token || '';
|
||||
} catch(err){
|
||||
console.warn('Graph token refresh failed:', err?.message||err);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureGraphToken(){
|
||||
let token = getGraphToken();
|
||||
if(token) return token;
|
||||
// If PB session is valid, try to refresh; if still missing, redirect to reauth once
|
||||
if (pb?.authStore?.isValid) {
|
||||
token = await refreshGraphToken();
|
||||
if (token) return token;
|
||||
const attempted = localStorage.getItem(GRAPH_REAUTH_FLAG) === '1';
|
||||
if (!attempted) {
|
||||
localStorage.setItem(GRAPH_REAUTH_FLAG, '1');
|
||||
console.warn('Graph token unavailable after refresh; redirecting once to sign-in to obtain a new token.');
|
||||
window.location.href = 'signin.html?reauth=1';
|
||||
throw new Error('GRAPH_TOKEN missing');
|
||||
}
|
||||
console.warn('Graph token unavailable after refresh; reauth already attempted. Proceeding without token.');
|
||||
return '';
|
||||
}
|
||||
// PB session invalid: redirect to signin
|
||||
window.location.href = 'signin.html';
|
||||
throw new Error('GRAPH_TOKEN missing');
|
||||
}
|
||||
|
||||
if (signOutBtn) {
|
||||
signOutBtn.addEventListener('click', () => {
|
||||
try { pb?.authStore?.clear?.(); } catch {}
|
||||
try { localStorage.removeItem(GRAPH_TOKEN_KEY); } catch {}
|
||||
try { localStorage.removeItem('pocketbase_auth'); } catch {}
|
||||
try { sessionStorage.clear(); } catch {}
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
// Load version from backend /version endpoint so UI matches package.json
|
||||
async function loadVersion(){
|
||||
if(!versionLabel) return;
|
||||
@@ -543,6 +613,25 @@
|
||||
// --- Modal & Notes ---
|
||||
function openDetail(job){
|
||||
currentJob = job;
|
||||
|
||||
// Show modal immediately
|
||||
detailModal.classList.remove('hidden');
|
||||
document.getElementById('detailBox').scrollTop = 0;
|
||||
switchTab('info');
|
||||
|
||||
// Show loading state
|
||||
detailTable.innerHTML = '<div class="text-gray-500 text-sm">Loading...</div>';
|
||||
|
||||
// Preload files in background
|
||||
if (job.Job_Folder_Link) {
|
||||
preloadJobFiles(job).catch(err => console.log('Background preload error:', err));
|
||||
}
|
||||
|
||||
// Populate details asynchronously
|
||||
setTimeout(() => populateDetailContent(job), 0);
|
||||
}
|
||||
|
||||
function populateDetailContent(job){
|
||||
detailTable.innerHTML = '';
|
||||
const fields = ["Job_Full_Name","Job_Number","Company_Client","Manager","Contact_Person","Phone_Number","Job_Address","Start_Date","Tax_Exempt","Job_Status","Job_Type"];
|
||||
let i = 0;
|
||||
@@ -724,11 +813,6 @@
|
||||
jobNotes.innerHTML = job.Notes || '';
|
||||
newNote.innerHTML = '';
|
||||
tbButtons.forEach(b=>b.classList.remove('bg-blue-100', 'text-gray-900', 'border-blue-600'));
|
||||
detailModal.classList.remove('hidden');
|
||||
document.getElementById('detailBox').scrollTop = 0;
|
||||
|
||||
// Reset to INFO tab when opening
|
||||
switchTab('info');
|
||||
}
|
||||
const closeModal = ()=>{ detailModal.classList.add('hidden'); renderCards(visibleJobs); };
|
||||
document.getElementById('btnClose').addEventListener('click',closeModal);
|
||||
@@ -894,6 +978,49 @@
|
||||
return 'other';
|
||||
};
|
||||
|
||||
// File cache for preloading
|
||||
const fileCache = new Map();
|
||||
|
||||
async function preloadJobFiles(job) {
|
||||
const link = job.Job_Folder_Link;
|
||||
if (!link || fileCache.has(link)) return; // Skip if no link or already cached
|
||||
|
||||
try {
|
||||
const graphToken = await ensureGraphToken();
|
||||
const headers = graphToken ? { 'x-graph-token': graphToken } : {};
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 15000);
|
||||
let resp = await fetch(`/api/job-files?link=${encodeURIComponent(link)}`, { headers, signal: controller.signal });
|
||||
clearTimeout(timeout);
|
||||
|
||||
// If unauthorized, try to refresh Graph token once, then retry
|
||||
if (resp.status === 401) {
|
||||
const refreshed = await refreshGraphToken();
|
||||
if (refreshed) {
|
||||
headers['x-graph-token'] = refreshed;
|
||||
const retryCtl = new AbortController();
|
||||
const retryTimeout = setTimeout(() => retryCtl.abort(), 15000);
|
||||
resp = await fetch(`/api/job-files?link=${encodeURIComponent(link)}`, { headers, signal: retryCtl.signal });
|
||||
clearTimeout(retryTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
if (resp.ok) {
|
||||
const data = await resp.json();
|
||||
const rootDriveId = data.driveId || '';
|
||||
const items = (data.items || []).map(f => ({
|
||||
...f,
|
||||
driveId: f.driveId || rootDriveId
|
||||
}));
|
||||
fileCache.set(link, { items, driveId: rootDriveId });
|
||||
console.log(`Preloaded ${items.length} files for job ${job.Job_Number}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Preload failed (non-critical):', err);
|
||||
}
|
||||
}
|
||||
|
||||
const formatSize = (bytes) => {
|
||||
if (!bytes && bytes !== 0) return '';
|
||||
if (bytes < 1024) return `${bytes} B`;
|
||||
@@ -905,14 +1032,29 @@
|
||||
const getFileIcon = (name, contentType) => {
|
||||
const ext = (name || '').toLowerCase().split('.').pop();
|
||||
const type = (contentType || '').toLowerCase();
|
||||
if (ext === 'pdf' || type.includes('pdf')) return '📄';
|
||||
if (['doc', 'docx'].includes(ext) || type.includes('word')) return '📝';
|
||||
if (['xls', 'xlsx'].includes(ext) || type.includes('excel') || type.includes('spreadsheet')) return '📊';
|
||||
if (['ppt', 'pptx'].includes(ext) || type.includes('powerpoint') || type.includes('presentation')) return '📽️';
|
||||
if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg'].includes(ext) || type.includes('image')) return '🖼️';
|
||||
if (['zip', 'rar', '7z', 'tar', 'gz'].includes(ext) || type.includes('zip') || type.includes('compressed')) return '📦';
|
||||
if (['txt', 'csv', 'log'].includes(ext) || type.includes('text')) return '📃';
|
||||
return '📎';
|
||||
|
||||
if (ext === 'pdf' || type.includes('pdf'))
|
||||
return '<svg class="w-6 h-6" viewBox="0 0 24 24" fill="none"><rect fill="#D32F2F" width="24" height="24" rx="2"/><text x="12" y="16" font-size="10" font-weight="bold" fill="white" text-anchor="middle">PDF</text></svg>';
|
||||
|
||||
if (['doc', 'docx'].includes(ext) || type.includes('word'))
|
||||
return '<svg class="w-6 h-6" viewBox="0 0 24 24" fill="none"><rect fill="#2B579A" width="24" height="24" rx="2"/><text x="12" y="16" font-size="10" font-weight="bold" fill="white" text-anchor="middle">W</text></svg>';
|
||||
|
||||
if (['xls', 'xlsx'].includes(ext) || type.includes('excel') || type.includes('spreadsheet'))
|
||||
return '<svg class="w-6 h-6" viewBox="0 0 24 24" fill="none"><rect fill="#217346" width="24" height="24" rx="2"/><text x="12" y="16" font-size="10" font-weight="bold" fill="white" text-anchor="middle">X</text></svg>';
|
||||
|
||||
if (['ppt', 'pptx'].includes(ext) || type.includes('powerpoint') || type.includes('presentation'))
|
||||
return '<svg class="w-6 h-6" viewBox="0 0 24 24" fill="none"><rect fill="#D35230" width="24" height="24" rx="2"/><text x="12" y="16" font-size="10" font-weight="bold" fill="white" text-anchor="middle">P</text></svg>';
|
||||
|
||||
if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg'].includes(ext) || type.includes('image'))
|
||||
return '<svg class="w-6 h-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="20" rx="2"/><circle cx="8" cy="8" r="2"/><path d="M21 15l-5-5L5 21"/></svg>';
|
||||
|
||||
if (['zip', 'rar', '7z', 'tar', 'gz'].includes(ext) || type.includes('zip') || type.includes('compressed'))
|
||||
return '<svg class="w-6 h-6" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14zm-5-9h-3v4h3v-4zm0-3h-3v2h3V7z"/></svg>';
|
||||
|
||||
if (['txt', 'csv', 'log'].includes(ext) || type.includes('text'))
|
||||
return '<svg class="w-6 h-6" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6z"/><path d="M16 13H8M16 17H8M10 9H8"/></svg>';
|
||||
|
||||
return '<svg class="w-6 h-6" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6z"/></svg>';
|
||||
};
|
||||
|
||||
const canPreviewInline = (name, contentType) => {
|
||||
@@ -1191,7 +1333,7 @@
|
||||
return `
|
||||
<div class="flex items-center justify-between py-2 border-b border-gray-100">
|
||||
<div class="flex items-center min-w-0 gap-2">
|
||||
<span class="text-2xl flex-shrink-0">${icon}</span>
|
||||
<span class="text-gray-600 flex-shrink-0">${icon}</span>
|
||||
<div class="min-w-0">
|
||||
<div class="font-medium text-gray-800 truncate" title="${f.name}">${f.name}</div>
|
||||
<div class="text-xs text-gray-500">
|
||||
@@ -1224,11 +1366,11 @@
|
||||
let groupsHost = fileListContainer.querySelector('.file-groups');
|
||||
if (!groupsHost) {
|
||||
fileListContainer.innerHTML = `
|
||||
<div class="flex items-center gap-2 mb-3 file-search-row">
|
||||
<div class="flex items-center gap-2 mb-3 file-search-row px-4 pt-4 flex-shrink-0">
|
||||
<input id="fileSearchInput" type="search" placeholder="Search files" value="${fileListState.filter}" class="flex-1 px-3 py-2 border border-gray-300 rounded-lg" />
|
||||
<a data-folder-link href="${folderLink}" target="_blank" rel="noopener" class="px-3 py-2 bg-gray-200 hover:bg-gray-300 rounded-lg text-sm text-gray-800">Open folder</a>
|
||||
</div>
|
||||
<div class="file-groups"></div>
|
||||
<div class="file-groups flex-1 overflow-auto px-4 pb-4"></div>
|
||||
`;
|
||||
groupsHost = fileListContainer.querySelector('.file-groups');
|
||||
const searchInput = document.getElementById('fileSearchInput');
|
||||
@@ -1263,15 +1405,22 @@
|
||||
fileListState.filter = '';
|
||||
fileListState.jobNumber = job.Job_Number || '';
|
||||
|
||||
const graphToken = getGraphToken();
|
||||
const headers = graphToken ? { 'x-graph-token': graphToken } : {};
|
||||
if (!graphToken) console.warn('Graph token not found locally; relying on backend default token if configured.');
|
||||
|
||||
// Show modal in list mode
|
||||
iframeViewerModal.classList.remove('hidden');
|
||||
iframeTitle.textContent = job.Job_Number ? `Job# ${job.Job_Number}` : 'Job Files';
|
||||
fileListContainer.classList.remove('hidden');
|
||||
iframeViewer.classList.add('hidden');
|
||||
|
||||
// Check if we have cached files
|
||||
const cached = fileCache.get(link);
|
||||
if (cached) {
|
||||
console.log('Using cached files for job', job.Job_Number);
|
||||
fileListState.items = cached.items;
|
||||
renderFileGroups(link);
|
||||
return;
|
||||
}
|
||||
|
||||
// If not cached, show loading and fetch
|
||||
iframeLoader.classList.remove('hidden');
|
||||
iframeLoader.innerHTML = `
|
||||
<div class="text-center">
|
||||
@@ -1280,27 +1429,73 @@
|
||||
</div>
|
||||
`;
|
||||
|
||||
const graphToken = await ensureGraphToken();
|
||||
const headers = graphToken ? { 'x-graph-token': graphToken } : {};
|
||||
|
||||
try {
|
||||
const resp = await fetch(`/api/job-files?link=${encodeURIComponent(link)}`, { headers });
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 15000);
|
||||
let resp = await fetch(`/api/job-files?link=${encodeURIComponent(link)}`, { headers, signal: controller.signal });
|
||||
clearTimeout(timeout);
|
||||
|
||||
// If unauthorized, try to refresh Graph token once, then retry
|
||||
if (resp.status === 401) {
|
||||
const refreshed = await refreshGraphToken();
|
||||
if (refreshed) {
|
||||
headers['x-graph-token'] = refreshed;
|
||||
const retryCtl = new AbortController();
|
||||
const retryTimeout = setTimeout(() => retryCtl.abort(), 15000);
|
||||
resp = await fetch(`/api/job-files?link=${encodeURIComponent(link)}`, { headers, signal: retryCtl.signal });
|
||||
clearTimeout(retryTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
if (resp.status === 401) throw new Error('AUTH_EXPIRED');
|
||||
if (resp.status === 500) throw new Error('GRAPH_TOKEN missing or expired');
|
||||
if (!resp.ok) throw new Error(`File list failed: ${resp.status}`);
|
||||
const data = await resp.json();
|
||||
const rootDriveId = data.driveId || '';
|
||||
fileListState.items = (data.items || []).map(f => ({
|
||||
const items = (data.items || []).map(f => ({
|
||||
...f,
|
||||
driveId: f.driveId || rootDriveId
|
||||
}));
|
||||
|
||||
// Cache the results
|
||||
fileCache.set(link, { items, driveId: rootDriveId });
|
||||
fileListState.items = items;
|
||||
|
||||
iframeLoader.classList.add('hidden');
|
||||
renderFileGroups(link);
|
||||
} catch (err) {
|
||||
console.error('job-files error', err);
|
||||
const authExpired = err?.message === 'AUTH_EXPIRED' || String(err || '').includes('401');
|
||||
const timedOut = err?.name === 'AbortError';
|
||||
const friendly = authExpired
|
||||
? 'Your Microsoft Graph session expired. Click Re-authenticate to sign in again.'
|
||||
: timedOut
|
||||
? 'Request timed out. If this keeps happening, re-authenticate and retry.'
|
||||
: (err?.message || 'Unknown error');
|
||||
iframeLoader.classList.remove('hidden');
|
||||
iframeLoader.innerHTML = `
|
||||
<div class="text-center max-w-md mx-auto px-4">
|
||||
<div class="text-red-600 text-lg font-semibold mb-2">Unable to load files</div>
|
||||
<div class="text-gray-600 text-sm mb-4">${err?.message || 'Unknown error'}</div>
|
||||
<div class="text-center max-w-md mx-auto px-4 space-y-3">
|
||||
<div class="text-red-600 text-lg font-semibold">Unable to load files</div>
|
||||
<div class="text-gray-600 text-sm">${friendly}</div>
|
||||
<div class="flex items-center justify-center gap-2 flex-wrap">
|
||||
<a href="${link}" target="_blank" rel="noopener" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 inline-block">Open folder instead</a>
|
||||
<button id="reauthBtn" class="px-4 py-2 bg-gray-200 hover:bg-gray-300 rounded-lg text-sm text-gray-800">Re-authenticate</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
const reauthBtn = document.getElementById('reauthBtn');
|
||||
if (reauthBtn) {
|
||||
reauthBtn.addEventListener('click', () => {
|
||||
try { pb?.authStore?.clear?.(); } catch {}
|
||||
try { localStorage.removeItem(GRAPH_TOKEN_KEY); } catch {}
|
||||
try { localStorage.removeItem('pocketbase_auth'); } catch {}
|
||||
try { sessionStorage.clear(); } catch {}
|
||||
window.location.href = 'signin.html';
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-1
@@ -49,6 +49,7 @@
|
||||
const errorEl = document.getElementById('error');
|
||||
const userInfo = document.getElementById('userInfo');
|
||||
const continueBtn = document.getElementById('continueBtn');
|
||||
const GRAPH_REAUTH_FLAG = 'graphReauthAttempted';
|
||||
|
||||
function goToIndex() {
|
||||
window.location.href = 'index.html';
|
||||
@@ -100,15 +101,25 @@
|
||||
const email = authData?.meta?.rawUser?.email || authData?.record?.email || pb.authStore.model?.email || 'Not available';
|
||||
const graphToken = extractGraphToken(authData);
|
||||
console.log('Extracted Graph token:', graphToken ? graphToken.substring(0, 30) + '...' : 'NOT FOUND');
|
||||
if (graphToken) localStorage.setItem(GRAPH_TOKEN_KEY, graphToken);
|
||||
if (graphToken) {
|
||||
localStorage.setItem(GRAPH_TOKEN_KEY, graphToken);
|
||||
localStorage.removeItem(GRAPH_REAUTH_FLAG);
|
||||
}
|
||||
showAuthedUI(displayName, email);
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const reauth = params.get('reauth') === '1';
|
||||
if (pb.authStore.isValid) {
|
||||
try {
|
||||
const authData = await pb.collection('users').authRefresh();
|
||||
displayUserInfo(authData);
|
||||
// If we came from a reauth flow and now have a token, go back automatically
|
||||
const hasToken = localStorage.getItem(GRAPH_TOKEN_KEY);
|
||||
if (reauth && hasToken) {
|
||||
window.location.href = 'index.html';
|
||||
}
|
||||
} catch (err) {
|
||||
pb.authStore.clear();
|
||||
logout();
|
||||
|
||||
Reference in New Issue
Block a user