diff --git a/apps/desktop/electron/main.ts b/apps/desktop/electron/main.ts index 5f6ba957ff8c..d4cb1ccb5f5d 100644 --- a/apps/desktop/electron/main.ts +++ b/apps/desktop/electron/main.ts @@ -142,6 +142,7 @@ import { createKeepAwake } from './power-save' import { FirstRunSetupResetError, runPrimaryBackendStartup } from './primary-backend-startup' import { rehomePrimaryConnection } from './primary-connection-rehome' import { decideProfileDeleteAction, profileNameFromDeleteRequest, resolveRouteProfile } from './profile-delete-routing' +import { fetchPrimaryProfileSessions } from './profile-session-routing' import { createQuickEntryShortcut, quickEntryWindowBounds, sanitizeQuickEntrySettings } from './quick-entry' import * as remoteLifecycle from './remote-lifecycle' import { RemoteLivenessTracker, RemoteRevalidationCoordinator, revalidateRemoteConnection } from './remote-liveness' @@ -9775,12 +9776,7 @@ async function fetchProfilesSessionSlice(searchParams, remoteProfiles) { return remoteSessionList(requested, searchParams) } - const primary = await ensureBackend(null) - - return fetchJson(`${primary.baseUrl}/api/profiles/sessions?${searchParams}`, primary.token, { - method: 'GET', - timeoutMs: DEFAULT_FETCH_TIMEOUT_MS - }).catch(() => ({ sessions: [], total: 0, profile_totals: {} })) + return fetchPrimaryProfileSessions(searchParams, fetchJsonForProfile) } return mergeRemoteProfileSessions(searchParams, remoteProfiles) @@ -9795,12 +9791,7 @@ async function mergeRemoteProfileSessions(searchParams, remoteProfiles) { const offset = Math.max(0, Number(searchParams.get('offset')) || 0) const order = searchParams.get('order') === 'created' ? 'started_at' : 'last_active' - const primary = await ensureBackend(null) - - const base = (await fetchJson(`${primary.baseUrl}/api/profiles/sessions?${searchParams}`, primary.token, { - method: 'GET', - timeoutMs: DEFAULT_FETCH_TIMEOUT_MS - }).catch(() => ({ sessions: [], total: 0, profile_totals: {} }))) as any + const base = (await fetchPrimaryProfileSessions(searchParams, fetchJsonForProfile)) as any // Over-fetch each remote from offset 0 (limit+offset rows) so the merged window // is correct for this page — mirrors the primary's per-profile over-fetch. diff --git a/apps/desktop/electron/profile-session-routing.test.ts b/apps/desktop/electron/profile-session-routing.test.ts new file mode 100644 index 000000000000..199740519bd6 --- /dev/null +++ b/apps/desktop/electron/profile-session-routing.test.ts @@ -0,0 +1,30 @@ +import assert from 'node:assert/strict' + +import { test } from 'vitest' + +import { fetchPrimaryProfileSessions } from './profile-session-routing' + +test('primary session reads use the profile-aware request path', async () => { + const calls: Array<{ profile: string | null; path: string }> = [] + const expected = { sessions: [{ id: 'session-1' }], total: 1, profile_totals: { default: 1 } } + + const result = await fetchPrimaryProfileSessions( + new URLSearchParams({ profile: 'default', limit: '20' }), + async (profile, path) => { + calls.push({ profile, path }) + + return expected + } + ) + + assert.deepEqual(calls, [{ profile: null, path: '/api/profiles/sessions?profile=default&limit=20' }]) + assert.equal(result, expected) +}) + +test('primary session reads preserve the empty-list fallback', async () => { + const result = await fetchPrimaryProfileSessions(new URLSearchParams({ profile: 'all' }), async () => { + throw new Error('remote unavailable') + }) + + assert.deepEqual(result, { sessions: [], total: 0, profile_totals: {} }) +}) diff --git a/apps/desktop/electron/profile-session-routing.ts b/apps/desktop/electron/profile-session-routing.ts new file mode 100644 index 000000000000..f31e22ca52a3 --- /dev/null +++ b/apps/desktop/electron/profile-session-routing.ts @@ -0,0 +1,19 @@ +export interface ProfileSessionsResponse { + sessions: unknown[] + total: number + profile_totals: Record + [key: string]: unknown +} + +type FetchJsonForProfile = (profile: string | null, path: string) => Promise + +export async function fetchPrimaryProfileSessions( + searchParams: URLSearchParams, + fetchJsonForProfile: FetchJsonForProfile +): Promise { + try { + return (await fetchJsonForProfile(null, `/api/profiles/sessions?${searchParams}`)) as ProfileSessionsResponse + } catch { + return { sessions: [], total: 0, profile_totals: {} } + } +}