@rapidmx/meet-plugin 0.14.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -55
- package/apps/meet/[token].tsx +341 -341
- package/apps/meet/_CallControls.tsx +508 -463
- package/apps/meet/_CallView.tsx +1060 -891
- package/apps/meet/_ChatPanel.tsx +194 -0
- package/apps/meet/_ColorWheel.tsx +136 -0
- package/apps/meet/_DiagnosticsPanel.tsx +65 -7
- package/apps/meet/_MeetLobby.tsx +224 -224
- package/apps/meet/_ParticipantTile.tsx +243 -169
- package/apps/meet/_ParticipantsDrawer.tsx +10 -7
- package/apps/meet/_Whiteboard.tsx +577 -0
- package/apps/meet/_icons.tsx +135 -80
- package/apps/meet/_meetApi.ts +213 -212
- package/apps/shared/collab/CollabSession.ts +675 -0
- package/apps/shared/collab/WhiteboardModel.ts +216 -0
- package/apps/shared/collab/chatMarkdown.tsx +375 -0
- package/apps/shared/collab/color.ts +67 -0
- package/apps/shared/collab/protocol.ts +321 -0
- package/apps/shared/collab/whiteboardRender.ts +311 -0
- package/apps/shared/media/filters/VideoFilterProcessor.ts +408 -408
- package/apps/shared/media/filters/mlModels.ts +174 -174
- package/apps/shared/relay/AudioPlayer.ts +29 -0
- package/apps/shared/relay/AudioSender.ts +23 -0
- package/apps/shared/relay/RelayClient.ts +54 -2
- package/apps/shared/relay/RelayReceiver.ts +43 -2
- package/apps/shared/relay/RelaySender.ts +23 -1
- package/apps/shared/relay/RelayTransport.ts +21 -1
- package/apps/shared/relay/SilenceMeter.ts +59 -0
- package/apps/shared/relay/relayEnv.ts +1 -1
- package/apps/shared/signaling/SignalingClient.ts +335 -0
- package/apps/shared/webrtc/MeshConnectionManager.ts +971 -949
- package/apps/shared/webrtc/types.ts +335 -247
- package/dist/apps/meet/[token].js +1 -1
- package/dist/apps/meet/_CallControls.d.ts +21 -6
- package/dist/apps/meet/_CallControls.js +14 -8
- package/dist/apps/meet/_CallView.d.ts +36 -7
- package/dist/apps/meet/_CallView.js +132 -16
- package/dist/apps/meet/_ChatPanel.d.ts +24 -0
- package/dist/apps/meet/_ChatPanel.js +116 -0
- package/dist/apps/meet/_ColorWheel.d.ts +14 -0
- package/dist/apps/meet/_ColorWheel.js +82 -0
- package/dist/apps/meet/_DiagnosticsPanel.d.ts +5 -3
- package/dist/apps/meet/_DiagnosticsPanel.js +17 -1
- package/dist/apps/meet/_ParticipantTile.d.ts +11 -3
- package/dist/apps/meet/_ParticipantTile.js +36 -5
- package/dist/apps/meet/_ParticipantsDrawer.js +3 -3
- package/dist/apps/meet/_Whiteboard.d.ts +64 -0
- package/dist/apps/meet/_Whiteboard.js +312 -0
- package/dist/apps/meet/_icons.d.ts +16 -0
- package/dist/apps/meet/_icons.js +16 -0
- package/dist/apps/meet/_meetApi.d.ts +8 -7
- package/dist/apps/meet/_meetApi.js +2 -2
- package/dist/apps/shared/collab/CollabSession.d.ts +208 -0
- package/dist/apps/shared/collab/CollabSession.js +559 -0
- package/dist/apps/shared/collab/WhiteboardModel.d.ts +54 -0
- package/dist/apps/shared/collab/WhiteboardModel.js +197 -0
- package/dist/apps/shared/collab/chatMarkdown.d.ts +65 -0
- package/dist/apps/shared/collab/chatMarkdown.js +311 -0
- package/dist/apps/shared/collab/color.d.ts +18 -0
- package/dist/apps/shared/collab/color.js +59 -0
- package/dist/apps/shared/collab/protocol.d.ts +157 -0
- package/dist/apps/shared/collab/protocol.js +222 -0
- package/dist/apps/shared/collab/whiteboardRender.d.ts +31 -0
- package/dist/apps/shared/collab/whiteboardRender.js +268 -0
- package/dist/apps/shared/relay/AudioPlayer.d.ts +9 -0
- package/dist/apps/shared/relay/AudioPlayer.js +24 -4
- package/dist/apps/shared/relay/AudioSender.d.ts +8 -0
- package/dist/apps/shared/relay/AudioSender.js +20 -4
- package/dist/apps/shared/relay/RelayClient.d.ts +16 -0
- package/dist/apps/shared/relay/RelayClient.js +46 -2
- package/dist/apps/shared/relay/RelayReceiver.d.ts +5 -0
- package/dist/apps/shared/relay/RelayReceiver.js +38 -6
- package/dist/apps/shared/relay/RelaySender.d.ts +7 -0
- package/dist/apps/shared/relay/RelaySender.js +20 -5
- package/dist/apps/shared/relay/RelayTransport.d.ts +6 -1
- package/dist/apps/shared/relay/RelayTransport.js +16 -1
- package/dist/apps/shared/relay/SilenceMeter.d.ts +25 -0
- package/dist/apps/shared/relay/SilenceMeter.js +55 -0
- package/dist/apps/shared/relay/relayEnv.d.ts +1 -1
- package/dist/apps/shared/signaling/SignalingClient.d.ts +95 -0
- package/dist/apps/shared/signaling/SignalingClient.js +278 -0
- package/dist/apps/shared/webrtc/MeshConnectionManager.d.ts +7 -7
- package/dist/apps/shared/webrtc/MeshConnectionManager.js +28 -9
- package/dist/apps/shared/webrtc/types.d.ts +104 -26
- package/dist/apps/shared/webrtc/types.js +3 -4
- package/dist/lib/routes/BaseVideoMeetingRoute.js +171 -69
- package/dist/lib/routes/BaseVideoMeetingRoute.js.map +1 -1
- package/dist/lib/util/RelayHub.js +15 -2
- package/dist/lib/util/RelayHub.js.map +1 -1
- package/dist/lib/util/SignalingHub.js +339 -0
- package/dist/lib/util/SignalingHub.js.map +1 -0
- package/dist/types/routes/BaseVideoMeetingRoute.d.ts +68 -46
- package/dist/types/util/RelayHub.d.ts +5 -2
- package/dist/types/util/SignalingHub.d.ts +112 -0
- package/package.json +208 -208
- package/apps/shared/push/GuestSignalingClient.ts +0 -359
- package/dist/apps/shared/push/GuestSignalingClient.d.ts +0 -98
- package/dist/apps/shared/push/GuestSignalingClient.js +0 -312
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SignalingHub.js","sourceRoot":"","sources":["../../../src/util/SignalingHub.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type JWTUser } from "@rapidrest/core";
|
|
2
|
-
import { HttpRequest } from "@rapidrest/service-core";
|
|
3
|
-
import { Mailbox } from "@rapidmx/restapi";
|
|
2
|
+
import { HttpRequest, RepoUtils } from "@rapidrest/service-core";
|
|
3
|
+
import { CalendarEventAttendeeLink, Mailbox } from "@rapidmx/restapi";
|
|
4
4
|
import { IceServerConfig } from "../util/IceServerUtils.js";
|
|
5
5
|
import { VideoMeeting, VideoMeetingInvitee, VideoMeetingStatus, VideoMeetingVisibility } from "../models/types.js";
|
|
6
6
|
/** How long a guest JWT minted by `join()` remains valid for. Four hours comfortably covers a realistic call's
|
|
@@ -97,12 +97,12 @@ export interface PublicVideoMeeting {
|
|
|
97
97
|
* **`authenticated`/`selfUid`/`token` - reading this shape correctly.** A caller who already presented a valid
|
|
98
98
|
* session for a real, non-guest RapidMX identity (see `join()`'s doc comment for exactly how that's told apart
|
|
99
99
|
* from a returning guest) gets `authenticated: true` and no `token`/`expiresAt` at all - there is no guest JWT to
|
|
100
|
-
* hand over, because the caller's own already-existing session cookie/header already authenticates
|
|
101
|
-
* them, with zero new client-side auth handling. The frontend must not write
|
|
102
|
-
* would be pointless at best, and at worst overwrite the caller's real cookie
|
|
103
|
-
* The common anonymous case (`authenticated: false`) is unchanged from Phase 1:
|
|
104
|
-
* and the frontend applies `token` as the `jwt` cookie before connecting - see
|
|
105
|
-
* `apps/shared/
|
|
100
|
+
* hand over, because the caller's own already-existing session cookie/header already authenticates the meeting's
|
|
101
|
+
* WebSockets (`signal()`, `relay()`) for them, with zero new client-side auth handling. The frontend must not write
|
|
102
|
+
* any cookie in that case (doing so would be pointless at best, and at worst overwrite the caller's real cookie
|
|
103
|
+
* with... the value it already has). The common anonymous case (`authenticated: false`) is unchanged from Phase 1:
|
|
104
|
+
* `token`/`expiresAt` are present and the frontend applies `token` as the `jwt` cookie before connecting - see
|
|
105
|
+
* `apps/shared/signaling/SignalingClient.ts`.
|
|
106
106
|
*/
|
|
107
107
|
export interface VideoMeetingJoinResult {
|
|
108
108
|
meeting: PublicVideoMeeting;
|
|
@@ -110,14 +110,14 @@ export interface VideoMeetingJoinResult {
|
|
|
110
110
|
/** `true` when the caller already presented a valid session for a real RapidMX identity when calling `join()`;
|
|
111
111
|
* `false` for the common anonymous case, where a fresh guest identity was just minted instead. */
|
|
112
112
|
authenticated: boolean;
|
|
113
|
-
/** The identity now holding `READ`/`CREATE` on the meeting's
|
|
113
|
+
/** The identity now holding `READ`/`CREATE` on the meeting's uid (what `signal()` and `relay()` check), and the uid the frontend's mesh
|
|
114
114
|
* connection manager (`apps/shared/webrtc/MeshConnectionManager.ts`) must identify itself as: the caller's own
|
|
115
115
|
* real uid when `authenticated` is `true`, otherwise the freshly minted synthetic `guest:<random>` uid `token`
|
|
116
116
|
* authenticates as. Always present - this replaces Phase 1's guest-only `guestUid` field now that the same
|
|
117
117
|
* grant-and-identify step also runs for a real, already-authenticated caller. */
|
|
118
118
|
selfUid: string;
|
|
119
|
-
/** A short-lived guest JWT, immediately usable
|
|
120
|
-
* and nothing else. Present only when `authenticated` is `false`; omitted entirely for an already-authenticated
|
|
119
|
+
/** A short-lived guest JWT, immediately usable to open the meeting's signaling (`signal()`) and relay (`relay()`)
|
|
120
|
+
* WebSockets for `meeting.uid` - and nothing else. Present only when `authenticated` is `false`; omitted entirely for an already-authenticated
|
|
121
121
|
* real caller, who has no guest token minted for them at all (see `join()`). See this class's doc comment. */
|
|
122
122
|
token?: string;
|
|
123
123
|
/** ISO 8601 instant `token` expires at. Present only when `authenticated` is `false`, exactly when `token`
|
|
@@ -136,7 +136,7 @@ export interface VideoMeetingJoinResult {
|
|
|
136
136
|
* (`meeting.hasPassword`) that the caller has not yet submitted - see `join()`'s doc comment for exactly when this
|
|
137
137
|
* applies (never for the organizer's own slug, which already proves stronger authority). Carries only enough to
|
|
138
138
|
* show a password prompt (the meeting's title/host, so the prompt isn't a bare form); nothing here grants
|
|
139
|
-
*
|
|
139
|
+
* signaling access, mints a guest identity, or includes ICE servers - that only happens once
|
|
140
140
|
* `verifyPassword()` succeeds, returning an ordinary `VideoMeetingJoinResult` instead. The literal `true` is a
|
|
141
141
|
* discriminant: a caller narrows on `"requiresPassword" in result && result.requiresPassword`.
|
|
142
142
|
*/
|
|
@@ -195,11 +195,11 @@ export type VideoMeetingJoinResponse = VideoMeetingJoinResult | VideoMeetingPass
|
|
|
195
195
|
*
|
|
196
196
|
* ## Why `VideoMeeting` gets its own per-record `AccessControlList`
|
|
197
197
|
*
|
|
198
|
-
* Unlike `Booking`, `VideoMeeting.uid` doubles as
|
|
199
|
-
*
|
|
200
|
-
* published
|
|
201
|
-
*
|
|
202
|
-
* `AccessControlList` document keyed by that exact uid - there being no such document at all (the case for
|
|
198
|
+
* Unlike `Booking`, `VideoMeeting.uid` doubles as the meeting's signaling channel: SDP offers/answers and ICE
|
|
199
|
+
* candidates travel over `signal()`'s WebSocket (see "The signaling WebSocket" below; until it replaced them they
|
|
200
|
+
* were published on the server's shared `/push` route), whose room is the meeting's uid. Like a `/push` channel
|
|
201
|
+
* before it, that socket is authorized purely by `ACLUtils.hasPermission(user, meetingUid, action)` against a
|
|
202
|
+
* per-record `AccessControlList` document keyed by that exact uid - there being no such document at all (the case for
|
|
203
203
|
* `Booking`, whose `@Protect` sets `recordACL: false`) makes `hasPermission()` always answer `false`. So
|
|
204
204
|
* `VideoMeetingMongo`/`VideoMeetingSQL` set `recordACL: true`, and `create()` below claims that per-record ACL
|
|
205
205
|
* with `parentUid` set to the meeting's own `mailboxUid` (mirroring `BaseFolderRoute`'s identical `acl: { uid,
|
|
@@ -210,17 +210,17 @@ export type VideoMeetingJoinResponse = VideoMeetingJoinResult | VideoMeetingPass
|
|
|
210
210
|
*
|
|
211
211
|
* ## The channel-ACL-grant mechanism
|
|
212
212
|
*
|
|
213
|
-
* An anonymous guest holds no `AccessControlList` grant of their own and has no `JWTUser` to authenticate
|
|
214
|
-
* with in the first place - `join()` therefore does two things together: it mints a short-lived, scope-limited
|
|
213
|
+
* An anonymous guest holds no `AccessControlList` grant of their own and has no `JWTUser` to authenticate the
|
|
214
|
+
* meeting's WebSockets with in the first place - `join()` therefore does two things together: it mints a short-lived, scope-limited
|
|
215
215
|
* guest JWT (`mintGuestToken()`: a synthetic `guest:<random>` uid, no roles, `GUEST_JWT_TTL_SECONDS` expiry, real and
|
|
216
216
|
* verifiable since it's signed with the same `auth` config every other token is), and it adds an explicit
|
|
217
217
|
* `ACLRecord` for that exact uid onto the meeting's own `AccessControlList` (`ensureChannelGrant()`), granting
|
|
218
|
-
* `READ`
|
|
219
|
-
*
|
|
218
|
+
* `READ` and `CREATE` - the pair `signal()` and `relay()` both require (`authorizeMeetingSocket()`), the same pair
|
|
219
|
+
* `/push` demanded to subscribe to and publish on a channel when signaling still went through it. The grant is scoped to exactly
|
|
220
220
|
* that one meeting's uid, nothing else - the same "possession of a link is the credential" pattern
|
|
221
221
|
* `BaseBookingRoute.requireBookingByToken()`/`resolveEffectiveUser()`'s `share:<token>` identity already establish
|
|
222
|
-
* elsewhere in this codebase, extended one step further because signaling needs a
|
|
223
|
-
*
|
|
222
|
+
* elsewhere in this codebase, extended one step further because signaling needs a long-lived socket, not just a
|
|
223
|
+
* stateless REST call.
|
|
224
224
|
*
|
|
225
225
|
* **Known limitation, documented rather than silently assumed away**: because each `join()` call for an anonymous
|
|
226
226
|
* caller mints a *fresh* random guest uid (so simultaneous participants of one shared link are distinguishable from
|
|
@@ -240,16 +240,16 @@ export type VideoMeetingJoinResponse = VideoMeetingJoinResult | VideoMeetingPass
|
|
|
240
240
|
* authenticated-optional endpoint in this codebase's family (e.g. `BaseScopedChildRoute.resolveEffectiveUser()`'s
|
|
241
241
|
* "prefer the real authenticated user, fall back to the anonymous token" precedent). This matters because a
|
|
242
242
|
* logged-in RapidMX user's browser already carries a real, `HttpOnly` `jwt` session cookie for this origin - a
|
|
243
|
-
* cookie `apps/shared/
|
|
243
|
+
* cookie `apps/shared/signaling/SignalingClient.ts` cannot overwrite with a guest token even if `join()` minted one
|
|
244
244
|
* (browsers refuse to let a script override an `HttpOnly` cookie of the same name), so a guest-only `join()` would
|
|
245
|
-
* leave that browser's
|
|
246
|
-
* synthetic guest uid - the
|
|
245
|
+
* leave that browser's signaling WebSocket authenticating as the real session while the meeting's ACL only names a
|
|
246
|
+
* synthetic guest uid - the socket is simply, safely refused, and the real user could never actually join.
|
|
247
247
|
*
|
|
248
248
|
* The fix: when `user` is present and is a *real* identity - not a guest uid from a previous `join()` call, told
|
|
249
249
|
* apart by the `GUEST_UID_PREFIX` a guest uid always starts with and a real, mailbox-owning identity never can -
|
|
250
250
|
* `join()` grants `user.uid` itself (not a synthetic one) `READ`/`CREATE` on the meeting's channel via
|
|
251
251
|
* `ensureChannelGrant()`, mints no guest JWT at all, and returns `authenticated: true` with `selfUid: user.uid`. The
|
|
252
|
-
* caller's own already-existing session cookie now already authenticates
|
|
252
|
+
* caller's own already-existing session cookie now already authenticates `signal()` for them with zero new
|
|
253
253
|
* client-side auth handling - the frontend must not write a cookie of its own in this case (see
|
|
254
254
|
* `VideoMeetingJoinResult`'s doc comment). When `user` is absent (the common, true-anonymous case - no existing
|
|
255
255
|
* session at all), behavior is exactly Phase 1's: a fresh guest identity is minted and granted instead.
|
|
@@ -310,6 +310,19 @@ export type VideoMeetingJoinResponse = VideoMeetingJoinResult | VideoMeetingPass
|
|
|
310
310
|
* it): a disabled relay closes every socket with 1008 "Relay disabled." and `join()` reports `relayEnabled: false`, so
|
|
311
311
|
* clients don't try it.
|
|
312
312
|
*
|
|
313
|
+
* ## The signaling WebSocket (`signal()`)
|
|
314
|
+
*
|
|
315
|
+
* Every participant's offers, answers, ICE candidates, presence, moderation signals, chat messages and whiteboard
|
|
316
|
+
* strokes travel over this plugin's own WebSocket, `signal()` at `/signal/:id`
|
|
317
|
+
* (`/api/mail/video-meetings/signal/<meetingUid>`), not the server's shared `/push` route: one socket in both
|
|
318
|
+
* directions, so a message is not a whole HTTP request, and a meeting's traffic never mixes with the webmail's own
|
|
319
|
+
* push notifications. `SignalingHub` carries the messages (see it for the protocol and limits) on the same bus as the
|
|
320
|
+
* relay, so it crosses replicas exactly when the relay does. It is authorized exactly as `relay()` is (the same
|
|
321
|
+
* `READ`/`CREATE` grant from `join()`, the same live-meeting check, the same "Not permitted." for every refusal) but
|
|
322
|
+
* is never switched off - without it there is no call. The hub stamps every message with the socket's own uid and
|
|
323
|
+
* peer id, so nobody can speak as someone else, and a point-to-point message (`to`) reaches only that peer. `kick()`
|
|
324
|
+
* closes the removed participant's sockets at once.
|
|
325
|
+
*
|
|
313
326
|
* ## Other known limitations
|
|
314
327
|
*
|
|
315
328
|
* **`VideoMeetingInvitee.joinToken` never expires** and has no GC job - identical tradeoff to `Booking.manageToken`.
|
|
@@ -332,10 +345,10 @@ export declare abstract class BaseVideoMeetingRoute<VM extends VideoMeeting, VMI
|
|
|
332
345
|
* `CalendarEventAttendeeLinkSQL`, from `@rapidmx/restapi`) - see `persistMeeting()`. */
|
|
333
346
|
protected abstract attendeeLinkClass: any;
|
|
334
347
|
private _objectFactory?;
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
348
|
+
protected meetingRepo?: RepoUtils<VM>;
|
|
349
|
+
protected inviteeRepo?: RepoUtils<VMI>;
|
|
350
|
+
protected mailboxRepo?: RepoUtils<M>;
|
|
351
|
+
protected attendeeLinkRepo?: RepoUtils<CalendarEventAttendeeLink>;
|
|
339
352
|
private aclUtils?;
|
|
340
353
|
private trustedRoles;
|
|
341
354
|
/** The `auth` config every other JWT in this deployment is signed/verified with - used by `mintGuestToken()` to
|
|
@@ -359,12 +372,15 @@ export declare abstract class BaseVideoMeetingRoute<VM extends VideoMeeting, VMI
|
|
|
359
372
|
* across replicas on; `null` when the deployment has none. Decides how `initRelayBus()` carries relay frames. */
|
|
360
373
|
private eventsConfig;
|
|
361
374
|
private logger;
|
|
362
|
-
/** The rooms of this process's relay sockets. One per route instance
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
private relayHub
|
|
375
|
+
/** The rooms of this process's relay sockets. One per route instance, built through the object factory by
|
|
376
|
+
* `initRelayBus()` - on a Redis bus when the deployment has one, else on an in-process bus - before any socket
|
|
377
|
+
* can attach. Its socket caps are per server replica. */
|
|
378
|
+
private relayHub?;
|
|
366
379
|
/** The Redis bus `relayHub` runs on, when there is one. */
|
|
367
380
|
private relayBus?;
|
|
381
|
+
/** The rooms of this process's signaling sockets (`signal()`), on the same bus as `relayHub` - built alongside it
|
|
382
|
+
* by `initRelayBus()`. Its socket caps are per server replica. */
|
|
383
|
+
private signalingHub?;
|
|
368
384
|
/** Every meeting's pending admission requests (`VideoMeeting.waitingRoomEnabled`), keyed by meeting uid then by
|
|
369
385
|
* the requester's own uid (real or freshly minted guest). In-memory and per server instance only - the same
|
|
370
386
|
* accepted tradeoff `relayHub` above already has: a restart drops anyone currently waiting, who simply
|
|
@@ -381,9 +397,9 @@ export declare abstract class BaseVideoMeetingRoute<VM extends VideoMeeting, VMI
|
|
|
381
397
|
*/
|
|
382
398
|
get modelClass(): any;
|
|
383
399
|
/**
|
|
384
|
-
* Picks how relay frames travel between server replicas, once the route is created: over
|
|
385
|
-
* (`RedisRelayBus`) when `datastores:events` is configured, else in-process only with a single
|
|
386
|
-
* participants on different replicas cannot
|
|
400
|
+
* Picks how relay frames and signaling messages travel between server replicas, once the route is created: over
|
|
401
|
+
* Redis pub/sub (`RedisRelayBus`) when `datastores:events` is configured, else in-process only with a single
|
|
402
|
+
* warning that participants on different replicas cannot reach each other. A Redis bus that cannot be created (the `redis`
|
|
387
403
|
* package is missing, an unusable URL) falls back the same way, with the error logged. Runs before the route
|
|
388
404
|
* serves anything, so no socket is attached to the hub it replaces.
|
|
389
405
|
*/
|
|
@@ -391,7 +407,8 @@ export declare abstract class BaseVideoMeetingRoute<VM extends VideoMeeting, VMI
|
|
|
391
407
|
/** Disconnects the Redis relay bus, if there is one, when the object factory destroys the route. (The factory
|
|
392
408
|
* runs one destroy method per object: a subclass declaring its own `@Destroy` must call this one.) */
|
|
393
409
|
destroyRelayBus(): void;
|
|
394
|
-
|
|
410
|
+
/** Builds the model repositories once, before the route serves anything. */
|
|
411
|
+
protected initialize(): Promise<void>;
|
|
395
412
|
/** Rejects a `403` unless `user` (with its trusted roles stripped - see `util/RouteAccessUtils.ts`) holds
|
|
396
413
|
* `action` on `mailboxUid`, by ownership or an explicit ACL grant. */
|
|
397
414
|
private requireMailboxAccess;
|
|
@@ -509,9 +526,9 @@ export declare abstract class BaseVideoMeetingRoute<VM extends VideoMeeting, VMI
|
|
|
509
526
|
private ensureChannelGrant;
|
|
510
527
|
/**
|
|
511
528
|
* Removes `uid`'s `ACLRecord` from `meetingUid`'s own `AccessControlList`, if it has one - the mirror image of
|
|
512
|
-
* `ensureChannelGrant()`, used to enforce a kick (see `kick()
|
|
513
|
-
*
|
|
514
|
-
* (`SignalMessage`'s doc comment) cannot keep participating. A no-op when `uid` holds no grant (already kicked,
|
|
529
|
+
* `ensureChannelGrant()`, used to enforce a kick (see `kick()`, which also closes `uid`'s open signaling sockets):
|
|
530
|
+
* once removed, `uid` can no longer open the meeting's signaling (`signal()`) or relay (`relay()`) socket, so even
|
|
531
|
+
* a client that ignores the cooperative `"kicked"` signal (`SignalMessage`'s doc comment) cannot keep participating. A no-op when `uid` holds no grant (already kicked,
|
|
515
532
|
* or never joined) or the meeting has no ACL, rather than an error - the caller wants "not able to rejoin",
|
|
516
533
|
* which is already true either way. Same optimistic-lock retry as `ensureChannelGrant()`, for the same reason:
|
|
517
534
|
* a concurrent joiner or the owner editing the ACL directly may save first.
|
|
@@ -585,7 +602,7 @@ export declare abstract class BaseVideoMeetingRoute<VM extends VideoMeeting, VMI
|
|
|
585
602
|
/**
|
|
586
603
|
* Decides whether `user` may open a relay socket to meeting `id`: the relay must be enabled, `user` must hold BOTH
|
|
587
604
|
* `READ` and `CREATE` on the meeting's uid - exactly what `ensureChannelGrant()` gives a joined participant, and
|
|
588
|
-
* what
|
|
605
|
+
* what `signal()` demands too (`authorizeMeetingSocket()`) - and the meeting must exist and be neither cancelled nor ended.
|
|
589
606
|
* Returns the reason to close the socket with, or `undefined` when permitted.
|
|
590
607
|
*
|
|
591
608
|
* `hasPermission()` gets `user` with its trusted roles stripped (`stripTrustedRoles()`), exactly as
|
|
@@ -595,14 +612,19 @@ export declare abstract class BaseVideoMeetingRoute<VM extends VideoMeeting, VMI
|
|
|
595
612
|
* failure gives the same `RELAY_NOT_PERMITTED`, whether the grant, the meeting or its state is what's missing.
|
|
596
613
|
*/
|
|
597
614
|
private authorizeRelay;
|
|
615
|
+
/** Why a meeting WebSocket (`relay()`, `signal()`) of `user` for meeting `id` must be refused, or `undefined` to
|
|
616
|
+
* accept it - see `authorizeRelay()`'s doc comment for the rules, which are the same for both apart from the
|
|
617
|
+
* relay's own on/off setting. */
|
|
618
|
+
private authorizeMeetingSocket;
|
|
598
619
|
/**
|
|
599
|
-
* The meeting uid a relay socket's URL names. `@Param("id")` is filled in by the Bun router, but NOT by the uWS
|
|
620
|
+
* The meeting uid a relay or signaling socket's URL names. `@Param("id")` is filled in by the Bun router, but NOT by the uWS
|
|
600
621
|
* router this framework actually serves with: it never populates `req.params` for a WebSocket upgrade (a
|
|
601
622
|
* `@WebSocket()` route has, so far, never had a path parameter - `BasePushRoute.connect()` has none). So when the
|
|
602
623
|
* decorated argument is empty this reads the last segment of the request path instead, which the
|
|
603
|
-
* `/relay/:id`
|
|
624
|
+
* `/relay/:id` and `/signal/:id` patterns guarantee is the id (the framework also registers the trailing-slash form; the path
|
|
604
625
|
* never carries the query string). Returns `undefined` when there is none or it isn't valid percent-encoding.
|
|
605
626
|
*/
|
|
606
|
-
private
|
|
627
|
+
private socketMeetingId;
|
|
607
628
|
relay(paramId: string | undefined, req: HttpRequest, sock: any, user?: JWTUser): Promise<void>;
|
|
629
|
+
signal(paramId: string | undefined, req: HttpRequest, sock: any, user?: JWTUser): Promise<void>;
|
|
608
630
|
}
|
|
@@ -133,11 +133,14 @@ export declare function parseRelayEnabled(value: unknown): boolean;
|
|
|
133
133
|
* same room replaces the older one, which is closed with 1008 "Replaced.".
|
|
134
134
|
*
|
|
135
135
|
* After that, `{"op":"want","peers":["<peerId>", ...]}` REPLACES the socket's interest set with the valid entries, at
|
|
136
|
-
* most `RELAY_MAX_WANT_PEERS` of them
|
|
136
|
+
* most `RELAY_MAX_WANT_PEERS` of them, and `{"op":"ping","t":<number>}` is answered at once with
|
|
137
|
+
* `{"op":"pong","t":<the same number>}` (the client's own clock, never interpreted here - it lets the client time its
|
|
138
|
+
* round trip to the server, on the same socket and so behind the same queues as its media); every other or malformed
|
|
139
|
+
* text message, including a ping without a finite numeric `t`, is ignored. Binary messages of 1 to
|
|
137
140
|
* `RelayHubOptions.maxPayloadBytes` bytes (`RELAY_MAX_PAYLOAD_BYTES` unless the route asked for more; opaque to the hub) are forwarded to every other socket of the room whose interest
|
|
138
141
|
* set contains the sender's peer id; binary before `hello`, empty and oversized messages are dropped.
|
|
139
142
|
*
|
|
140
|
-
* Server to client: `{"op":"ready","v":1,"maxMessageBytes":N}` once, then binary messages `[1 byte: UTF-8 length N of the sender's peer
|
|
143
|
+
* Server to client: `{"op":"ready","v":1,"maxMessageBytes":N}` once, a `pong` for each ping, then binary messages `[1 byte: UTF-8 length N of the sender's peer
|
|
141
144
|
* id][N bytes: the sender's peer id][the original payload]`. The sender is stamped by the hub from the `hello`,
|
|
142
145
|
* never trusted from the payload.
|
|
143
146
|
*
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { type RelayBus, type RelayConnection, type RelaySocket } from "./RelayHub.js";
|
|
2
|
+
/** The version of the signaling wire protocol this module implements - the `v` of `hello`/`ready`. */
|
|
3
|
+
export declare const SIGNALING_PROTOCOL_VERSION = 1;
|
|
4
|
+
/** The largest text message a socket may send when the route could not raise the framework's own limit. */
|
|
5
|
+
export declare const SIGNALING_MAX_PAYLOAD_BYTES: number;
|
|
6
|
+
/** The larger message limit the signaling route asks the framework for when it can (see the relay's identical
|
|
7
|
+
* `RELAY_LARGE_PAYLOAD_BYTES`): a whiteboard's catch-up for a late joiner then travels in a few messages, not dozens. */
|
|
8
|
+
export declare const SIGNALING_LARGE_PAYLOAD_BYTES: number;
|
|
9
|
+
/** The most sockets (registered or still awaiting their `hello`) one room holds at once. */
|
|
10
|
+
export declare const SIGNALING_MAX_SOCKETS_PER_ROOM = 16;
|
|
11
|
+
/** The most sockets one authenticated uid may hold in one room at once (several tabs or devices of one account). */
|
|
12
|
+
export declare const SIGNALING_MAX_SOCKETS_PER_UID = 4;
|
|
13
|
+
/** Sustained inbound messages per second per socket. Drawing on the whiteboard sends about 15 a second, and a
|
|
14
|
+
* connection being set up sends a burst of ICE candidates; this leaves room for both at once. */
|
|
15
|
+
export declare const SIGNALING_RATE_MESSAGES_PER_SECOND = 50;
|
|
16
|
+
/** Burst allowance, in messages, of a socket's message bucket - also its capacity. */
|
|
17
|
+
export declare const SIGNALING_BURST_MESSAGES = 200;
|
|
18
|
+
/** Sustained inbound bytes per second per socket. */
|
|
19
|
+
export declare const SIGNALING_RATE_BYTES_PER_SECOND: number;
|
|
20
|
+
/** Burst allowance, in bytes, of a socket's byte bucket - also its capacity. Enough for a late joiner's catch-up. */
|
|
21
|
+
export declare const SIGNALING_BURST_BYTES: number;
|
|
22
|
+
/** How long a socket may stay over budget continuously before it is closed with 1008 "Too much data.". */
|
|
23
|
+
export declare const SIGNALING_OVER_BUDGET_CLOSE_MS = 10000;
|
|
24
|
+
/** Two over-budget drops further apart than this are separate episodes - see `RELAY_OVER_BUDGET_GRACE_MS`. */
|
|
25
|
+
export declare const SIGNALING_OVER_BUDGET_GRACE_MS = 1000;
|
|
26
|
+
/** How long a new socket has to send its `hello` before it is closed with 1008 "Hello timeout.". */
|
|
27
|
+
export declare const SIGNALING_HELLO_TIMEOUT_MS = 10000;
|
|
28
|
+
/** The reason a socket is closed with (1008) when its uid is removed from the meeting - see `SignalingHub.evict()`. */
|
|
29
|
+
export declare const SIGNALING_EVICTED_REASON = "Removed from the meeting.";
|
|
30
|
+
/** The bus room a meeting's signaling travels in, kept apart from the same meeting's relay room on a shared bus. */
|
|
31
|
+
export declare function signalingBusRoom(roomId: string): string;
|
|
32
|
+
/** Counters of what the hub did with messages - for tests and diagnostics. */
|
|
33
|
+
export interface SignalingStats {
|
|
34
|
+
/** Messages handed to a receiving socket. */
|
|
35
|
+
delivered: number;
|
|
36
|
+
/** Messages dropped because their sender was over budget. */
|
|
37
|
+
droppedBudget: number;
|
|
38
|
+
/** Messages dropped for being malformed, oversized, or sent before `hello`. */
|
|
39
|
+
droppedInvalid: number;
|
|
40
|
+
}
|
|
41
|
+
export interface SignalingHubOptions {
|
|
42
|
+
/** Where messages travel between sockets. Defaults to a new `InProcessRelayBus`. A bus shared with a `RelayHub` is
|
|
43
|
+
* fine - the hub keeps to its own rooms (`signalingBusRoom()`). */
|
|
44
|
+
bus?: RelayBus;
|
|
45
|
+
logger?: {
|
|
46
|
+
debug(message: string): void;
|
|
47
|
+
};
|
|
48
|
+
/** The largest text message a socket may send, and what `ready` tells the client. Defaults to
|
|
49
|
+
* `SIGNALING_MAX_PAYLOAD_BYTES`; the route sets `SIGNALING_LARGE_PAYLOAD_BYTES` when the framework was asked for that. */
|
|
50
|
+
maxPayloadBytes?: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The meeting's own signaling channel: a WebSocket per participant tab, carrying the JSON `SignalMessage`s of the
|
|
54
|
+
* full-mesh call (`apps/shared/webrtc/types.ts`) - offers, answers, ICE candidates, presence, moderation, chat and
|
|
55
|
+
* whiteboard - between the participants of one room (one meeting). It replaces publishing on the server's shared `/push`
|
|
56
|
+
* route, so the call's traffic is neither an HTTP request per message nor shared with the webmail's own push
|
|
57
|
+
* notifications. Like `RelayHub` it knows nothing about meetings, users, HTTP or any framework: a caller authorizes a
|
|
58
|
+
* socket however it likes, hands it to `attach()` and feeds the returned connection the socket's events.
|
|
59
|
+
*
|
|
60
|
+
* ## Wire protocol (version 1, text messages only)
|
|
61
|
+
*
|
|
62
|
+
* Client to server, the first message must be `{"op":"hello","v":1,"peer":"<peerId>"}` within
|
|
63
|
+
* `SIGNALING_HELLO_TIMEOUT_MS`, `peer` being the socket's uid or starting with `<uid>~` (the same rule, and the same
|
|
64
|
+
* closing reasons, as the relay's `hello`). The server answers `{"op":"ready","v":1,"maxMessageBytes":N}`. A second
|
|
65
|
+
* socket registering an already registered peer in the same room replaces the older one ("Replaced.").
|
|
66
|
+
*
|
|
67
|
+
* After that, `{"op":"signal","data":{...}}` sends `data` to the room, and `{"op":"ping","t":<number>}` is answered
|
|
68
|
+
* with `{"op":"pong","t":<the same number>}` (a keepalive). The server overwrites `data.from` with the socket's uid
|
|
69
|
+
* and `data.peer` with its peer id, so nobody can speak as someone else. When `data.to` names a peer id, only that peer's
|
|
70
|
+
* socket receives it - an offer, an answer and ICE candidates never reach anyone they are not for; otherwise every
|
|
71
|
+
* other registered socket of the room does. Each arrives as `{"op":"signal","data":{...}}`. Anything malformed,
|
|
72
|
+
* binary, oversized, or sent before `hello`, is dropped.
|
|
73
|
+
*
|
|
74
|
+
* ## Limits and removal
|
|
75
|
+
*
|
|
76
|
+
* `SIGNALING_MAX_SOCKETS_PER_ROOM` sockets per room and `SIGNALING_MAX_SOCKETS_PER_UID` per uid per room ("Room full."
|
|
77
|
+
* / "Too many connections."). Each socket's messages are metered by two token buckets (messages and bytes); an
|
|
78
|
+
* over-budget message is dropped, and a socket kept over budget for `SIGNALING_OVER_BUDGET_CLOSE_MS` is closed with
|
|
79
|
+
* "Too much data.". `evict()` closes every socket of one uid in a room, on every replica the bus reaches - the host
|
|
80
|
+
* removing someone takes them off the signaling channel at once, not at their next reconnect.
|
|
81
|
+
*
|
|
82
|
+
* ## Replicas
|
|
83
|
+
*
|
|
84
|
+
* Exactly as `RelayHub`'s: messages cross replicas when the bus does (`RedisRelayBus`), and every limit is per replica.
|
|
85
|
+
*/
|
|
86
|
+
export declare class SignalingHub {
|
|
87
|
+
readonly stats: SignalingStats;
|
|
88
|
+
private readonly bus;
|
|
89
|
+
private readonly rooms;
|
|
90
|
+
private readonly env;
|
|
91
|
+
private readonly logger?;
|
|
92
|
+
constructor(options?: SignalingHubOptions);
|
|
93
|
+
/** The number of rooms that currently hold at least one socket. */
|
|
94
|
+
get roomCount(): number;
|
|
95
|
+
/** The number of sockets in `roomId` (0 for an unknown room). */
|
|
96
|
+
socketCount(roomId: string): number;
|
|
97
|
+
/**
|
|
98
|
+
* Accepts an already-authorized `socket` of `uid` into room `roomId`, or refuses it (closing it with 1008) when
|
|
99
|
+
* the room or the uid is at its limit. Returns the connection to feed the socket's events to, or `undefined`
|
|
100
|
+
* when refused.
|
|
101
|
+
*/
|
|
102
|
+
attach(roomId: string, uid: string, socket: RelaySocket): RelayConnection | undefined;
|
|
103
|
+
/** Closes every socket `uid` holds in room `roomId` (`SIGNALING_EVICTED_REASON`), on this replica and - through
|
|
104
|
+
* the bus - every other. */
|
|
105
|
+
evict(roomId: string, uid: string): void;
|
|
106
|
+
private evictLocal;
|
|
107
|
+
/** Forgets `conn`, and its room once that is empty. */
|
|
108
|
+
private remove;
|
|
109
|
+
/** Hands one bus frame to the sockets of `busRoom` it is for. A frame for a room that has just emptied, or one
|
|
110
|
+
* that is not a frame this hub publishes, is ignored. */
|
|
111
|
+
private deliver;
|
|
112
|
+
}
|