feat(desktop): add client release updater
Desktop Client Build / Resolve Build Metadata (push) Successful in 19s
Frontend CI / Frontend (push) Successful in 3m20s
Backend CI / Backend (push) Successful in 16m48s
Desktop Client Build / Build Desktop Client (push) Successful in 24m46s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 37s
Desktop Client Build / Resolve Build Metadata (push) Successful in 19s
Frontend CI / Frontend (push) Successful in 3m20s
Backend CI / Backend (push) Successful in 16m48s
Desktop Client Build / Build Desktop Client (push) Successful in 24m46s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 37s
This commit is contained in:
@@ -13,7 +13,11 @@ import type {
|
||||
AccountProbeState,
|
||||
AuthProbeResult,
|
||||
} from './auth-types'
|
||||
import { getPlatformAdapter, type PlatformFailureInput } from './platform-auth-adapters'
|
||||
import {
|
||||
getPlatformAdapter,
|
||||
type PlatformAdapter,
|
||||
type PlatformFailureInput,
|
||||
} from './platform-auth-adapters'
|
||||
import { emitRuntimeInvalidated } from './runtime-events'
|
||||
import { getPersistedPartition, getSessionHandle } from './session-registry'
|
||||
|
||||
@@ -30,6 +34,7 @@ const MAX_CONCURRENT_ACCOUNT_PROBES = 2
|
||||
const MANUAL_PROBE_CACHE_TTL_MS = 5 * 60_000
|
||||
const MANUAL_PROBE_MIN_INTERVAL_MS = 15_000
|
||||
const ACCOUNT_PROBE_RETRY_BACKOFF_MS = [1_000, 3_000, 10_000] as const
|
||||
const CHALLENGE_RECHECK_ATTEMPTS = 1
|
||||
|
||||
type ProbeTrigger = 'scheduled' | 'manual' | 'preflight' | 'confirm'
|
||||
|
||||
@@ -366,6 +371,62 @@ function applyNetworkResult(
|
||||
return commitRecord(record, previousSignature)
|
||||
}
|
||||
|
||||
function networkProbeResult(): AuthProbeResult {
|
||||
return {
|
||||
verdict: 'network_error',
|
||||
reason: 'network_error',
|
||||
profile: null,
|
||||
sessionFingerprint: null,
|
||||
}
|
||||
}
|
||||
|
||||
async function probeAdapter(
|
||||
adapter: PlatformAdapter,
|
||||
account: PublishAccountIdentity,
|
||||
partition: string,
|
||||
): Promise<AuthProbeResult> {
|
||||
return await adapter.probe(account, partition).catch<AuthProbeResult>(() => networkProbeResult())
|
||||
}
|
||||
|
||||
async function recheckChallengeResult(
|
||||
adapter: PlatformAdapter,
|
||||
account: PublishAccountIdentity,
|
||||
partition: string,
|
||||
result: AuthProbeResult,
|
||||
): Promise<AuthProbeResult> {
|
||||
if (result.verdict !== 'challenge_required') {
|
||||
return result
|
||||
}
|
||||
|
||||
let confirmed = result
|
||||
for (let attempt = 0; attempt < CHALLENGE_RECHECK_ATTEMPTS; attempt += 1) {
|
||||
confirmed = await probeAdapter(adapter, account, partition)
|
||||
if (confirmed.verdict !== 'challenge_required') {
|
||||
return confirmed
|
||||
}
|
||||
}
|
||||
|
||||
return confirmed
|
||||
}
|
||||
|
||||
function applyProbeResult(
|
||||
record: AccountHealthRecord,
|
||||
result: AuthProbeResult,
|
||||
now: number,
|
||||
trigger: ProbeTrigger,
|
||||
): AccountHealthSnapshot {
|
||||
switch (result.verdict) {
|
||||
case 'active':
|
||||
return applyActiveResult(record, result, now)
|
||||
case 'expired':
|
||||
return applyExpiredResult(record, result, now)
|
||||
case 'challenge_required':
|
||||
return applyChallengeResult(record, result, now)
|
||||
default:
|
||||
return applyNetworkResult(record, result, now, trigger)
|
||||
}
|
||||
}
|
||||
|
||||
async function performProbeLocked(
|
||||
account: PublishAccountIdentity,
|
||||
options: {
|
||||
@@ -407,28 +468,18 @@ async function performProbeLocked(
|
||||
}
|
||||
}
|
||||
|
||||
const result = await adapter.probe(account, partition).catch<AuthProbeResult>(() => ({
|
||||
verdict: 'network_error',
|
||||
reason: 'network_error',
|
||||
profile: null,
|
||||
sessionFingerprint: null,
|
||||
}))
|
||||
const result = await probeAdapter(adapter, account, partition)
|
||||
|
||||
if (record.authRevision !== probeAuthRevision) {
|
||||
return toPublicSnapshot(record)
|
||||
}
|
||||
|
||||
const now = Date.now()
|
||||
switch (result.verdict) {
|
||||
case 'active':
|
||||
return applyActiveResult(record, result, now)
|
||||
case 'expired':
|
||||
return applyExpiredResult(record, result, now)
|
||||
case 'challenge_required':
|
||||
return applyChallengeResult(record, result, now)
|
||||
default:
|
||||
return applyNetworkResult(record, result, now, options.trigger)
|
||||
const confirmedResult = await recheckChallengeResult(adapter, account, partition, result)
|
||||
if (record.authRevision !== probeAuthRevision) {
|
||||
return toPublicSnapshot(record)
|
||||
}
|
||||
|
||||
return applyProbeResult(record, confirmedResult, Date.now(), options.trigger)
|
||||
}
|
||||
|
||||
function canUseCachedProbe(
|
||||
@@ -922,15 +973,25 @@ export async function reportAccountFailure(
|
||||
const now = Date.now()
|
||||
|
||||
if (classification === 'challenge' || classification === 'risk_control') {
|
||||
record.authState = 'challenge_required'
|
||||
record.probeState = 'idle'
|
||||
record.authReason = classification === 'risk_control' ? 'risk_control' : 'captcha_gate'
|
||||
record.lastAuthFailureAt = now
|
||||
record.lastProbeAt = now
|
||||
record.nextProbeAt = nextRegularProbeAt(now)
|
||||
record.suspectedExpiredAt = null
|
||||
record.confirmFailureCount = 0
|
||||
return commitRecord(record, previousSignature)
|
||||
record.probeState = 'probing'
|
||||
commitRecord(record, previousSignature)
|
||||
|
||||
const partition = resolveKnownPartition(account.id)
|
||||
const result = partition
|
||||
? await recheckChallengeResult(adapter, account, partition, {
|
||||
verdict: 'challenge_required',
|
||||
reason: classification === 'risk_control' ? 'risk_control' : 'captcha_gate',
|
||||
profile: null,
|
||||
sessionFingerprint: null,
|
||||
})
|
||||
: {
|
||||
verdict: 'expired' as const,
|
||||
reason: 'missing_partition' as const,
|
||||
profile: null,
|
||||
sessionFingerprint: null,
|
||||
}
|
||||
|
||||
return applyProbeResult(record, result, Date.now(), 'confirm')
|
||||
}
|
||||
|
||||
if (classification === 'network') {
|
||||
|
||||
Reference in New Issue
Block a user