feat(web): add spaces, grouped channels and file chat

This commit is contained in:
justme committed 2026-10-13 01:39:23 +08:00
1 parent c7d8ef15bc
commit f6cfb12716
111 files changed
+4371 -506

No files matched your search

@@ -0,0 +1,37 @@
import { parseChannelLink, type ParsedChannelLink } from '@/lib/channelLink';
import { makeLink } from './links';
import { decryptInvitation, encryptInvitation } from './accountCrypto';
interface ChannelGrant {
v: 1;
channelId: string;
mode: 'enc' | 'raw';
role: 'owner' | 'member';
keys: ParsedChannelLink['keys'];
}
export async function sealChannelGrant(link: string, recipient: string, publicKey: JsonWebKey): Promise<string> {
const parsed = parseChannelLink(link);
if (!parsed.ok) throw new Error('空间凭据格式无效');
const grant: ChannelGrant = { v: 1, channelId: parsed.link.id, mode: parsed.link.mode,
role: parsed.link.isOwner ? 'owner' : 'member', keys: parsed.link.keys };
return encryptInvitation(JSON.stringify(grant), recipient, grant.channelId, publicKey);
}
export async function openChannelGrant(payload: string, recipient: string, channelId: string, privateKey: JsonWebKey,
role: 'owner' | 'member', origin: string): Promise<ParsedChannelLink> {
const plain = await decryptInvitation(payload, recipient, channelId, privateKey);
let parsed: ReturnType<typeof parseChannelLink>;
if (plain.startsWith('{')) {
const grant = JSON.parse(plain) as ChannelGrant;
if (grant.v !== 1 || grant.channelId !== channelId || grant.role !== role || !['enc', 'raw'].includes(grant.mode)
|| !grant.keys || typeof grant.keys !== 'object') throw new Error('空间授权包校验失败');
parsed = parseChannelLink(makeLink(origin, channelId, grant.keys));
if (!parsed.ok || parsed.link.mode !== grant.mode) throw new Error('空间授权包模式不匹配');
} else {
// Existing invitations hold an encrypted link; their secret never becomes a navigation URL.
parsed = parseChannelLink(plain);
}
if (!parsed.ok || parsed.link.id !== channelId || parsed.link.isOwner !== (role === 'owner')) throw new Error('空间授权包权限不匹配');
return parsed.link;
}