feat: update session management functions and improve partition handling
Desktop Client Build / Resolve Build Metadata (push) Successful in 31s
Frontend CI / Frontend (push) Successful in 2m58s
Backend CI / Backend (push) Successful in 18m2s
Desktop Client Build / Build Desktop Client (push) Successful in 24m49s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 38s

This commit is contained in:
2026-06-19 00:14:30 +08:00
parent 3aa8eb86f8
commit 0cddb47055
6 changed files with 80 additions and 19 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@geo/desktop-client", "name": "@geo/desktop-client",
"version": "0.1.1", "version": "0.1.2",
"private": true, "private": true,
"description": "省心推客户端 — 连接和管理您的媒体账号,轻松发布和监控内容表现。", "description": "省心推客户端 — 连接和管理您的媒体账号,轻松发布和监控内容表现。",
"author": { "author": {
@@ -9,8 +9,8 @@ const probeAIAccountSession = vi.fn()
const probePublishAccountSession = vi.fn() const probePublishAccountSession = vi.fn()
const silentRefreshAIAccountSession = vi.fn() const silentRefreshAIAccountSession = vi.fn()
const silentRefreshPublishAccountSession = vi.fn() const silentRefreshPublishAccountSession = vi.fn()
const getSessionHandle = vi.fn() const createSessionHandleForPartition = vi.fn()
const getPersistedPartition = vi.fn() const resolveStoredSessionPartition = vi.fn()
const emitRuntimeInvalidated = vi.fn() const emitRuntimeInvalidated = vi.fn()
vi.mock('electron/main', () => ({ vi.mock('electron/main', () => ({
@@ -27,8 +27,8 @@ vi.mock('./account-binder', () => ({
})) }))
vi.mock('./session-registry', () => ({ vi.mock('./session-registry', () => ({
getSessionHandle, createSessionHandleForPartition,
getPersistedPartition, resolveStoredSessionPartition,
})) }))
vi.mock('./runtime-events', () => ({ vi.mock('./runtime-events', () => ({
@@ -68,8 +68,8 @@ describe('account health challenge recheck', () => {
beforeEach(() => { beforeEach(() => {
vi.resetModules() vi.resetModules()
vi.clearAllMocks() vi.clearAllMocks()
getSessionHandle.mockReturnValue({ partition: 'persist:test' }) resolveStoredSessionPartition.mockReturnValue('persist:test')
getPersistedPartition.mockReturnValue('persist:test') createSessionHandleForPartition.mockReturnValue({ partition: 'persist:test' })
silentRefreshAIAccountSession.mockResolvedValue(true) silentRefreshAIAccountSession.mockResolvedValue(true)
silentRefreshPublishAccountSession.mockResolvedValue(true) silentRefreshPublishAccountSession.mockResolvedValue(true)
}) })
@@ -105,4 +105,23 @@ describe('account health challenge recheck', () => {
expect(snapshot.authState).toBe('challenge_required') expect(snapshot.authState).toBe('challenge_required')
expect(snapshot.authReason).toBe('risk_control') 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')
})
}) })
@@ -19,7 +19,7 @@ import {
type PlatformFailureInput, type PlatformFailureInput,
} from './platform-auth-adapters' } from './platform-auth-adapters'
import { emitRuntimeInvalidated } from './runtime-events' import { emitRuntimeInvalidated } from './runtime-events'
import { getPersistedPartition, getSessionHandle } from './session-registry' import { createSessionHandleForPartition, resolveStoredSessionPartition } from './session-registry'
const PROBE_TICK_MS = 1_000 const PROBE_TICK_MS = 1_000
const STALE_AFTER_MS = 45 * 60_000 const STALE_AFTER_MS = 45 * 60_000
@@ -117,15 +117,16 @@ function persistedHealthPath(): string {
} }
function resolvePartition(accountId: string): string { function resolvePartition(accountId: string): string {
return ( return resolveStoredSessionPartition(accountId) ?? `persist:acc-${accountId}`
getSessionHandle(accountId)?.partition ??
getPersistedPartition(accountId) ??
`persist:acc-${accountId}`
)
} }
function resolveKnownPartition(accountId: string): string | null { 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 { function nextInitialProbeAt(now = Date.now()): number {
@@ -103,8 +103,7 @@ import {
import { import {
clearSessionHandle, clearSessionHandle,
createSessionHandle, createSessionHandle,
getPersistedPartition, hasLocalSessionPartition,
getSessionHandle,
} from './session-registry' } from './session-registry'
import { import {
cancelDesktopTask, cancelDesktopTask,
@@ -345,7 +344,7 @@ function isDefinitiveAuthState(authState: string): boolean {
} }
function hasLocalAccountSession(accountId: string): boolean { function hasLocalAccountSession(accountId: string): boolean {
return Boolean(getSessionHandle(accountId) || getPersistedPartition(accountId)) return hasLocalSessionPartition(accountId)
} }
function isAccountOwnedByCurrentClient(account: DesktopAccountInfo): boolean { function isAccountOwnedByCurrentClient(account: DesktopAccountInfo): boolean {
@@ -1,5 +1,5 @@
import { randomUUID } from 'node:crypto' 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 { dirname, join } from 'node:path'
import type { Session } from 'electron/main' import type { Session } from 'electron/main'
@@ -32,6 +32,10 @@ function persistedPartitionsPath(): string {
return join(app.getPath('userData'), 'desktop-session-partitions.json') return join(app.getPath('userData'), 'desktop-session-partitions.json')
} }
function partitionsRootPath(): string {
return join(app.getPath('userData'), 'Partitions')
}
function readPersistedPartitions(): Record<string, string> { function readPersistedPartitions(): Record<string, string> {
if (persistedPartitionsCache) { if (persistedPartitionsCache) {
return persistedPartitionsCache return persistedPartitionsCache
@@ -111,6 +115,20 @@ function partitionFor(accountId: string): string {
return `persist:acc-${accountId}` 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 { function pendingPartitionFor(seed: string): string {
return `persist:pending-${seed}-${randomUUID()}` return `persist:pending-${seed}-${randomUUID()}`
} }
@@ -159,6 +177,29 @@ export function createSessionHandle(accountId?: string): SessionHandle {
return handle 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( export function createSessionHandleForPartition(
accountId: string, accountId: string,
partition: string, partition: string,
@@ -385,7 +385,8 @@ async function cleanDesktopStorageOnce(): Promise<DesktopStorageCleanupResult> {
for (const [partition, accountId] of persistedPartitions) { for (const [partition, accountId] of persistedPartitions) {
const root = partitionPath(partition) 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 continue
} }
forgetPersistedSessionPartition(accountId, partition) forgetPersistedSessionPartition(accountId, partition)