@rapidmx/meet-plugin 0.3.1 → 0.4.1

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.
@@ -17,7 +17,7 @@ export type VideoMeetingStatus = "scheduled" | "active" | "ended" | "cancelled";
17
17
  /** Mirrors `BaseVideoMeetingRoute.ts`'s `IceServerConfig` - already the exact shape `RTCPeerConnection`'s
18
18
  * `iceServers` constructor option expects, passed straight through with no reshaping. */
19
19
  export interface IceServerConfig {
20
- urls: string;
20
+ urls: string | string[];
21
21
  username?: string;
22
22
  credential?: string;
23
23
  }
@@ -3,7 +3,7 @@ export type VideoMeetingStatus = "scheduled" | "active" | "ended" | "cancelled";
3
3
  /** Mirrors `BaseVideoMeetingRoute.ts`'s `IceServerConfig` - already the exact shape `RTCPeerConnection`'s
4
4
  * `iceServers` constructor option expects, passed straight through with no reshaping. */
5
5
  export interface IceServerConfig {
6
- urls: string;
6
+ urls: string | string[];
7
7
  username?: string;
8
8
  credential?: string;
9
9
  }
@@ -3,6 +3,11 @@
3
3
  // SPDX-License-Identifier: MPL-2.0
4
4
  ///////////////////////////////////////////////////////////////////////////////
5
5
  import * as crypto from "crypto";
6
+ /** The URLs in a `mail:videoconf:turn:url` value: one, or several separated by commas and/or whitespace (for a TURN server
7
+ * listening on both UDP and TLS, `turn:host:3478,turns:host:5349`). Blank entries are dropped. */
8
+ export function parseTurnUrls(value) {
9
+ return value.split(/[\s,]+/).filter(Boolean);
10
+ }
6
11
  /** Always-available, free public STUN servers - discovery only, no media relay. Two independent providers, so a
7
12
  * call still has STUN available if one of them is ever unreachable. */
