feat: simplify pollSessions to use federation proxy endpoint

This commit is contained in:
Brian Krabach
2026-04-01 12:54:08 -07:00
parent 0e3ed96c16
commit 442cfcc051
2 changed files with 78 additions and 67 deletions
+14 -67
View File
@@ -303,78 +303,25 @@ function setConnectionStatus(level) {
* @returns {Promise<void>}
*/
async function pollSessions() {
// Falls back to local-only if _sources is empty
if (_sources.length === 0) {
try {
const res = await api('GET', '/api/sessions');
const sessions = await res.json();
const prev = _currentSessions;
_currentSessions = sessions;
_pollFailCount = 0;
setConnectionStatus('ok');
renderGrid(sessions);
renderSidebar(sessions, _viewingSession);
handleBellTransitions(prev, sessions);
updateSessionPill(sessions);
} catch (err) {
_pollFailCount++;
setConnectionStatus(_pollFailCount <= 2 ? 'warn' : 'err');
}
return;
}
// Multi-source parallel polling
const now = Date.now();
const fetchResults = await Promise.all(
_sources.map(async (source) => {
// Skip sources currently in backoff (nextRetryAt in future)
if (source.nextRetryAt && now < source.nextRetryAt) {
return null;
}
try {
const res = await api('GET', '/api/sessions', undefined, source.url || undefined);
const sessions = await res.json();
// Reset source status on success
source.status = 'authenticated';
source.backoffMs = 2000;
delete source.nextRetryAt;
return { source, sessions };
} catch (err) {
if (err.status === 401 || err.status === 403) {
source.status = 'auth_required';
} else {
source.status = 'unreachable';
// Exponential backoff: current * 2, capped at 30s
const newBackoff = Math.min((source.backoffMs || 2000) * 2, 30000);
source.backoffMs = newBackoff;
source.nextRetryAt = Date.now() + newBackoff;
}
return null;
}
}),
);
// Filter skipped/failed sources and merge results
const validResults = fetchResults.filter((r) => r !== null);
const merged = mergeSources(validResults);
// Connection status is based on local source health
const localSource = _sources.find((s) => s.type === 'local' || s.url === '');
if (localSource && localSource.status === 'authenticated') {
try {
var endpoint = (_serverSettings && _serverSettings.multi_device_enabled)
? '/api/federation/sessions'
: '/api/sessions';
const res = await api('GET', endpoint);
const sessions = await res.json();
const prev = _currentSessions;
_currentSessions = sessions;
_pollFailCount = 0;
setConnectionStatus('ok');
} else {
renderGrid(sessions);
renderSidebar(sessions, _viewingSession);
handleBellTransitions(prev, sessions);
updateSessionPill(sessions);
updateFaviconBadge();
} catch (err) {
_pollFailCount++;
setConnectionStatus(_pollFailCount <= 2 ? 'warn' : 'err');
}
const prev = _currentSessions;
_currentSessions = merged;
renderGrid(merged);
renderSidebar(merged, _viewingSession);
handleBellTransitions(prev, merged);
updateSessionPill(merged);
updateFaviconBadge();
}
/**
+64
View File
@@ -375,6 +375,70 @@ test('pollSessions calls renderSidebar when viewMode is fullscreen', async () =>
app._setViewMode('grid');
});
// --- pollSessions federation endpoint ---
test('pollSessions source includes /api/federation/sessions', () => {
assert.ok(
app.pollSessions.toString().includes('/api/federation/sessions'),
'pollSessions must reference /api/federation/sessions endpoint',
);
});
test('pollSessions source includes multi_device_enabled check', () => {
assert.ok(
app.pollSessions.toString().includes('multi_device_enabled'),
'pollSessions must check multi_device_enabled flag',
);
});
test('pollSessions uses /api/federation/sessions when multi_device_enabled is true', async () => {
const fetchedUrls = [];
const mockEl = { textContent: '', className: '' };
const origGetById = globalThis.document.getElementById;
globalThis.document.getElementById = (id) => (id === 'connection-status' ? mockEl : null);
globalThis.fetch = async (url) => {
fetchedUrls.push(url);
return { ok: true, json: async () => [] };
};
app._setServerSettings({ multi_device_enabled: true });
await app.pollSessions();
app._setServerSettings(null);
assert.ok(
fetchedUrls.some((u) => u === '/api/federation/sessions'),
'should fetch /api/federation/sessions when multi_device_enabled is true',
);
globalThis.document.getElementById = origGetById;
globalThis.fetch = undefined;
});
test('pollSessions uses /api/sessions when multi_device_enabled is false', async () => {
const fetchedUrls = [];
const mockEl = { textContent: '', className: '' };
const origGetById = globalThis.document.getElementById;
globalThis.document.getElementById = (id) => (id === 'connection-status' ? mockEl : null);
globalThis.fetch = async (url) => {
fetchedUrls.push(url);
return { ok: true, json: async () => [] };
};
app._setServerSettings({ multi_device_enabled: false });
await app.pollSessions();
app._setServerSettings(null);
assert.ok(
fetchedUrls.some((u) => u === '/api/sessions'),
'should fetch /api/sessions when multi_device_enabled is false',
);
assert.ok(
!fetchedUrls.some((u) => u === '/api/federation/sessions'),
'should NOT fetch /api/federation/sessions when multi_device_enabled is false',
);
globalThis.document.getElementById = origGetById;
globalThis.fetch = undefined;
});
// --- startPolling ---
test('startPolling guards against double-start (only creates one interval)', () => {