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
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:
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user