fedipod-server 0.16.0 → 0.18.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 +37 -15
- package/dist/claims.d.ts +10 -11
- package/dist/claims.js +19 -17
- package/dist/directory.d.ts +11 -3
- package/dist/directory.js +28 -14
- package/dist/handler.d.ts +30 -7
- package/dist/handler.js +133 -42
- package/dist/handler.jsonld +14 -6
- package/dist/streaming-handler.js +7 -2
- package/lib/client/c2s.mjs +95 -61
- package/lib/client/masto/index.mjs +7 -1
- package/lib/client/masto/timelines.mjs +1 -1
- package/lib/client/oidc-auth.mjs +5 -3
- package/lib/core/contexts/anno.json +126 -0
- package/lib/core/contexts/index.mjs +4 -0
- package/lib/core/contexts/map.json +2 -1
- package/lib/core/intake/index.mjs +32 -5
- package/lib/core/publisher/index.mjs +15 -1
- package/lib/core/publisher/notes.mjs +84 -2
- package/lib/core/publisher/questions.mjs +1 -0
- package/lib/core/publisher/restore.mjs +27 -2
- package/lib/core/social.mjs +1 -0
- package/lib/core/store.mjs +4 -0
- package/lib/core/wire.mjs +20 -13
- package/lib/device/admin/routes/gateway.mjs +1 -1
- package/lib/device/admin/surface.mjs +25 -17
- package/lib/device/cli/commands/setup.mjs +9 -1
- package/lib/device/setup.mjs +6 -0
- package/lib/gateway/front-core.mjs +66 -12
- package/lib/gateway/gateway-core.mjs +40 -0
- package/lib/pod/actor.mjs +2 -2
- package/lib/pod/root.mjs +11 -0
- package/lib/pod/transport.mjs +9 -3
- package/lib/server/embed.mjs +23 -6
- package/package.json +2 -1
- package/run-agent.mjs +10 -1
- package/vendor/gate.cjs +5 -2
- package/web/admin/index.html +2 -0
- package/web/admin/upkeep.js +9 -1
- package/web/app/README.md +1 -1
- package/web/app/agent.mjs +11 -1
- package/web/app/boot.mjs +49 -10
- package/web/app/dist/boot.js +89 -37
- package/web/app/dist/boot.js.map +3 -3
- package/web/app/dist/sw.js +1326 -662
- package/web/app/dist/sw.js.map +4 -4
- package/web/app/index.html +16 -6
- package/web/app/signup.mjs +3 -1
- package/web/app/site/admin/index.html +2 -0
- package/web/app/site/admin/upkeep.js +9 -1
- package/web/app/site/boot.js +89 -37
- package/web/app/site/index.html +16 -6
- package/web/app/site/sw.js +1326 -662
- package/web/front/#new-account.html# +0 -43
- package/web/front/new-account.html~ +0 -50
package/dist/handler.js
CHANGED
|
@@ -17,6 +17,14 @@ const adapt_1 = require("./adapt");
|
|
|
17
17
|
const store_css_1 = require("./store-css");
|
|
18
18
|
const store_pod_1 = require("./store-pod");
|
|
19
19
|
const directory_1 = require("./directory");
|
|
20
|
+
/** The pod's path as a mount prefix: `''` for a host root, else `/aisha`. */
|
|
21
|
+
function mountOf(podBase) {
|
|
22
|
+
return new URL(podBase).pathname.replace(/\/+$/u, '');
|
|
23
|
+
}
|
|
24
|
+
/** Whether `a` is `b` or an ancestor path of `b` (both mount-shaped, no trailing slash). */
|
|
25
|
+
function pathContains(a, b) {
|
|
26
|
+
return a === b || b.startsWith(a + '/') || a === '';
|
|
27
|
+
}
|
|
20
28
|
// The JS front-core is FediPod's own ESM tree, reached at runtime. A real
|
|
21
29
|
// dynamic import() built via Function keeps tsc from downleveling it to
|
|
22
30
|
// require() — which cannot load an ESM module with top-level await under a
|
|
@@ -54,7 +62,7 @@ function deriveHandle(podBase) {
|
|
|
54
62
|
function normalizeUiPath(raw) {
|
|
55
63
|
if (raw === '')
|
|
56
64
|
return '';
|
|
57
|
-
const path = raw ?? '/
|
|
65
|
+
const path = raw ?? '/fp/';
|
|
58
66
|
return `/${path.replace(/^\/+|\/+$/gu, '')}/`;
|
|
59
67
|
}
|
|
60
68
|
class FediPodServerHandler extends community_server_1.HttpHandler {
|
|
@@ -63,12 +71,17 @@ class FediPodServerHandler extends community_server_1.HttpHandler {
|
|
|
63
71
|
dir;
|
|
64
72
|
podPut;
|
|
65
73
|
logger = (0, community_server_1.getLoggerFor)(this);
|
|
66
|
-
|
|
74
|
+
// Every pod whose routes this server claims, keyed by pod base. A claim
|
|
75
|
+
// records the host it answers on and the mount — the pod's own path, `''` for
|
|
76
|
+
// a host-root or subdomain pod, `/aisha` for a suffix pod on `server/aisha/`.
|
|
77
|
+
// A suffix pod shares its host (often the front's own) with others, so a
|
|
78
|
+
// request is matched to an identity by host AND mount, never host alone.
|
|
79
|
+
claimed = new Map(); // pod base → claim
|
|
67
80
|
agentHandles = new Map(); // handle → pod base
|
|
68
81
|
frontHost;
|
|
69
82
|
uiPath;
|
|
70
83
|
identities = new Map();
|
|
71
|
-
surfaces = new Map();
|
|
84
|
+
surfaces = new Map(); // pod base → running identity
|
|
72
85
|
registry;
|
|
73
86
|
doorSecrets = new Map(); // pod base → its door secret
|
|
74
87
|
starting = new Set();
|
|
@@ -100,32 +113,81 @@ class FediPodServerHandler extends community_server_1.HttpHandler {
|
|
|
100
113
|
: null;
|
|
101
114
|
}
|
|
102
115
|
/**
|
|
103
|
-
* Whether this pod may become an identity here
|
|
104
|
-
*
|
|
105
|
-
* two pods must never share <agentDataDir>/<handle>/.
|
|
116
|
+
* Whether this pod may become an identity here, and the claim it earns: a
|
|
117
|
+
* real URL, a place no other identity already sits, and a handle no other
|
|
118
|
+
* identity uses — two pods must never share <agentDataDir>/<handle>/.
|
|
119
|
+
*
|
|
120
|
+
* A HOST-ROOT or subdomain pod needs an origin of its own, and it may not be
|
|
121
|
+
* the front's host: the whole surface answers at the origin root, so two of
|
|
122
|
+
* them, or one sharing the front, would collide. A SUFFIX pod lives on a path
|
|
123
|
+
* (`server/aisha/`), so it may share its host — with the front and with other
|
|
124
|
+
* suffix pods — provided no claim already contains or nests under its path.
|
|
106
125
|
*/
|
|
107
|
-
|
|
126
|
+
validateAgentPod(podBase) {
|
|
108
127
|
let host;
|
|
128
|
+
let mount;
|
|
109
129
|
try {
|
|
110
|
-
|
|
130
|
+
const u = new URL(podBase);
|
|
131
|
+
host = u.host.toLowerCase();
|
|
132
|
+
mount = mountOf(podBase);
|
|
111
133
|
}
|
|
112
134
|
catch {
|
|
113
135
|
throw new Error(`not a pod URL: ${podBase}`);
|
|
114
136
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
137
|
+
if (mount === '') {
|
|
138
|
+
// A pod at the root of its host: it owns the whole origin, so it cannot
|
|
139
|
+
// share it with the front or with any other claim.
|
|
140
|
+
if (host.split(':')[0] === String(this.frontHost).toLowerCase()) {
|
|
141
|
+
throw new Error(`${podBase} is on the front's own host — give the identity its own origin, or host it on a path`);
|
|
142
|
+
}
|
|
143
|
+
for (const c of this.claimed.values()) {
|
|
144
|
+
if (c.host === host) {
|
|
145
|
+
throw new Error(`the host ${host} already carries an identity — a host-root identity needs an origin of its own`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
119
148
|
}
|
|
120
|
-
|
|
121
|
-
|
|
149
|
+
else {
|
|
150
|
+
// A pod on a path: it owns only its subtree. Refuse anything that already
|
|
151
|
+
// contains it or that it would contain, so no identity can answer under
|
|
152
|
+
// another's path (and none straddles the front's own routes underneath).
|
|
153
|
+
for (const c of this.claimed.values()) {
|
|
154
|
+
if (c.host !== host)
|
|
155
|
+
continue;
|
|
156
|
+
if (pathContains(c.mount, mount) || pathContains(mount, c.mount)) {
|
|
157
|
+
throw new Error(`${podBase} nests with the identity already at ${c.podBase} — a path pod owns only its own subtree`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
122
160
|
}
|
|
123
161
|
const handle = deriveHandle(podBase);
|
|
124
162
|
const holder = this.agentHandles.get(handle);
|
|
125
163
|
if (holder && holder !== podBase) {
|
|
126
164
|
throw new Error(`the name ${handle} already belongs to ${holder} — two identities cannot share it`);
|
|
127
165
|
}
|
|
128
|
-
return host;
|
|
166
|
+
return { host, mount, podBase, handle };
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* The claim a request belongs to, or null. A request matches when its host is
|
|
170
|
+
* the claim's host and its path is at or under the claim's mount; the deepest
|
|
171
|
+
* mount wins, so a suffix pod's own routes are never swallowed by a shallower
|
|
172
|
+
* claim on the same host.
|
|
173
|
+
*/
|
|
174
|
+
resolveClaim(host, pathname) {
|
|
175
|
+
let best = null;
|
|
176
|
+
for (const c of this.claimed.values()) {
|
|
177
|
+
if (c.host !== host)
|
|
178
|
+
continue;
|
|
179
|
+
if (c.mount === '' || pathname === c.mount || pathname.startsWith(c.mount + '/')) {
|
|
180
|
+
if (!best || c.mount.length > best.mount.length)
|
|
181
|
+
best = c;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return best;
|
|
185
|
+
}
|
|
186
|
+
/** A request path relative to a mount: `/aisha/ap/actor` under `/aisha` → `/ap/actor`. */
|
|
187
|
+
stripMount(pathname, mount) {
|
|
188
|
+
if (!mount)
|
|
189
|
+
return pathname;
|
|
190
|
+
return pathname.slice(mount.length) || '/';
|
|
129
191
|
}
|
|
130
192
|
/**
|
|
131
193
|
* Start an agent for each opted-in pod. Runs before the server listens, so
|
|
@@ -164,21 +226,22 @@ class FediPodServerHandler extends community_server_1.HttpHandler {
|
|
|
164
226
|
// persist, and the next start recovers them.
|
|
165
227
|
const pods = [];
|
|
166
228
|
try {
|
|
167
|
-
const
|
|
168
|
-
for (const
|
|
169
|
-
const row = await this.registry.get(
|
|
229
|
+
const keys = await this.registry.listKeys();
|
|
230
|
+
for (const key of keys) {
|
|
231
|
+
const row = await this.registry.get(key);
|
|
170
232
|
if (!row)
|
|
171
233
|
continue;
|
|
172
|
-
if (this.
|
|
234
|
+
if (this.claimed.has(row.podBase) || this.identities.has(row.podBase))
|
|
173
235
|
continue; // already claimed
|
|
236
|
+
let claim;
|
|
174
237
|
try {
|
|
175
|
-
this.
|
|
238
|
+
claim = this.validateAgentPod(row.podBase);
|
|
176
239
|
}
|
|
177
240
|
catch (e) {
|
|
178
241
|
this.logger.error(`opted-in pod ${row.podBase} no longer valid: ${e.message}`);
|
|
179
242
|
continue;
|
|
180
243
|
}
|
|
181
|
-
this.
|
|
244
|
+
this.claimed.set(row.podBase, claim);
|
|
182
245
|
this.agentHandles.set(row.handle, row.podBase);
|
|
183
246
|
pods.push(row.podBase);
|
|
184
247
|
}
|
|
@@ -266,10 +329,15 @@ class FediPodServerHandler extends community_server_1.HttpHandler {
|
|
|
266
329
|
return;
|
|
267
330
|
}
|
|
268
331
|
this.identities.set(podBase, identity);
|
|
269
|
-
this.surfaces.set(
|
|
332
|
+
this.surfaces.set(podBase, identity);
|
|
270
333
|
this.logger.info(`FediPod agent @${identity.handle} running on ${podBase}`);
|
|
271
|
-
|
|
334
|
+
// A suffix pod cannot answer WebFinger for itself — its host root is
|
|
335
|
+
// the front's — so it is followable ONLY through the door's apex
|
|
336
|
+
// dispatch. Front it whether or not auto-front is on: the row is the
|
|
337
|
+
// whole of what makes @handle@host resolve to it.
|
|
338
|
+
if (this.args.agentAutoFront || this.claimed.get(podBase)?.mount) {
|
|
272
339
|
await this.frontIdentity(podBase, identity);
|
|
340
|
+
}
|
|
273
341
|
return;
|
|
274
342
|
}
|
|
275
343
|
catch (e) {
|
|
@@ -318,14 +386,20 @@ class FediPodServerHandler extends community_server_1.HttpHandler {
|
|
|
318
386
|
}
|
|
319
387
|
}
|
|
320
388
|
async canHandle({ request }) {
|
|
321
|
-
const host = request.headers.host;
|
|
389
|
+
const host = String(request.headers.host ?? '').toLowerCase();
|
|
322
390
|
const pathname = new URL(request.url ?? '/', `https://${host}`).pathname;
|
|
323
|
-
//
|
|
324
|
-
//
|
|
325
|
-
//
|
|
391
|
+
// The front's own routes win first, so a suffix pod can never shadow the
|
|
392
|
+
// door's dispatch, its WebFinger or its API even where its mount would
|
|
393
|
+
// otherwise contain that path.
|
|
326
394
|
if ((0, claims_1.claims)({ host, pathname }, this.frontHost))
|
|
327
395
|
return;
|
|
328
|
-
|
|
396
|
+
// Claimed from the opt-in roster, never from what is running: a pod resource
|
|
397
|
+
// must not be served by CSS for the seconds before an identity finishes
|
|
398
|
+
// starting, and then stop being served once it has. The path is matched
|
|
399
|
+
// relative to the claim's mount, so a suffix pod's `/aisha/ap/actor` is
|
|
400
|
+
// judged as `/ap/actor`.
|
|
401
|
+
const c = this.resolveClaim(host, pathname);
|
|
402
|
+
if (c && (0, claims_1.agentClaims)({ pathname: this.stripMount(pathname, c.mount), method: request.method }, this.uiPath))
|
|
329
403
|
return;
|
|
330
404
|
throw new Error('not a gateway route'); // reject → CSS's LDP handler takes it
|
|
331
405
|
}
|
|
@@ -340,9 +414,17 @@ class FediPodServerHandler extends community_server_1.HttpHandler {
|
|
|
340
414
|
const cluster = this.args.clusterManager;
|
|
341
415
|
return !cluster || cluster.isSingleThreaded() || cluster.isPrimary();
|
|
342
416
|
}
|
|
343
|
-
/**
|
|
344
|
-
|
|
345
|
-
|
|
417
|
+
/**
|
|
418
|
+
* The identity a request belongs to (matched by host and mount) and the
|
|
419
|
+
* request path relative to that identity's mount, or null. The identity is
|
|
420
|
+
* undefined when the pod is claimed but still starting. Used by the streaming
|
|
421
|
+
* upgrade, which has only the request to go on.
|
|
422
|
+
*/
|
|
423
|
+
matchIdentity(host, pathname = '/') {
|
|
424
|
+
const c = this.resolveClaim(String(host ?? '').toLowerCase(), pathname);
|
|
425
|
+
if (!c)
|
|
426
|
+
return null;
|
|
427
|
+
return { identity: this.surfaces.get(c.podBase), rel: this.stripMount(pathname, c.mount) };
|
|
346
428
|
}
|
|
347
429
|
/**
|
|
348
430
|
* A pod owner, already proven to control podBase, asks this server to run
|
|
@@ -366,13 +448,14 @@ class FediPodServerHandler extends community_server_1.HttpHandler {
|
|
|
366
448
|
return { httpStatus: 201, ok: true, handle, host: new URL(base).host.toLowerCase(),
|
|
367
449
|
doorSecret: door.secret, doorPath: this.uiPath, status: 'rotated' };
|
|
368
450
|
}
|
|
369
|
-
let
|
|
451
|
+
let claim;
|
|
370
452
|
try {
|
|
371
|
-
|
|
453
|
+
claim = this.validateAgentPod(base);
|
|
372
454
|
}
|
|
373
455
|
catch (e) {
|
|
374
456
|
return { httpStatus: 409, error: e.message };
|
|
375
457
|
}
|
|
458
|
+
const { host } = claim;
|
|
376
459
|
try {
|
|
377
460
|
await this.registry.add({ podBase: base, handle, host, webId, optedInAt: new Date().toISOString() });
|
|
378
461
|
}
|
|
@@ -382,7 +465,7 @@ class FediPodServerHandler extends community_server_1.HttpHandler {
|
|
|
382
465
|
}
|
|
383
466
|
// From this instant the pod's identity routes answer 503 instead of LDP,
|
|
384
467
|
// until the agent registers its surface.
|
|
385
|
-
this.
|
|
468
|
+
this.claimed.set(base, claim);
|
|
386
469
|
this.agentHandles.set(handle, base);
|
|
387
470
|
const door = await this.doorSecretFor(base, undefined, { rotate: true });
|
|
388
471
|
this.doorSecrets.set(base, door.secret);
|
|
@@ -397,19 +480,20 @@ class FediPodServerHandler extends community_server_1.HttpHandler {
|
|
|
397
480
|
return { httpStatus: 501, error: 'this server does not offer runtime opt-in' };
|
|
398
481
|
const base = podBase.endsWith('/') ? podBase : `${podBase}/`;
|
|
399
482
|
const host = new URL(base).host.toLowerCase();
|
|
400
|
-
const
|
|
483
|
+
const key = (0, directory_1.agentKey)(host, base);
|
|
484
|
+
const row = await this.registry.get(key);
|
|
401
485
|
if (!row || row.podBase !== base)
|
|
402
486
|
return { httpStatus: 404, error: 'this pod has not opted in' };
|
|
403
|
-
this.
|
|
487
|
+
this.claimed.delete(base); // routes fall to LDP now
|
|
404
488
|
this.startCancelled.add(base); // a pending start stands down
|
|
405
489
|
const identity = this.identities.get(base);
|
|
406
490
|
this.identities.delete(base);
|
|
407
|
-
this.surfaces.delete(
|
|
491
|
+
this.surfaces.delete(base);
|
|
408
492
|
this.agentHandles.delete(row.handle);
|
|
409
493
|
this.doorSecrets.delete(base);
|
|
410
494
|
if (identity)
|
|
411
495
|
await identity.stop();
|
|
412
|
-
await this.registry.remove(
|
|
496
|
+
await this.registry.remove(key);
|
|
413
497
|
this.logger.info(`runtime opt-out: @${row.handle} on ${base} — the pod serves plain LDP again`);
|
|
414
498
|
return { httpStatus: 200, ok: true, stopped: Boolean(identity) };
|
|
415
499
|
}
|
|
@@ -442,8 +526,13 @@ class FediPodServerHandler extends community_server_1.HttpHandler {
|
|
|
442
526
|
}
|
|
443
527
|
async handle({ request, response }) {
|
|
444
528
|
const host = String(request.headers.host ?? '').toLowerCase();
|
|
445
|
-
|
|
446
|
-
|
|
529
|
+
const pathname = new URL(request.url ?? '/', `https://${host}`).pathname;
|
|
530
|
+
// The front's own routes win first — its dispatch, WebFinger and API sit at
|
|
531
|
+
// the apex above every suffix pod — so a claim is consulted only where the
|
|
532
|
+
// front does not answer.
|
|
533
|
+
const claimed = (0, claims_1.claims)({ host, pathname }, this.frontHost) ? null : this.resolveClaim(host, pathname);
|
|
534
|
+
if (claimed) {
|
|
535
|
+
const identity = this.surfaces.get(claimed.podBase);
|
|
447
536
|
if (!identity) {
|
|
448
537
|
// Claimed, but nothing here can answer for it. Saying which of the two
|
|
449
538
|
// reasons it is beats letting the pod answer for a route that is not
|
|
@@ -459,8 +548,10 @@ class FediPodServerHandler extends community_server_1.HttpHandler {
|
|
|
459
548
|
response.end(JSON.stringify({ error: 'this identity is still starting' }));
|
|
460
549
|
return;
|
|
461
550
|
}
|
|
462
|
-
|
|
463
|
-
|
|
551
|
+
// This identity's own inbox path — <mount>/<root>/ap/inbox/, from its
|
|
552
|
+
// actor, so it already carries the mount for a suffix pod.
|
|
553
|
+
const inboxPath = new URL(identity.actorUrl).pathname.replace(/ap\/actor$/u, 'ap/inbox/');
|
|
554
|
+
if (pathname === inboxPath && String(request.method).toUpperCase() === 'POST') {
|
|
464
555
|
await this.deliverAtDoor(identity, request, response);
|
|
465
556
|
return;
|
|
466
557
|
}
|
package/dist/handler.jsonld
CHANGED
|
@@ -249,8 +249,8 @@
|
|
|
249
249
|
"memberFieldName": "logger"
|
|
250
250
|
},
|
|
251
251
|
{
|
|
252
|
-
"@id": "fps:dist/handler.jsonld#
|
|
253
|
-
"memberFieldName": "
|
|
252
|
+
"@id": "fps:dist/handler.jsonld#FediPodServerHandler__member_claimed",
|
|
253
|
+
"memberFieldName": "claimed"
|
|
254
254
|
},
|
|
255
255
|
{
|
|
256
256
|
"@id": "fps:dist/handler.jsonld#FediPodServerHandler__member_agentHandles",
|
|
@@ -301,8 +301,16 @@
|
|
|
301
301
|
"memberFieldName": "constructor"
|
|
302
302
|
},
|
|
303
303
|
{
|
|
304
|
-
"@id": "fps:dist/handler.jsonld#
|
|
305
|
-
"memberFieldName": "
|
|
304
|
+
"@id": "fps:dist/handler.jsonld#FediPodServerHandler__member_validateAgentPod",
|
|
305
|
+
"memberFieldName": "validateAgentPod"
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
"@id": "fps:dist/handler.jsonld#FediPodServerHandler__member_resolveClaim",
|
|
309
|
+
"memberFieldName": "resolveClaim"
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
"@id": "fps:dist/handler.jsonld#FediPodServerHandler__member_stripMount",
|
|
313
|
+
"memberFieldName": "stripMount"
|
|
306
314
|
},
|
|
307
315
|
{
|
|
308
316
|
"@id": "fps:dist/handler.jsonld#FediPodServerHandler__member_initialize",
|
|
@@ -333,8 +341,8 @@
|
|
|
333
341
|
"memberFieldName": "runsIdentities"
|
|
334
342
|
},
|
|
335
343
|
{
|
|
336
|
-
"@id": "fps:dist/handler.jsonld#
|
|
337
|
-
"memberFieldName": "
|
|
344
|
+
"@id": "fps:dist/handler.jsonld#FediPodServerHandler__member_matchIdentity",
|
|
345
|
+
"memberFieldName": "matchIdentity"
|
|
338
346
|
},
|
|
339
347
|
{
|
|
340
348
|
"@id": "fps:dist/handler.jsonld#FediPodServerHandler__member_optInPod",
|
|
@@ -23,12 +23,17 @@ class FediPodStreamingHandler extends community_server_1.WebSocketHandler {
|
|
|
23
23
|
async canHandle({ upgradeRequest }) {
|
|
24
24
|
const host = upgradeRequest.headers.host;
|
|
25
25
|
const { pathname } = new URL(upgradeRequest.url ?? '/', `http://${host}`);
|
|
26
|
-
|
|
26
|
+
// Matched relative to the identity's mount, so a suffix pod's own streaming
|
|
27
|
+
// socket at `/aisha/api/v1/streaming` is recognised as `/api/v1/streaming`.
|
|
28
|
+
const match = this.server.matchIdentity(host, pathname);
|
|
29
|
+
if (!match || !match.rel.startsWith(STREAMING_PATH)) {
|
|
27
30
|
throw new Error('not a FediPod streaming socket');
|
|
28
31
|
}
|
|
29
32
|
}
|
|
30
33
|
async handle({ webSocket, upgradeRequest }) {
|
|
31
|
-
const
|
|
34
|
+
const host = upgradeRequest.headers.host;
|
|
35
|
+
const { pathname } = new URL(upgradeRequest.url ?? '/', `http://${host}`);
|
|
36
|
+
const identity = this.server.matchIdentity(host, pathname)?.identity;
|
|
32
37
|
const streaming = identity?.surface?.streaming;
|
|
33
38
|
if (!streaming) {
|
|
34
39
|
webSocket.close(1011, 'identity not running');
|