8
13
  export const DEFAULT_STUN_SERVERS = [
@@ -48,10 +53,12 @@ export function turnRestCredential(sharedSecret, userPart, ttlSeconds = DEFAULT_
48
53
  */
49
54
  export function buildIceServers(settings, options) {
50
55
  const servers = [...DEFAULT_STUN_SERVERS];
51
- const url = settings.url.trim();
52
- if (!url) {
56
+ const turnUrls = parseTurnUrls(settings.url);
57
+ if (turnUrls.length === 0) {
53
58
  return servers;
54
59
  }
60
+ // A single URL stays a string, exactly as before, so a deployment that never sets more than one sees no change.
61
+ const url = turnUrls.length === 1 ? turnUrls[0] : turnUrls;
55
62
  if (settings.sharedSecret.trim()) {
56
63
  const userPart = settings.username.trim() || (options?.randomUserPart ?? (() => crypto.randomBytes(8).toString("hex")))();
57
64
  const { username, credential } = turnRestCredential(settings.sharedSecret.trim(), userPart, options?.ttlSeconds, options?.now);
@@ -1 +1 @@
1
- {"version":3,"file":"IceServerUtils.js","sourceRoot":"","sources":["../../../src/util/IceServerUtils.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAkBjC;uEACuE;AACvE,MAAM,CAAC,MAAM,oBAAoB,GAA+B;IAC5D,EAAE,IAAI,EAAE,8BAA8B,EAAE;IACxC,EAAE,IAAI,EAAE,+BAA+B,EAAE;CAC5C,CAAC;AAEF;;iBAEiB;AACjB,MAAM,CAAC,MAAM,mCAAmC,GAAW,EAAE,GAAG,EAAE,CAAC;AAEnE;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB,CAC9B,YAAoB,EACpB,QAAgB,EAChB,aAAqB,mCAAmC,EACxD,MAAY,IAAI,IAAI,EAAE;IAEtB,MAAM,MAAM,GAAW,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;IAC9F,MAAM,QAAQ,GAAW,GAAG,MAAM,IAAI,QAAQ,EAAE,CAAC;IACjD,MAAM,UAAU,GAAW,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrG,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;AACpC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAC3B,QAAsB,EACtB,OAA4E;IAE5E,MAAM,OAAO,GAAsB,CAAC,GAAG,oBAAoB,CAAC,CAAC;IAC7D,MAAM,GAAG,GAAW,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IACxC,IAAI,CAAC,GAAG,EAAE,CAAC;QACP,OAAO,OAAO,CAAC;IACnB,CAAC;IACD,IAAI,QAAQ,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAW,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,cAAc,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAClI,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,kBAAkB,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;QAC/H,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;IACtD,CAAC;SAAM,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC;QAChE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC5G,CAAC;SAAM,CAAC;QACJ,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"IceServerUtils.js","sourceRoot":"","sources":["../../../src/util/IceServerUtils.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAoBjC;kGACkG;AAClG,MAAM,UAAU,aAAa,CAAC,KAAa;IACvC,OAAO,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AACjD,CAAC;AAED;uEACuE;AACvE,MAAM,CAAC,MAAM,oBAAoB,GAA+B;IAC5D,EAAE,IAAI,EAAE,8BAA8B,EAAE;IACxC,EAAE,IAAI,EAAE,+BAA+B,EAAE;CAC5C,CAAC;AAEF;;iBAEiB;AACjB,MAAM,CAAC,MAAM,mCAAmC,GAAW,EAAE,GAAG,EAAE,CAAC;AAEnE;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB,CAC9B,YAAoB,EACpB,QAAgB,EAChB,aAAqB,mCAAmC,EACxD,MAAY,IAAI,IAAI,EAAE;IAEtB,MAAM,MAAM,GAAW,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;IAC9F,MAAM,QAAQ,GAAW,GAAG,MAAM,IAAI,QAAQ,EAAE,CAAC;IACjD,MAAM,UAAU,GAAW,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrG,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;AACpC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAC3B,QAAsB,EACtB,OAA4E;IAE5E,MAAM,OAAO,GAAsB,CAAC,GAAG,oBAAoB,CAAC,CAAC;IAC7D,MAAM,QAAQ,GAAa,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACvD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,OAAO,CAAC;IACnB,CAAC;IACD,gHAAgH;IAChH,MAAM,GAAG,GAAsB,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC9E,IAAI,QAAQ,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAW,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,cAAc,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAClI,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,kBAAkB,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;QAC/H,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;IACtD,CAAC;SAAM,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC;QAChE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC5G,CAAC;SAAM,CAAC;QACJ,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC"}
@@ -1,17 +1,22 @@
1
1
  /** A single ICE server entry, matching the shape the browser `RTCPeerConnection` `iceServers` constructor option
2
2
  * expects (`RTCIceServer`), so the join response can be handed to it directly with no reshaping. */
3
3
  export interface IceServerConfig {
4
- urls: string;
4
+ /** One URL, or several that share one credential (a TURN server's UDP and TLS addresses, say). */
5
+ urls: string | string[];
5
6
  username?: string;
6
7
  credential?: string;
7
8
  }
8
- /** The `mail:videoconf:turn:*` settings, exactly as configured (empty strings for anything unset). */
9
+ /** The `mail:videoconf:turn:*` settings, exactly as configured (empty strings for anything unset). `url` may name several
10
+ * URLs, separated by commas and/or whitespace - see `parseTurnUrls()`. */
9
11
  export interface TurnSettings {
10
12
  url: string;
11
13
  username: string;
12
14
  credential: string;
13
15
  sharedSecret: string;
14
16
  }
17
+ /** The URLs in a `mail:videoconf:turn:url` value: one, or several separated by commas and/or whitespace (for a TURN server
18
+ * listening on both UDP and TLS, `turn:host:3478,turns:host:5349`). Blank entries are dropped. */
19
+ export declare function parseTurnUrls(value: string): string[];
15
20
  /** Always-available, free public STUN servers - discovery only, no media relay. Two independent providers, so a
16
21
  * call still has STUN available if one of them is ever unreachable. */
17
22
  export declare const DEFAULT_STUN_SERVERS: readonly IceServerConfig[];
package/package.json CHANGED
@@ -1,181 +1,181 @@
1
- {
2
- "name": "@rapidmx/meet-plugin",
3
- "version": "0.3.1",
4
- "description": "RapidMX plugin that provides WebRTC video conferencing, joinable from a calendar invite or a shared link.",
5
- "repository": "https://github.com/RapidMX/meet-plugin.git",
6
- "author": "RapidREST <rapidrests@gmail.com>",
7
- "license": "MPL-2.0",
8
- "contributors": [
9
- "Jean-Philippe Steinmetz"
10
- ],
11
- "scripts": {
12
- "build": "yarn lint && rimraf dist && tsc && tsc -p tsconfig.apps.json",
13
- "lint": "eslint ./src ./apps ./test",
14
- "local-audit": "yarn install && yarn npm audit -A -R \"$@\"",
15
- "checkup": "yarn dlx @yarnpkg/doctor \"$@\"",
16
- "dependency-check": "yarn dlx depcheck \"$@\"",
17
- "report-coverage": "cat ./coverage/lcov.info | coveralls",
18
- "test": "vitest run",
19
- "test:prod": "npm run lint && vitest run --coverage",
20
- "test:watch": "vitest",
21
- "release": "npx @rapidrest/cli release",
22
- "prepublish": "yarn build"
23
- },
24
- "peerDependencies": {
25
- "@rapidmx/react-shared": ">=0.14.0 <1",
26
- "@rapidmx/restapi": ">=0.19.0 <1",
27
- "@rapidmx/web-client": ">=0.11.0 <1",
28
- "@rapidrest/core": "5.x",
29
- "@rapidrest/service-core": "2.x",
30
- "react": "19.x",
31
- "react-dom": "19.x"
32
- },
33
- "devDependencies": {
34
- "@rapidmx/react-shared": "^0.15.0",
35
- "@rapidmx/restapi": "^0.20.1",
36
- "@rapidmx/web-client": "^0.14.1",
37
- "@rapidrest/core": "^6.0.0",
38
- "@rapidrest/service-core": "^2.3.0",
39
- "@swc/core": "^1.16.2",
40
- "@testing-library/dom": "^10.4.1",
41
- "@testing-library/jest-dom": "^7.0.1",
42
- "@testing-library/react": "^16.3.3",
43
- "@testing-library/user-event": "^14.6.7",
44
- "@types/nconf": "^0.10.7",
45
- "@types/node": "^26.5.0",
46
- "@types/react": "^19.2.18",
47
- "@types/react-dom": "^19.2.7",
48
- "@typescript-eslint/eslint-plugin": "^8.70.0",
49
- "@typescript-eslint/parser": "^8.70.0",
50
- "@vitest/coverage-v8": "^5.0.0",
51
- "better-sqlite3": "^13.0.3",
52
- "eslint": "^10.10.0",
53
- "eslint-config-prettier": "^10.1.8",
54
- "eslint-plugin-import": "^2.32.0",
55
- "eslint-plugin-jsdoc": "^64.3.6",
56
- "globals": "^17.12.0",
57
- "jsdom": "^30.0.1",
58
- "lodash-es": "^4.18.1",
59
- "mongodb": "^7.6.0",
60
- "mongodb-memory-server": "^11.2.0",
61
- "nconf": "^0.13.0",
62
- "openapi3-ts": "^4.6.1",
63
- "prom-client": "^15.1.3",
64
- "react": "^19.2.8",
65
- "react-dom": "^19.2.8",
66
- "redis": "^6.2.1",
67
- "reflect-metadata": "^0.2.2",
68
- "rimraf": "^6.1.3",
69
- "ts-node": "^10.9.2",
70
- "tsx": "^4.23.13",
71
- "typeorm": "^1.1.1",
72
- "typescript": "^6.0.3",
73
- "unplugin-swc": "^1.6.0",
74
- "uuid": "^14.0.2",
75
- "vite": "^8.2.2",
76
- "vitest": "^5.0.0",
77
- "winston": "^3.19.0"
78
- },
79
- "resolutions": {
80
- "@rapidmx/react-shared": "^0.14.0",
81
- "@rapidmx/restapi": "^0.19.0",
82
- "@rapidmx/web-client": "^0.11.0"
83
- },
84
- "packageManager": "yarn@4.5.3",
85
- "engines": {
86
- "node": ">=24.0.0"
87
- },
88
- "publishConfig": {
89
- "registry": "https://registry.npmjs.org/"
90
- },
91
- "type": "module",
92
- "main": "dist/lib/index.js",
93
- "exports": {
94
- ".": {
95
- "import": "./dist/lib/index.js",
96
- "types": "./dist/types/index.d.ts"
97
- },
98
- "./mongo": {
99
- "import": "./dist/lib/mongo.js",
100
- "types": "./dist/types/mongo.d.ts"
101
- },
102
- "./sql": {
103
- "import": "./dist/lib/sql.js",
104
- "types": "./dist/types/sql.d.ts"
105
- }
106
- },
107
- "typings": "dist/types/index.d.ts",
108
- "files": [
109
- "apps",
110
- "dist"
111
- ],
112
- "rapidmx": {
113
- "plugin": {
114
- "apiVersion": 1,
115
- "displayName": "Video Conferencing",
116
- "description": "Private and public WebRTC video meetings. A private meeting mints one link per invitee for a calendar invite's location and body; a public meeting shares a single link. Joining needs no account - a guest picks a name, checks their camera and microphone, and enters.",
117
- "mailboxScopedData": true,
118
- "settings": [
119
- {
120
- "key": "mail:videoconf:public_url",
121
- "label": "Public join page URL",
122
- "type": "string",
123
- "default": "https://<host>/meet",
124
- "help": "The address of the public join pages. Filled in with this server's address when the plugin is installed. Calendar invites include a join link only when this is set."
125
- },
126
- {
127
- "key": "mail:videoconf:turn:url",
128
- "label": "TURN server URL",
129
- "type": "string",
130
- "default": "",
131
- "help": "A turn: or turns: URL (e.g. turns:turn.example.com:5349) relaying media for a participant behind a restrictive NAT or firewall, for whom a direct peer-to-peer connection fails. Public STUN servers are used for everyone else and need no configuration. Leave empty to serve calls with STUN only - most calls will still connect, but some participants may not."
132
- },
133
- {
134
- "key": "mail:videoconf:turn:username",
135
- "label": "TURN server username",
136
- "type": "string",
137
- "default": "",
138
- "help": "Used as-is unless a shared secret is also set below, in which case it is combined with a per-join expiring credential instead (coturn's time-limited REST credential mechanism) and this value is ignored."
139
- },
140
- {
141
- "key": "mail:videoconf:turn:credential",
142
- "label": "TURN server credential (static)",
143
- "type": "string",
144
- "default": "",
145
- "help": "The TURN server's password for the username above. Ignored when a shared secret is set below."
146
- },
147
- {
148
- "key": "mail:videoconf:turn:shared_secret",
149
- "label": "TURN server shared secret (for expiring credentials)",
150
- "type": "string",
151
- "default": "",
152
- "help": "coturn's static-auth-secret, if configured. When set, every join mints its own short-lived username/credential pair (an HMAC-SHA1 of a timestamp with this secret) instead of handing out one permanent password to every participant - the recommended setup. Leave empty to use the static username/credential above instead."
153
- }
154
- ],
155
- "ui": {
156
- "apps": [
157
- {
158
- "id": "meet",
159
- "host": "public",
160
- "mount": "/meet",
161
- "dir": "apps/meet"
162
- },
163
- {
164
- "id": "video-conferencing",
165
- "host": "www",
166
- "mount": "/settings/video-conferencing",
167
- "dir": "apps/settings-video-conferencing"
168
- }
169
- ],
170
- "settingsSections": [
171
- {
172
- "id": "video-conferencing",
173
- "label": "Video Conferencing",
174
- "href": "/settings/video-conferencing",
175
- "icon": "HiOutlineVideoCamera"
176
- }
177
- ]
178
- }
179
- }
180
- }
181
- }
1
+ {
2
+ "name": "@rapidmx/meet-plugin",
3
+ "version": "0.4.1",
4
+ "description": "RapidMX plugin that provides WebRTC video conferencing, joinable from a calendar invite or a shared link.",
5
+ "repository": "https://github.com/RapidMX/meet-plugin.git",
6
+ "author": "RapidREST <rapidrests@gmail.com>",
7
+ "license": "MPL-2.0",
8
+ "contributors": [
9
+ "Jean-Philippe Steinmetz"
10
+ ],
11
+ "scripts": {
12
+ "build": "yarn lint && rimraf dist && tsc && tsc -p tsconfig.apps.json",
13
+ "lint": "eslint ./src ./apps ./test",
14
+ "local-audit": "yarn install && yarn npm audit -A -R \"$@\"",
15
+ "checkup": "yarn dlx @yarnpkg/doctor \"$@\"",
16
+ "dependency-check": "yarn dlx depcheck \"$@\"",
17
+ "report-coverage": "cat ./coverage/lcov.info | coveralls",
18
+ "test": "vitest run",
19
+ "test:prod": "npm run lint && vitest run --coverage",
20
+ "test:watch": "vitest",
21
+ "release": "npx @rapidrest/cli release",
22
+ "prepublish": "yarn build"
23
+ },
24
+ "peerDependencies": {
25
+ "@rapidmx/react-shared": ">=0.16.0 <1",
26
+ "@rapidmx/restapi": ">=0.21.1 <1",
27
+ "@rapidmx/web-client": ">=0.15.1 <1",
28
+ "@rapidrest/core": "5.x",
29
+ "@rapidrest/service-core": "2.x",
30
+ "react": "19.x",
31
+ "react-dom": "19.x"
32
+ },
33
+ "devDependencies": {
34
+ "@rapidmx/react-shared": "^0.16.0",
35
+ "@rapidmx/restapi": "^0.21.1",
36
+ "@rapidmx/web-client": "^0.15.1",
37
+ "@rapidrest/core": "^6.0.0",
38
+ "@rapidrest/service-core": "^2.3.0",
39
+ "@swc/core": "^1.16.2",
40
+ "@testing-library/dom": "^10.4.1",
41
+ "@testing-library/jest-dom": "^7.0.1",
42
+ "@testing-library/react": "^16.3.3",
43
+ "@testing-library/user-event": "^14.6.7",
44
+ "@types/nconf": "^0.10.7",
45
+ "@types/node": "^26.5.0",
46
+ "@types/react": "^19.2.18",
47
+ "@types/react-dom": "^19.2.7",
48
+ "@typescript-eslint/eslint-plugin": "^8.70.0",
49
+ "@typescript-eslint/parser": "^8.70.0",
50
+ "@vitest/coverage-v8": "^5.0.0",
51
+ "better-sqlite3": "^13.0.3",
52
+ "eslint": "^10.10.0",
53
+ "eslint-config-prettier": "^10.1.8",
54
+ "eslint-plugin-import": "^2.32.0",
55
+ "eslint-plugin-jsdoc": "^64.3.6",
56
+ "globals": "^17.12.0",
57
+ "jsdom": "^30.0.1",
58
+ "lodash-es": "^4.18.1",
59
+ "mongodb": "^7.6.0",
60
+ "mongodb-memory-server": "^11.2.0",
61
+ "nconf": "^0.13.0",
62
+ "openapi3-ts": "^4.6.1",
63
+ "prom-client": "^15.1.3",
64
+ "react": "^19.2.8",
65
+ "react-dom": "^19.2.8",
66
+ "redis": "^6.2.1",
67
+ "reflect-metadata": "^0.2.2",
68
+ "rimraf": "^6.1.3",
69
+ "ts-node": "^10.9.2",
70
+ "tsx": "^4.23.13",
71
+ "typeorm": "^1.1.1",
72
+ "typescript": "^6.0.3",
73
+ "unplugin-swc": "^1.6.0",
74
+ "uuid": "^14.0.2",
75
+ "vite": "^8.2.2",
76
+ "vitest": "^5.0.0",
77
+ "winston": "^3.19.0"
78
+ },
79
+ "resolutions": {
80
+ "@rapidmx/react-shared": "^0.16.0",
81
+ "@rapidmx/restapi": "^0.21.1",
82
+ "@rapidmx/web-client": "^0.15.1"
83
+ },
84
+ "packageManager": "yarn@4.5.3",
85
+ "engines": {
86
+ "node": ">=24.0.0"
87
+ },
88
+ "publishConfig": {
89
+ "registry": "https://registry.npmjs.org/"
90
+ },
91
+ "type": "module",
92
+ "main": "dist/lib/index.js",
93
+ "exports": {
94
+ ".": {
95
+ "import": "./dist/lib/index.js",
96
+ "types": "./dist/types/index.d.ts"
97
+ },
98
+ "./mongo": {
99
+ "import": "./dist/lib/mongo.js",
100
+ "types": "./dist/types/mongo.d.ts"
101
+ },
102
+ "./sql": {
103
+ "import": "./dist/lib/sql.js",
104
+ "types": "./dist/types/sql.d.ts"
105
+ }
106
+ },
107
+ "typings": "dist/types/index.d.ts",
108
+ "files": [
109
+ "apps",
110
+ "dist"
111
+ ],
112
+ "rapidmx": {
113
+ "plugin": {
114
+ "apiVersion": 1,
115
+ "displayName": "Video Conferencing",
116
+ "description": "Private and public WebRTC video meetings. A private meeting mints one link per invitee for a calendar invite's location and body; a public meeting shares a single link. Joining needs no account - a guest picks a name, checks their camera and microphone, and enters.",
117
+ "mailboxScopedData": true,
118
+ "settings": [
119
+ {
120
+ "key": "mail:videoconf:public_url",
121
+ "label": "Public join page URL",
122
+ "type": "string",
123
+ "default": "https://<host>/meet",
124
+ "help": "The address of the public join pages. Filled in with this server's address when the plugin is installed. Calendar invites include a join link only when this is set."
125
+ },
126
+ {
127
+ "key": "mail:videoconf:turn:url",
128
+ "label": "TURN server URL",
129
+ "type": "string",
130
+ "default": "",
131
+ "help": "A turn: or turns: URL (e.g. turns:turn.example.com:5349), or several separated by commas (turn:turn.example.com:3478,turns:turn.example.com:5349), relaying media for a participant behind a restrictive NAT or firewall, for whom a direct peer-to-peer connection fails. Public STUN servers are used for everyone else and need no configuration. Leave empty to serve calls with STUN only - most calls will still connect, but some participants may not."
132
+ },
133
+ {
134
+ "key": "mail:videoconf:turn:username",
135
+ "label": "TURN server username",
136
+ "type": "string",
137
+ "default": "",
138
+ "help": "Used as-is unless a shared secret is also set below, in which case it is combined with a per-join expiring credential instead (coturn's time-limited REST credential mechanism) and this value is ignored."
139
+ },
140
+ {
141
+ "key": "mail:videoconf:turn:credential",
142
+ "label": "TURN server credential (static)",
143
+ "type": "string",
144
+ "default": "",
145
+ "help": "The TURN server's password for the username above. Ignored when a shared secret is set below."
146
+ },
147
+ {
148
+ "key": "mail:videoconf:turn:shared_secret",
149
+ "label": "TURN server shared secret (for expiring credentials)",
150
+ "type": "string",
151
+ "default": "",
152
+ "help": "coturn's static-auth-secret, if configured. When set, every join mints its own short-lived username/credential pair (an HMAC-SHA1 of a timestamp with this secret) instead of handing out one permanent password to every participant - the recommended setup. Leave empty to use the static username/credential above instead."
153
+ }
154
+ ],
155
+ "ui": {
156
+ "apps": [
157
+ {
158
+ "id": "meet",
159
+ "host": "public",
160
+ "mount": "/meet",
161
+ "dir": "apps/meet"
162
+ },
163
+ {
164
+ "id": "video-conferencing",
165
+ "host": "www",
166
+ "mount": "/settings/video-conferencing",
167
+ "dir": "apps/settings-video-conferencing"
168
+ }
169
+ ],
170
+ "settingsSections": [
171
+ {
172
+ "id": "video-conferencing",
173
+ "label": "Video Conferencing",
174
+ "href": "/settings/video-conferencing",
175
+ "icon": "HiOutlineVideoCamera"
176
+ }
177
+ ]
178
+ }
179
+ }
180
+ }
181
+ }