import { saveResource } from '../database'; import type { Item, Share } from '../types'; import { getSession, sessions } from './sessions'; import { apiFetch } from './http'; import { toShareInfo, type ShareWire } from './wire'; import { flushOutbox, persistPlan } from './transfer'; import { publishShare } from './presentation'; import { encodeManifest } from './shareManifest'; import { readShareInfo, releaseOwner } from './shareState'; export async function sealShare(id: string): Promise { const { session, resource } = await getSession(id); if (resource.kind !== 'share' || resource.state !== 'draft' || !session.can('owner')) throw new Error('此分享已封存'); if (!(await flushOutbox(resource, session))) throw new Error('网络连接失败,请稍后重试'); const before = await readShareInfo(session, resource); if (before.state === 'sealed') return toShareInfo(before, resource); if ((resource.draftItems ?? []).some((item) => item.state.kind !== 'complete')) throw new Error('还有内容没有上传完成'); if (!resource.pendingManifestId) { const bytes = encodeManifest(resource.manifestEntries ?? []); const plan = session.encryptBytes(bytes, { kind: 'manifest' }, { gated: false }); await persistPlan(resource, plan); resource.pendingManifestId = plan.itemId; await saveResource(resource); } if (!(await flushOutbox(resource, session))) throw new Error('清单上传暂停,请重试'); let raw: ShareWire; try { const response = await apiFetch(`/shares/${id}/finish`, { method: 'POST', headers: { ...session.authHeaders('owner'), 'Content-Type': 'application/json' }, body: JSON.stringify({ manifest_item_id: resource.pendingManifestId }), }); raw = await response.json() as ShareWire; } catch (error) { raw = await readShareInfo(session, resource).catch(() => { throw error; }); if (raw.state !== 'sealed') throw error; } const items = resource.draftItems ?? []; if (sessions.get(id)?.can('owner')) await releaseOwner(session, resource, raw); const sealed = toShareInfo(raw, resource); publishShare(id, { t: 'share', share: sealed }); const remaining = new Map(raw.items.map((item) => [item.item_id, item.downloads_left])); for (const draft of items) { const item: Item = draft.kind === 'file' ? { ...draft, remaining: remaining.get(draft.id) ?? null } : draft; publishShare(id, { t: 'item', item }); } publishShare(id, { t: 'ready' }); return sealed; }