feat(web): add account workspaces and signed content flows
This commit is contained in:
1 parent
7b8d61d1ba
commit
b5cf3d5958
117 files changed
+2789
-1674
No files matched your search
@@ -1,5 +1,8 @@
|
||||
import { ChannelSession } from '@webdrop/core';
|
||||
import { getResource, listResources, removeResource, saveResource, type SavedResource } from '../database';
|
||||
import { cacheResource, getResource, listResources, removeResource, saveResource, type SavedResource } from '../database';
|
||||
import { shareCache, isOfflineFailure } from './resourceMetadata';
|
||||
import type { ShareSettingsPatch } from '../generated/api';
|
||||
import { readShareInfo } from './shareState';
|
||||
import type { Share, ShareSettings } from '../types';
|
||||
|
||||
import { active, blobs, getSession, sessions } from './sessions';
|
||||
@@ -29,11 +32,12 @@ export async function createShare(settings: ShareSettings, captchaToken?: string
|
||||
});
|
||||
const raw = await response.json() as ShareWire;
|
||||
const resource: SavedResource = {
|
||||
...shareCache(raw),
|
||||
id: raw.id, kind: 'share', mode: 'enc', role: 'owner', mine: true,
|
||||
link: toShareLink(session.link(self.location.origin)),
|
||||
ownerLink: toShareLink(session.link(self.location.origin, 'owner')),
|
||||
endpointSecret: Uint8Array.from(session.endpointSecret),
|
||||
createdAt: raw.created_at, expiresAt: raw.expires_at, seats: 0, membersCanUpload: false,
|
||||
createdAt: raw.created_at, expiresAt: raw.expires_at, seats: 0,
|
||||
state: 'draft', maxVisitors: raw.settings.max_visitors,
|
||||
ttlSeconds: raw.settings.ttl_seconds,
|
||||
maxDownloads: raw.settings.file_max_downloads === 0 ? null : raw.settings.file_max_downloads,
|
||||
@@ -56,10 +60,11 @@ export async function joinShare(input: { link: { id: string; key: string } }): P
|
||||
const raw = await readJson<ShareWire>(`/shares/${input.link.id}`, session.authHeaders('read'));
|
||||
if (raw.state !== 'sealed') throw fail(404, 'not_found');
|
||||
const resource: SavedResource = {
|
||||
...shareCache(raw),
|
||||
id: input.link.id, kind: 'share', mode: 'enc', role: 'member', mine: false,
|
||||
link: toShareLink(makeLink(self.location.origin, input.link.id, { k: input.link.key })),
|
||||
endpointSecret: Uint8Array.from(session.endpointSecret), createdAt: raw.created_at, expiresAt: raw.expires_at,
|
||||
seats: 0, membersCanUpload: false, state: raw.state,
|
||||
seats: 0, state: raw.state,
|
||||
ttlSeconds: raw.settings.ttl_seconds,
|
||||
maxVisitors: raw.settings.max_visitors, maxDownloads: raw.settings.file_max_downloads === 0 ? null : raw.settings.file_max_downloads,
|
||||
};
|
||||
@@ -75,19 +80,15 @@ export async function joinShare(input: { link: { id: string; key: string } }): P
|
||||
export async function updateShareSettings(id: string, patch: Partial<ShareSettings>): Promise<Share> {
|
||||
const { session, resource } = await getSession(id);
|
||||
if (resource.kind !== 'share' || !session.can('owner')) throw new Error('此分享不再可修改');
|
||||
const body: Record<string, number> = {};
|
||||
const body: ShareSettingsPatch = {};
|
||||
if (patch.ttlSeconds !== undefined) body.ttl_seconds = patch.ttlSeconds;
|
||||
if (patch.maxVisitors !== undefined) body.max_visitors = patch.maxVisitors;
|
||||
if (patch.maxDownloads !== undefined) body.file_max_downloads = patch.maxDownloads ?? 0;
|
||||
const raw = await apiFetch(`/shares/${id}/settings`, {
|
||||
method: 'PATCH', headers: { ...session.authHeaders('owner'), 'Content-Type': 'application/json' }, body: JSON.stringify(body),
|
||||
}).then((response) => response.json() as Promise<ShareWire>);
|
||||
resource.expiresAt = raw.expires_at;
|
||||
resource.ttlSeconds = raw.settings.ttl_seconds;
|
||||
resource.maxVisitors = raw.settings.max_visitors;
|
||||
resource.maxDownloads = raw.settings.file_max_downloads === 0 ? null : raw.settings.file_max_downloads;
|
||||
await saveResource(resource);
|
||||
return toShareInfo(raw, resource);
|
||||
const updated = await cacheResource(resource, shareCache(raw));
|
||||
return toShareInfo(raw, updated);
|
||||
}
|
||||
|
||||
export async function removeShare(id: string): Promise<void> {
|
||||
@@ -101,10 +102,25 @@ export async function removeShare(id: string): Promise<void> {
|
||||
|
||||
export async function listSavedShares(): Promise<Share[]> {
|
||||
const saved = await listResources('share');
|
||||
return saved.map((resource) => ({
|
||||
const rows = await Promise.all(saved.map(async (resource): Promise<Share | null> => {
|
||||
try {
|
||||
const { session } = await getSession(resource.id);
|
||||
const raw = await readShareInfo(session, resource);
|
||||
const info = toShareInfo(raw, resource);
|
||||
await cacheResource(resource, shareCache(raw));
|
||||
return { ...info, dataSource: 'server' };
|
||||
} catch (error) {
|
||||
if (error instanceof Error && 'status' in error && (error.status === 404 || error.status === 410)) return null;
|
||||
if (!isOfflineFailure(error)) throw error;
|
||||
if (resource.shareMetadata) return { ...toShareInfo(resource.shareMetadata, resource), dataSource: 'cache' };
|
||||
return {
|
||||
id: resource.id, state: resource.state ?? 'draft', mine: resource.mine ?? false,
|
||||
link: resource.link, createdAt: resource.createdAt, expiresAt: resource.expiresAt,
|
||||
ttlSeconds: resource.ttlSeconds ?? 86400, maxVisitors: resource.maxVisitors ?? 0,
|
||||
ttlSeconds: resource.ttlSeconds ?? Math.max(0, (resource.expiresAt - resource.createdAt) / 1000), maxVisitors: resource.maxVisitors ?? 0,
|
||||
maxDownloads: resource.maxDownloads ?? null,
|
||||
dataSource: 'cache',
|
||||
};
|
||||
}
|
||||
}));
|
||||
return rows.filter((row): row is Share => row !== null);
|
||||
}
|
||||
Reference in new issue
Block a user