[READ-ONLY] Mirror of https://github.com/aaronateataco/ermine. A stoat web client erminechat.vercel.app
0

Configure Feed

Select the types of activity you want to include in your feed.

Stop CORS user lookup spam and add fallback guard

Fascinating Pistachio (Feb 14, 2026, 3:01 PM UTC) 21d64196 4e2b0c7e

+27 -9
+27 -9
src/App.jsx
··· 297 297 const preloadedMembersRef = useRef({}); 298 298 const pendingUserFetchRef = useRef(new Set()); 299 299 const membersLoadIdRef = useRef(0); 300 + const canFetchMissingUsersRef = useRef(true); 301 + const loggedMissingUserFetchErrorRef = useRef(false); 300 302 301 303 const serverList = useMemo(() => Object.values(servers), [servers]); 302 304 ··· 362 364 }; 363 365 364 366 const fetchMissingUsers = async (messageList = []) => { 367 + if (!canFetchMissingUsersRef.current) return; 368 + 365 369 const unresolved = new Set(); 366 370 367 371 messageList.forEach((entry) => { 368 372 const authorId = typeof entry?.author === 'string' ? entry.author : entry?.author?._id; 369 - if (!authorId || users[authorId] || pendingUserFetchRef.current.has(authorId)) return; 373 + if (!authorId || authorId === '00000000000000000000000000' || users[authorId] || pendingUserFetchRef.current.has(authorId)) return; 370 374 unresolved.add(authorId); 371 375 }); 372 376 373 377 if (!unresolved.size) return; 374 378 375 - unresolved.forEach((id) => pendingUserFetchRef.current.add(id)); 379 + const MAX_BACKGROUND_USER_FETCHES = 6; 380 + const limitedIds = [...unresolved].slice(0, MAX_BACKGROUND_USER_FETCHES); 381 + limitedIds.forEach((id) => pendingUserFetchRef.current.add(id)); 376 382 377 383 await Promise.all( 378 - [...unresolved].map(async (userId) => { 384 + limitedIds.map(async (userId) => { 379 385 try { 380 - const res = await fetch(`${config.apiUrl}/users/${userId}`, { 381 - headers: { 'x-session-token': auth.token }, 382 - }); 383 - if (!res.ok) return; 386 + const res = await fetch(`${config.apiUrl}/users/${userId}`); 387 + if (!res.ok) { 388 + if (res.status === 401 || res.status === 403 || res.status === 405) { 389 + canFetchMissingUsersRef.current = false; 390 + } 391 + return; 392 + } 384 393 const data = await res.json(); 385 394 if (data?._id) upsertUsers([data]); 386 - } catch { 387 - // no-op 395 + } catch (error) { 396 + const message = (error && error.message) || ''; 397 + if (message.includes('Failed to fetch') || message.includes('CORS')) { 398 + canFetchMissingUsersRef.current = false; 399 + } 400 + if (!loggedMissingUserFetchErrorRef.current) { 401 + loggedMissingUserFetchErrorRef.current = true; 402 + console.warn('Disabling background user lookup due to network/CORS restrictions.'); 403 + } 388 404 } finally { 389 405 pendingUserFetchRef.current.delete(userId); 390 406 } ··· 819 835 preloadedMembersRef.current = {}; 820 836 pendingUserFetchRef.current = new Set(); 821 837 membersLoadIdRef.current = 0; 838 + canFetchMissingUsersRef.current = true; 839 + loggedMissingUserFetchErrorRef.current = false; 822 840 setIsMembersLoading(false); 823 841 setView('login'); 824 842 setStatus('disconnected');