feat: support media account unbind requests

This commit is contained in:
2026-06-18 23:49:01 +08:00
parent fd37263116
commit 889a575188
7 changed files with 708 additions and 27 deletions
@@ -150,6 +150,7 @@ type MonitorExecutionPhase =
const heartbeatIntervalMs = 15_000
const pullIntervalMs = 60_000
const publishPullIntervalMs = 30_000
const accountSyncIntervalMs = 30_000
const pumpWatchdogIntervalMs = 30_000
const leaseExtendIntervalMs = 60_000
const defaultPublishTaskTimeoutMs = 5 * 60_000
@@ -258,11 +259,13 @@ interface RuntimeState {
publishFallbackBackoffUntil: number
heartbeatTimer: ReturnType<typeof setInterval> | null
pullTimer: ReturnType<typeof setInterval> | null
accountSyncTimer: ReturnType<typeof setInterval> | null
pumpWatchdogTimer: ReturnType<typeof setInterval> | null
accountHealthReportTimer: ReturnType<typeof setTimeout> | null
accountHealthReportQueue: Map<string, DesktopAccountHealthReport>
accountHealthReportBadAccounts: Set<string>
accountRemoteHealth: Map<string, DesktopAccountInfo['health']>
accountSyncRunning: boolean
dispatchWsClient: DispatchWsClient | null
dispatchWsConnected: boolean
lastHeartbeatAt: number
@@ -289,11 +292,13 @@ const state: RuntimeState = {
publishFallbackBackoffUntil: 0,
heartbeatTimer: null,
pullTimer: null,
accountSyncTimer: null,
pumpWatchdogTimer: null,
accountHealthReportTimer: null,
accountHealthReportQueue: new Map<string, DesktopAccountHealthReport>(),
accountHealthReportBadAccounts: new Set<string>(),
accountRemoteHealth: new Map<string, DesktopAccountInfo['health']>(),
accountSyncRunning: false,
dispatchWsClient: null,
dispatchWsConnected: false,
lastHeartbeatAt: 0,
@@ -712,6 +717,7 @@ function startRuntime(): void {
connectDispatchWs()
scheduleHeartbeatLoop()
schedulePullLoop()
scheduleAccountSyncLoop()
schedulePumpWatchdogLoop()
void sendHeartbeat('startup')
@@ -753,6 +759,10 @@ function stopRuntime(): void {
clearInterval(state.pullTimer)
state.pullTimer = null
}
if (state.accountSyncTimer) {
clearInterval(state.accountSyncTimer)
state.accountSyncTimer = null
}
if (state.pumpWatchdogTimer) {
clearInterval(state.pumpWatchdogTimer)
state.pumpWatchdogTimer = null
@@ -824,6 +834,16 @@ function schedulePullLoop(): void {
}, publishPullIntervalMs)
}
function scheduleAccountSyncLoop(): void {
if (state.accountSyncTimer) {
clearInterval(state.accountSyncTimer)
}
state.accountSyncTimer = setInterval(() => {
void syncAccounts('timer')
}, accountSyncIntervalMs)
}
function schedulePumpWatchdogLoop(): void {
if (state.pumpWatchdogTimer) {
clearInterval(state.pumpWatchdogTimer)
@@ -1180,13 +1200,17 @@ async function sendHeartbeat(source: 'startup' | 'timer'): Promise<void> {
}
}
async function syncAccounts(source: 'startup' | 'manual'): Promise<void> {
async function syncAccounts(source: 'startup' | 'manual' | 'timer'): Promise<void> {
if (!canRunLive(state.session)) {
return
}
if (state.accountSyncRunning) {
return
}
state.accountSyncRunning = true
try {
const accounts = await listDesktopAccounts()
const accounts = await consumeDeleteRequestedAccounts(await listDesktopAccounts())
state.lastAccountsSyncAt = Date.now()
state.lastError = null
@@ -1298,9 +1322,47 @@ async function syncAccounts(source: 'startup' | 'manual'): Promise<void> {
setSchedulerError(message)
recordActivity('warn', '账号同步失败', message)
} finally {
state.accountSyncRunning = false
}
}
async function consumeDeleteRequestedAccounts(
accounts: DesktopAccountInfo[],
): Promise<DesktopAccountInfo[]> {
const remainingAccounts: DesktopAccountInfo[] = []
for (const account of accounts) {
if (!account.delete_requested_at) {
remainingAccounts.push(account)
continue
}
try {
await deleteDesktopAccount(account.id, account.sync_version)
await clearSessionHandle(account.id)
forgetTrackedAccountHealth(account.id)
state.accountProfiles.delete(account.id)
recordActivity(
'info',
'账号已自动解绑',
`${account.display_name} 已按服务端解绑申请移除,并清理了当前机器上的本地会话缓存。`,
)
} catch (error) {
remainingAccounts.push(account)
console.warn('[desktop-runtime] requested desktop account cleanup failed', {
accountId: account.id,
platform: account.platform,
platformUid: account.platform_uid,
message: errorMessage(error),
})
recordActivity('warn', '账号自动解绑失败', `${account.display_name}${errorMessage(error)}`)
}
}
return remainingAccounts
}
async function cleanupDuplicateDesktopAccounts(accounts: DesktopAccountInfo[]): Promise<void> {
for (const account of accounts) {
try {