import { ChannelSession } from '@webdrop/core'; import { getResource, importResources, listResourceDeletes, type SavedResource } from '../database'; import { openChannelGrant } from './channelGrant'; import { makeLink } from './links'; import { apiFetch, readJson } from './http'; import type { InvitationKeyPair } from './accountCrypto'; import type { ChannelWire } from './wire'; import type { AccountChannelBody } from '../generated/api'; import { channelCache } from './resourceMetadata'; export async function recoverAccountChannels(username: string, keys: InvitationKeyPair, signal: AbortSignal): Promise { const grants = await apiFetch('/accounts/channels', { signal }).then((r) => r.json() as Promise); const deletes = new Map((await listResourceDeletes()).map((row) => [row.id, row.modifiedAt])); for (const grant of grants) { if (grant.role !== 'owner' && grant.role !== 'member') throw new Error('空间成员权限无效'); if (signal.aborted) throw new DOMException('账号已切换', 'AbortError'); if ((deletes.get(grant.channel_id) ?? -1) >= grant.created_at || await getResource(grant.channel_id)) continue; const parsed = await openChannelGrant(grant.payload, username, grant.channel_id, keys.privateKey, grant.role, self.location.origin); const session = await ChannelSession.open(makeLink(self.location.origin, parsed.id, parsed.keys)); try { const info = await readJson(`/channels/${session.id}`, session.authHeaders('read')); session.verifyChannelInfo(info); if (grant.role === 'owner' && info.creator_account !== username) throw new Error('空间所属账号不匹配'); const resource: SavedResource = { ...channelCache(info), id: session.id, kind: 'channel', mode: session.mode, role: grant.role, creatorAccount: info.creator_account ?? undefined, name: info.settings.name ?? undefined, link: session.link(self.location.origin), ...(grant.role === 'owner' ? { ownerLink: session.link(self.location.origin, 'owner') } : {}), endpointSecret: new Uint8Array(32), createdAt: info.created_at, modifiedAt: grant.created_at, expiresAt: info.expires_at, maxMembers: info.settings.max_members, maxDownloads: info.settings.file_max_downloads }; if (signal.aborted) throw new DOMException('账号已切换', 'AbortError'); await importResources([resource]); } finally { session.dispose(); } } }