fix(desktop): scope account access to owning client and extend identity verification
Backend CI / Backend (push) Successful in 14m18s

Restrict account tracking, probing, and health reporting to accounts owned
by the current client (client_id match). Extend identity-match verification
to bilibili, juejin, and smzdm so session validity is confirmed locally
rather than deferred to remote health state. Server-side account view now
uses stored client_id as authoritative owner instead of presence data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Xu Liang
2026-05-05 22:09:10 +08:00
parent 337bb6f9ac
commit 680adf7b93
7 changed files with 151 additions and 34 deletions
@@ -28,6 +28,7 @@ import {
forgetTrackedAccountHealth,
getAccountHealthSnapshot,
getProjectedAccountHealth,
markTrackedAccountMissingPartition,
markTrackedAccountBound,
probeTrackedAccount,
reconcileTrackedAccountRemoteState,
@@ -319,8 +320,24 @@ function hasLocalAccountSession(accountId: string): boolean {
return Boolean(getSessionHandle(accountId) || getPersistedPartition(accountId))
}
function isAccountOwnedByCurrentClient(account: DesktopAccountInfo): boolean {
return Boolean(account.client_id && account.client_id === state.client?.id)
}
function hasLocalAccountAuthorization(account: DesktopAccountInfo): boolean {
return isAccountOwnedByCurrentClient(account)
}
function accountRequiresLocalAuthorization(account: DesktopAccountInfo): boolean {
return Boolean(account.client_id)
}
function accountRequiresStrictProbe(account: DesktopAccountInfo): boolean {
return ['bilibili', 'juejin', 'smzdm'].includes(account.platform)
}
function shouldReportAccountPresence(account: DesktopAccountInfo): boolean {
return hasLocalAccountSession(account.id) || account.client_id === state.client?.id
return hasLocalAccountSession(account.id) && isAccountOwnedByCurrentClient(account)
}
function runtimePlatformLabel(platform: string): string {
@@ -955,7 +972,9 @@ async function syncAccounts(source: 'startup' | 'manual'): Promise<void> {
noteSchedulerAccountsSync()
setSchedulerError(null)
const identities = accounts.map((account) => accountIdentityFromDesktopAccount(account))
const identities = accounts
.filter((account) => hasLocalAccountAuthorization(account))
.map((account) => accountIdentityFromDesktopAccount(account))
syncTrackedAccounts(identities)
state.accountRemoteHealth = new Map(accounts.map((account) => [account.id, account.health]))
@@ -968,15 +987,19 @@ async function syncAccounts(source: 'startup' | 'manual'): Promise<void> {
...account,
platform_uid: normalizedPlatformUid,
})
reconcileTrackedAccountRemoteState(accountIdentity, {
health: account.health,
verifiedAt: account.verified_at,
profile: {
subjectUid: normalizedPlatformUid,
displayName: account.display_name,
avatarUrl: account.avatar_url,
},
})
if (hasLocalAccountAuthorization(account) && !accountRequiresStrictProbe(account)) {
reconcileTrackedAccountRemoteState(accountIdentity, {
health: account.health,
verifiedAt: account.verified_at,
profile: {
subjectUid: normalizedPlatformUid,
displayName: account.display_name,
avatarUrl: account.avatar_url,
},
})
} else if (accountRequiresLocalAuthorization(account)) {
markTrackedAccountMissingPartition(accountIdentity)
}
const projected = getProjectedAccountHealth({
accountId: account.id,
platform: account.platform,
@@ -1140,6 +1163,9 @@ function enqueueAccountHealthReport(accountId: string): void {
if (!account) {
return
}
if (!hasLocalAccountAuthorization(account)) {
return
}
const report = buildAccountHealthReport(account)
if (!shouldQueueAccountHealthReport(account, report)) {
@@ -1270,6 +1296,10 @@ export async function requestRuntimeAccountProbe(
if (!account) {
throw new Error('runtime_account_not_found')
}
if (existingAccount && !hasLocalAccountAuthorization(existingAccount)) {
markTrackedAccountMissingPartition(account)
return
}
try {
await probeTrackedAccount(account, {
@@ -1318,7 +1348,11 @@ export function noteRuntimeAccountBound(account: DesktopAccountInfo): void {
avatarUrl: normalizedAccount.avatar_url,
})
state.lastAccountsSyncAt = Date.now()
syncTrackedAccounts(state.accounts.map((item) => accountIdentityFromDesktopAccount(item)))
syncTrackedAccounts(
state.accounts
.filter((item) => hasLocalAccountAuthorization(item))
.map((item) => accountIdentityFromDesktopAccount(item)),
)
markTrackedAccountBound(accountIdentityFromDesktopAccount(normalizedAccount), {
subjectUid: normalizedAccount.platform_uid,
displayName: normalizedAccount.display_name,