Files
WebDrop/web/src/core-bridge/worker/shareSeal.ts
T

49 lines
2.4 KiB
TypeScript

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<Share> {
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;
}