feat(communication): federate tenant collaboration with signed delivery

This commit is contained in:
2026-09-30 14:48:05 +02:00
parent ea641bb4d0
commit 7b5696eda4
16 changed files with 914 additions and 14 deletions

View File

@@ -4,6 +4,8 @@ import { CollaborationService, rows } from "../modules/collaboration/service"
import { escapeHtml, fail, Principal, text, UserContext, uuid } from "../modules/collaboration/types"
export type CollaborationFederation = {
deliveries(ctx: UserContext, roomId: string): Promise<any[]>;
retryInvitation(ctx: UserContext, roomId: string, invitationId: string): Promise<any>;
contact(principal: Principal): string;
resolveContact(ctx: UserContext, address: string): Promise<Principal>;
invite(ctx: UserContext, roomId: string, target: Principal, invitationId: string): Promise<void>;
@@ -35,7 +37,16 @@ export async function collaborationRoutes(server: FastifyInstance, options: Coll
server.get("/collaboration/contacts", async (req) => ({ contacts: await service.localContacts(await context(req)) }))
server.post("/collaboration/rooms", async (req) => service.createRoom(await context(req), body(req)))
server.patch("/collaboration/rooms/:id", async (req: any) => service.updateRoom(await context(req), req.params.id, body(req)))
server.get("/collaboration/rooms/:id/management", async (req: any) => service.management(await context(req), req.params.id))
server.get("/collaboration/rooms/:id/management", async (req: any) => {
const ctx = await context(req)
const result = await service.management(ctx, req.params.id)
const deliveries = federation ? await federation.deliveries(ctx, req.params.id) : []
return { ...result, invitations: result.invitations.map(i => ({ ...i, delivery: deliveries.find(d => d.id === i.id) || null })) }
})
server.post("/collaboration/rooms/:id/invitations/:invitationId/retry", async (req: any) => {
if (!federation) fail("Föderation ist nicht konfiguriert", 503)
return federation.retryInvitation(await context(req), req.params.id, req.params.invitationId)
})
server.post("/collaboration/rooms/:id/invitations", async (req: any) => {
const ctx = await context(req), input = body(req)
// Check ownership before resolving or creating any recipient identity.
@@ -59,7 +70,6 @@ export async function collaborationRoutes(server: FastifyInstance, options: Coll
} else fail("Ungültige Teilnehmerart")
await service.rateLimit(`invite:${ctx.tenantId}:${ctx.userId}`, 30, 600)
const invitation = await service.invite(ctx, req.params.id, target, input)
if (target.kind === "remote") await federation!.invite(ctx, req.params.id, target, invitation.id)
const url = target.kind === "guest" ? `${service.options.publicUrl}/guest#invite=${invitation.token}` : null
let mailSent = false
if (url && input.sendEmail === true) {