From 0cddb470553d1aa2c73e301b00c087c08fd8b5b5 Mon Sep 17 00:00:00 2001 From: liangxu Date: Fri, 19 Jun 2026 00:14:30 +0800 Subject: [PATCH] feat: update session management functions and improve partition handling --- apps/desktop-client/package.json | 2 +- .../src/main/account-health.test.ts | 31 ++++++++++--- .../desktop-client/src/main/account-health.ts | 15 ++++--- .../src/main/runtime-controller.ts | 5 +-- .../src/main/session-registry.ts | 43 ++++++++++++++++++- .../src/main/storage-cleaner.ts | 3 +- 6 files changed, 80 insertions(+), 19 deletions(-) diff --git a/apps/desktop-client/package.json b/apps/desktop-client/package.json index 4b192c2..f13d0ea 100644 --- a/apps/desktop-client/package.json +++ b/apps/desktop-client/package.json @@ -1,6 +1,6 @@ { "name": "@geo/desktop-client", - "version": "0.1.1", + "version": "0.1.2", "private": true, "description": "省心推客户端 — 连接和管理您的媒体账号,轻松发布和监控内容表现。", "author": { diff --git a/apps/desktop-client/src/main/account-health.test.ts b/apps/desktop-client/src/main/account-health.test.ts index cc040df..5951d2e 100644 --- a/apps/desktop-client/src/main/account-health.test.ts +++ b/apps/desktop-client/src/main/account-health.test.ts @@ -9,8 +9,8 @@ const probeAIAccountSession = vi.fn() const probePublishAccountSession = vi.fn() const silentRefreshAIAccountSession = vi.fn() const silentRefreshPublishAccountSession = vi.fn() -const getSessionHandle = vi.fn() -const getPersistedPartition = vi.fn() +const createSessionHandleForPartition = vi.fn() +const resolveStoredSessionPartition = vi.fn() const emitRuntimeInvalidated = vi.fn() vi.mock('electron/main', () => ({ @@ -27,8 +27,8 @@ vi.mock('./account-binder', () => ({ })) vi.mock('./session-registry', () => ({ - getSessionHandle, - getPersistedPartition, + createSessionHandleForPartition, + resolveStoredSessionPartition, })) vi.mock('./runtime-events', () => ({ @@ -68,8 +68,8 @@ describe('account health challenge recheck', () => { beforeEach(() => { vi.resetModules() vi.clearAllMocks() - getSessionHandle.mockReturnValue({ partition: 'persist:test' }) - getPersistedPartition.mockReturnValue('persist:test') + resolveStoredSessionPartition.mockReturnValue('persist:test') + createSessionHandleForPartition.mockReturnValue({ partition: 'persist:test' }) silentRefreshAIAccountSession.mockResolvedValue(true) silentRefreshPublishAccountSession.mockResolvedValue(true) }) @@ -105,4 +105,23 @@ describe('account health challenge recheck', () => { expect(snapshot.authState).toBe('challenge_required') expect(snapshot.authReason).toBe('risk_control') }) + + it('probes restored default partitions instead of marking restarted accounts expired', async () => { + resolveStoredSessionPartition.mockReturnValue('persist:acc-account-1') + probeAIAccountSession.mockResolvedValueOnce(active()) + const { probeTrackedAccount } = await import('./account-health') + + const snapshot = await probeTrackedAccount(account, { + trigger: 'manual', + allowSilentRefresh: true, + force: true, + }) + + expect(createSessionHandleForPartition).toHaveBeenCalledWith( + 'account-1', + 'persist:acc-account-1', + ) + expect(probeAIAccountSession).toHaveBeenCalledWith(account, 'persist:acc-account-1') + expect(snapshot.authState).toBe('active') + }) }) diff --git a/apps/desktop-client/src/main/account-health.ts b/apps/desktop-client/src/main/account-health.ts index c32673f..08116e6 100644 --- a/apps/desktop-client/src/main/account-health.ts +++ b/apps/desktop-client/src/main/account-health.ts @@ -19,7 +19,7 @@ import { type PlatformFailureInput, } from './platform-auth-adapters' import { emitRuntimeInvalidated } from './runtime-events' -import { getPersistedPartition, getSessionHandle } from './session-registry' +import { createSessionHandleForPartition, resolveStoredSessionPartition } from './session-registry' const PROBE_TICK_MS = 1_000 const STALE_AFTER_MS = 45 * 60_000 @@ -117,15 +117,16 @@ function persistedHealthPath(): string { } function resolvePartition(accountId: string): string { - return ( - getSessionHandle(accountId)?.partition ?? - getPersistedPartition(accountId) ?? - `persist:acc-${accountId}` - ) + return resolveStoredSessionPartition(accountId) ?? `persist:acc-${accountId}` } function resolveKnownPartition(accountId: string): string | null { - return getSessionHandle(accountId)?.partition ?? getPersistedPartition(accountId) ?? null + const partition = resolveStoredSessionPartition(accountId) + if (!partition) { + return null + } + createSessionHandleForPartition(accountId, partition) + return partition } function nextInitialProbeAt(now = Date.now()): number { diff --git a/apps/desktop-client/src/main/runtime-controller.ts b/apps/desktop-client/src/main/runtime-controller.ts index d9c37a1..d53ea50 100644 --- a/apps/desktop-client/src/main/runtime-controller.ts +++ b/apps/desktop-client/src/main/runtime-controller.ts @@ -103,8 +103,7 @@ import { import { clearSessionHandle, createSessionHandle, - getPersistedPartition, - getSessionHandle, + hasLocalSessionPartition, } from './session-registry' import { cancelDesktopTask, @@ -345,7 +344,7 @@ function isDefinitiveAuthState(authState: string): boolean { } function hasLocalAccountSession(accountId: string): boolean { - return Boolean(getSessionHandle(accountId) || getPersistedPartition(accountId)) + return hasLocalSessionPartition(accountId) } function isAccountOwnedByCurrentClient(account: DesktopAccountInfo): boolean { diff --git a/apps/desktop-client/src/main/session-registry.ts b/apps/desktop-client/src/main/session-registry.ts index 464a2f1..6ebeefc 100644 --- a/apps/desktop-client/src/main/session-registry.ts +++ b/apps/desktop-client/src/main/session-registry.ts @@ -1,5 +1,5 @@ import { randomUUID } from 'node:crypto' -import { mkdirSync, readFileSync, writeFileSync } from 'node:fs' +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' import { dirname, join } from 'node:path' import type { Session } from 'electron/main' @@ -32,6 +32,10 @@ function persistedPartitionsPath(): string { return join(app.getPath('userData'), 'desktop-session-partitions.json') } +function partitionsRootPath(): string { + return join(app.getPath('userData'), 'Partitions') +} + function readPersistedPartitions(): Record { if (persistedPartitionsCache) { return persistedPartitionsCache @@ -111,6 +115,20 @@ function partitionFor(accountId: string): string { return `persist:acc-${accountId}` } +function partitionDirectoryName(partition: string): string | null { + if (!partition.startsWith('persist:')) { + return null + } + + const name = partition.slice('persist:'.length) + return name ? name : null +} + +function hasPersistedPartitionDirectory(partition: string): boolean { + const name = partitionDirectoryName(partition) + return Boolean(name && existsSync(join(partitionsRootPath(), name))) +} + function pendingPartitionFor(seed: string): string { return `persist:pending-${seed}-${randomUUID()}` } @@ -159,6 +177,29 @@ export function createSessionHandle(accountId?: string): SessionHandle { return handle } +export function hasLocalSessionPartition(accountId: string): boolean { + return Boolean( + registry.has(accountId) || + persistedPartitionFor(accountId) || + hasPersistedPartitionDirectory(partitionFor(accountId)), + ) +} + +export function resolveStoredSessionPartition(accountId: string): string | null { + const active = registry.get(accountId) + if (active) { + return active.partition + } + + const persisted = persistedPartitionFor(accountId) + if (persisted) { + return persisted + } + + const defaultPartition = partitionFor(accountId) + return hasPersistedPartitionDirectory(defaultPartition) ? defaultPartition : null +} + export function createSessionHandleForPartition( accountId: string, partition: string, diff --git a/apps/desktop-client/src/main/storage-cleaner.ts b/apps/desktop-client/src/main/storage-cleaner.ts index 4d0fc7d..99109c0 100644 --- a/apps/desktop-client/src/main/storage-cleaner.ts +++ b/apps/desktop-client/src/main/storage-cleaner.ts @@ -385,7 +385,8 @@ async function cleanDesktopStorageOnce(): Promise { for (const [partition, accountId] of persistedPartitions) { const root = partitionPath(partition) - if (root && before.entries.some((entry) => entry.partition === partition)) { + const footprint = root ? await directoryFootprint(root) : { bytes: 0, lastModifiedAt: null } + if (footprint.bytes > 0 || footprint.lastModifiedAt) { continue } forgetPersistedSessionPartition(accountId, partition)