@mh-gg/cli 0.1.1-alpha.20260613T085325975Z
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 +5 -0
- package/bin/matterhorn.cjs +57 -0
- package/package.json +49 -0
- package/runtime/bin/appFrontend/artifacts.cjs +25 -0
- package/runtime/bin/appFrontend/buildServers.cjs +176 -0
- package/runtime/bin/appFrontend/commandEnv.cjs +74 -0
- package/runtime/bin/appFrontend/commandPolicy.cjs +23 -0
- package/runtime/bin/appFrontend/devServers.cjs +150 -0
- package/runtime/bin/appFrontend/httpServers.cjs +221 -0
- package/runtime/bin/appFrontend/paths.cjs +103 -0
- package/runtime/bin/appFrontend/ports.cjs +36 -0
- package/runtime/bin/appFrontend/processes.cjs +127 -0
- package/runtime/bin/appFrontend.cjs +45 -0
- package/runtime/bin/appHostCommand.cjs +381 -0
- package/runtime/bin/matterhorn.cjs +501 -0
- package/runtime/bin/matterhornAppLoader.cjs +588 -0
- package/runtime/bin/matterhornApps.cjs +223 -0
- package/runtime/bin/matterhornDeploy.cjs +108 -0
- package/runtime/bin/matterhornEmitAppBundle.cjs +20 -0
- package/runtime/bin/matterhornInstall.cjs +609 -0
- package/runtime/host/callAuth.cjs +76 -0
- package/runtime/host/host.cjs +103 -0
- package/runtime/host/hostAnnouncement.cjs +70 -0
- package/runtime/host/hostClients/constants.cjs +7 -0
- package/runtime/host/hostClients/frontendBundleRefresh.cjs +158 -0
- package/runtime/host/hostClients/frontendRequests.cjs +166 -0
- package/runtime/host/hostClients/index.cjs +68 -0
- package/runtime/host/hostClients/rejections.cjs +37 -0
- package/runtime/host/hostSession.cjs +160 -0
- package/runtime/host/inlineProgressBar.cjs +128 -0
- package/runtime/host/localPeerServer.cjs +114 -0
- package/runtime/host/localRelayClient.cjs +151 -0
- package/runtime/host/matterhornrc.cjs +75 -0
- package/runtime/host/memberRootRegistry.cjs +132 -0
- package/runtime/host/nodePeer.cjs +127 -0
- package/runtime/host/nodePeerRacePatch.cjs +106 -0
- package/runtime/host/peerJsConfig.cjs +26 -0
- package/runtime/host/pushEgress.cjs +48 -0
- package/runtime/host/pushStorage.cjs +233 -0
- package/runtime/host/relay/config.cjs +179 -0
- package/runtime/host/relay/connectionCleanup.cjs +34 -0
- package/runtime/host/relay/connectionDispatcher.cjs +140 -0
- package/runtime/host/relay/matterhornOperationEvents.cjs +100 -0
- package/runtime/host/relay/matterhornRuntimeEventBridge.cjs +182 -0
- package/runtime/host/relay/nostrRelay.cjs +30 -0
- package/runtime/host/relay/peerStartup.cjs +81 -0
- package/runtime/host/relay.cjs +653 -0
- package/runtime/host/relayClientRouting.cjs +1054 -0
- package/runtime/host/relayConfig.cjs +156 -0
- package/runtime/host/relayHostAuth.cjs +39 -0
- package/runtime/host/relayHostMessages.cjs +367 -0
- package/runtime/host/relayHttp.cjs +48 -0
- package/runtime/host/relayIdentity.cjs +496 -0
- package/runtime/host/relayIncomingGate.cjs +153 -0
- package/runtime/host/relayMeshEnvelopes.cjs +522 -0
- package/runtime/host/relayPeerLifecycle.cjs +96 -0
- package/runtime/host/relayPeerSignals.cjs +175 -0
- package/runtime/host/relayRoomRuntimePersistence.cjs +129 -0
- package/runtime/host/relayStatus.cjs +160 -0
- package/runtime/host/sfuRelay.cjs +553 -0
- package/runtime/host/sqliteRelayStorage.cjs +352 -0
- package/runtime/host/wireValidation/client.cjs +213 -0
- package/runtime/host/wireValidation/host.cjs +33 -0
- package/runtime/host/wireValidation/index.cjs +13 -0
- package/runtime/host/wireValidation/peerSignal.cjs +35 -0
- package/runtime/host/wireValidation/presenceEvent.cjs +49 -0
- package/runtime/host/wireValidation/push.cjs +49 -0
- package/runtime/host/wireValidation/relay.cjs +131 -0
- package/runtime/host/wireValidation/shared.cjs +49 -0
- package/runtime/scripts/ensureWorkspaceSdkBuild.cjs +148 -0
- package/runtime/scripts/killChildTree.cjs +18 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
const { sendMessage } = require("@mh-gg/relay-core");
|
|
2
|
+
|
|
3
|
+
function clientPeerSignalMessage(message) {
|
|
4
|
+
const outbound = {
|
|
5
|
+
type: "client/peer-signal",
|
|
6
|
+
protocol: 1,
|
|
7
|
+
roomName: message.roomName,
|
|
8
|
+
targetClientId: message.targetClientId,
|
|
9
|
+
targetPeerId: message.targetPeerId,
|
|
10
|
+
sourceClientId: message.sourceClientId,
|
|
11
|
+
sourcePeerId: message.sourcePeerId,
|
|
12
|
+
signal: message.signal,
|
|
13
|
+
auth: message.auth
|
|
14
|
+
};
|
|
15
|
+
if (message.encryptedSignal !== undefined) outbound.encryptedSignal = message.encryptedSignal;
|
|
16
|
+
return outbound;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function debugPeerSignal(message, details = {}) {
|
|
20
|
+
if (process.env.MATTERHORN_DEBUG !== "1") return;
|
|
21
|
+
console.log(`[relay-peer-signal] ${message} ${JSON.stringify(details)}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function profileTransferDetails(message) {
|
|
25
|
+
const signal = message?.signal;
|
|
26
|
+
if (!signal || (signal.type !== "profile.transfer.chunk" && signal.type !== "profile.transfer.repair-chunk")) return undefined;
|
|
27
|
+
return {
|
|
28
|
+
roomName: message.roomName,
|
|
29
|
+
signalType: signal.type,
|
|
30
|
+
sourceClientId: message.sourceClientId,
|
|
31
|
+
targetClientId: message.targetClientId,
|
|
32
|
+
targetPeerId: message.targetPeerId,
|
|
33
|
+
sessionId: signal.sessionId,
|
|
34
|
+
messageId: signal.messageId,
|
|
35
|
+
payloadKind: signal.payloadKind,
|
|
36
|
+
chunk: `${Number(signal.index) + 1}/${signal.total}`
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function logProfileTransfer(message, phase) {
|
|
41
|
+
const details = profileTransferDetails(message);
|
|
42
|
+
if (!details) return;
|
|
43
|
+
console.log(`[profile-transfer-relay] ${phase} ${JSON.stringify(details)}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function routeUsesRelayConnection(mesh, conn) {
|
|
47
|
+
return Boolean(
|
|
48
|
+
(typeof mesh.hasRelayConnection === "function" && mesh.hasRelayConnection(conn))
|
|
49
|
+
|| (typeof mesh.connectionHasRelayMetadata === "function" && mesh.connectionHasRelayMetadata(conn))
|
|
50
|
+
|| conn?.MatterhornRelayAddress
|
|
51
|
+
|| conn?.MatterhornExpectedRelayAddress
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function createRelayPeerSignalRouter({
|
|
56
|
+
config,
|
|
57
|
+
clientConnections,
|
|
58
|
+
mesh,
|
|
59
|
+
sfu
|
|
60
|
+
}) {
|
|
61
|
+
function sendPeerSignalToRoute(route, message, originConn) {
|
|
62
|
+
if (!route?.conn || route.conn.open === false || route.conn === originConn) return false;
|
|
63
|
+
const routeIsRelay = routeUsesRelayConnection(mesh, route.conn);
|
|
64
|
+
if (routeIsRelay) {
|
|
65
|
+
logProfileTransfer(message, "forward-relay-route");
|
|
66
|
+
debugPeerSignal("forward-relay-route", { roomName: message.roomName, targetClientId: message.targetClientId, peerId: route.peerId });
|
|
67
|
+
sendMessage(route.conn, {
|
|
68
|
+
...message,
|
|
69
|
+
id: message.id || mesh.nextMessageId("relay.peer-signal")
|
|
70
|
+
});
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
if (!routeIsRelay && (clientConnections.get(route.peerId) === route.conn || typeof mesh.hasRelayConnection === "function")) {
|
|
74
|
+
logProfileTransfer(message, "deliver-client-route");
|
|
75
|
+
debugPeerSignal("deliver-client-route", { roomName: message.roomName, targetClientId: message.targetClientId, peerId: route.peerId });
|
|
76
|
+
sendMessage(route.conn, clientPeerSignalMessage(message));
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
logProfileTransfer(message, "forward-route");
|
|
80
|
+
debugPeerSignal("forward-route", { roomName: message.roomName, targetClientId: message.targetClientId, peerId: route.peerId, routeIsRelay });
|
|
81
|
+
sendMessage(route.conn, {
|
|
82
|
+
...message,
|
|
83
|
+
id: message.id || mesh.nextMessageId("relay.peer-signal")
|
|
84
|
+
});
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function sendPeerSignalToClient(message, originConn) {
|
|
89
|
+
if (typeof message.targetPeerId === "string") {
|
|
90
|
+
const directClient = clientConnections.get(message.targetPeerId);
|
|
91
|
+
if (directClient && directClient.open !== false && directClient !== originConn) {
|
|
92
|
+
logProfileTransfer(message, "deliver-target-peer");
|
|
93
|
+
debugPeerSignal("deliver-target-peer", { roomName: message.roomName, targetClientId: message.targetClientId, targetPeerId: message.targetPeerId });
|
|
94
|
+
sendMessage(directClient, clientPeerSignalMessage(message));
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const latestRoute = mesh.latestClientRoute(message.roomName, message.targetClientId);
|
|
100
|
+
if (sendPeerSignalToRoute(latestRoute, message, originConn)) return true;
|
|
101
|
+
|
|
102
|
+
if (typeof message.targetPeerId === "string") {
|
|
103
|
+
const relayConn = mesh.remoteClientRoute(message.targetPeerId);
|
|
104
|
+
if (relayConn && relayConn !== originConn && relayConn.open !== false) {
|
|
105
|
+
logProfileTransfer(message, "forward-remote-peer");
|
|
106
|
+
sendMessage(relayConn, {
|
|
107
|
+
...message,
|
|
108
|
+
id: message.id || mesh.nextMessageId("relay.peer-signal")
|
|
109
|
+
});
|
|
110
|
+
debugPeerSignal("forward-remote-peer", { roomName: message.roomName, targetClientId: message.targetClientId, targetPeerId: message.targetPeerId });
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
if (mesh.sendPeerSignalToDiscoveredRoute(message.targetPeerId, message, originConn)) return true;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function routePeerSignalMessage(conn, message) {
|
|
120
|
+
const roomName = typeof message.roomName === "string" ? message.roomName : undefined;
|
|
121
|
+
const sourceClientId = conn.MatterhornClientId || (typeof message.clientId === "string" ? message.clientId : undefined);
|
|
122
|
+
const targetClientId = typeof message.targetClientId === "string" ? message.targetClientId : undefined;
|
|
123
|
+
const targetPeerId = typeof message.targetPeerId === "string" ? message.targetPeerId : undefined;
|
|
124
|
+
if (!roomName || !sourceClientId || (!targetClientId && !targetPeerId)) return;
|
|
125
|
+
|
|
126
|
+
const sourcePeerId = mesh.scopedPeerId(conn);
|
|
127
|
+
clientConnections.set(sourcePeerId, conn);
|
|
128
|
+
mesh.rememberClientRoute(conn, roomName, sourceClientId, sourcePeerId);
|
|
129
|
+
if (targetClientId === `sfu:${config.sfuPeerId}`) {
|
|
130
|
+
if (message.encryptedSignal) return;
|
|
131
|
+
if (sfu.handleSignal({
|
|
132
|
+
roomName,
|
|
133
|
+
sourceClientId,
|
|
134
|
+
signal: message.signal,
|
|
135
|
+
auth: message.auth
|
|
136
|
+
})) return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const envelope = {
|
|
140
|
+
type: "relay.peer-signal",
|
|
141
|
+
roomName,
|
|
142
|
+
sourceClientId,
|
|
143
|
+
sourcePeerId,
|
|
144
|
+
targetClientId,
|
|
145
|
+
targetPeerId,
|
|
146
|
+
signal: message.signal,
|
|
147
|
+
auth: message.auth
|
|
148
|
+
};
|
|
149
|
+
if (message.encryptedSignal !== undefined) envelope.encryptedSignal = message.encryptedSignal;
|
|
150
|
+
|
|
151
|
+
if (sendPeerSignalToClient(envelope, conn)) return;
|
|
152
|
+
const sent = mesh.sendEnvelope(envelope, conn);
|
|
153
|
+
logProfileTransfer(envelope, `broadcast-envelope:${sent}`);
|
|
154
|
+
debugPeerSignal("broadcast-envelope", { roomName, sourceClientId, targetClientId, targetPeerId, sent });
|
|
155
|
+
if (sent > 0 || conn.open === false) return;
|
|
156
|
+
logProfileTransfer(envelope, "peer-unavailable");
|
|
157
|
+
sendMessage(conn, {
|
|
158
|
+
type: "client/peer-signal.error",
|
|
159
|
+
protocol: 1,
|
|
160
|
+
roomName,
|
|
161
|
+
code: "peer-unavailable",
|
|
162
|
+
message: "The target peer is not connected."
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
routePeerSignalMessage,
|
|
168
|
+
sendPeerSignalToClient
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
module.exports = {
|
|
173
|
+
clientPeerSignalMessage,
|
|
174
|
+
createRelayPeerSignalRouter
|
|
175
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
const fs = require("node:fs");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
|
|
4
|
+
function clone(value) {
|
|
5
|
+
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function runtimeDirectory(storagePath) {
|
|
9
|
+
return path.join(storagePath, "room-runtime");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function roomRuntimeFile(storagePath, roomName) {
|
|
13
|
+
if (typeof roomName !== "string" || !roomName) throw new Error("roomName is required");
|
|
14
|
+
return path.join(runtimeDirectory(storagePath), `${encodeURIComponent(roomName)}.json`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function readJsonFile(filePath) {
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
20
|
+
} catch (error) {
|
|
21
|
+
if (error?.code === "ENOENT") return undefined;
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function writeJsonFileAtomic(filePath, value) {
|
|
27
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
28
|
+
const tmpPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
29
|
+
fs.writeFileSync(tmpPath, `${JSON.stringify(value, null, 2)}\n`);
|
|
30
|
+
fs.renameSync(tmpPath, filePath);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function loadRelayRoomRuntimeSnapshot(storagePath, roomName) {
|
|
34
|
+
if (!storagePath || !roomName) return undefined;
|
|
35
|
+
const record = readJsonFile(roomRuntimeFile(storagePath, roomName));
|
|
36
|
+
if (!record || record.schemaVersion !== 1 || record.roomName !== roomName) return undefined;
|
|
37
|
+
return {
|
|
38
|
+
schemaVersion: 1,
|
|
39
|
+
roomName,
|
|
40
|
+
savedAt: Number.isFinite(record.savedAt) ? record.savedAt : 0,
|
|
41
|
+
state: clone(record.state),
|
|
42
|
+
operations: Array.isArray(record.operations) ? record.operations.map(clone) : []
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function createRelayRoomRuntimePersistence({ storagePath, roomName, initialState = null, initialOperations = [] } = {}) {
|
|
47
|
+
if (!storagePath) throw new Error("storagePath is required");
|
|
48
|
+
if (!roomName) throw new Error("roomName is required");
|
|
49
|
+
|
|
50
|
+
const filePath = roomRuntimeFile(storagePath, roomName);
|
|
51
|
+
let state = clone(initialState);
|
|
52
|
+
let operations = Array.isArray(initialOperations) ? initialOperations.map(clone) : [];
|
|
53
|
+
|
|
54
|
+
function persist() {
|
|
55
|
+
writeJsonFileAtomic(filePath, {
|
|
56
|
+
schemaVersion: 1,
|
|
57
|
+
roomName,
|
|
58
|
+
savedAt: Date.now(),
|
|
59
|
+
state: clone(state),
|
|
60
|
+
operations: operations.map(clone)
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const store = {
|
|
65
|
+
async load() {
|
|
66
|
+
return clone(state);
|
|
67
|
+
},
|
|
68
|
+
async save(nextState) {
|
|
69
|
+
state = clone(nextState);
|
|
70
|
+
persist();
|
|
71
|
+
},
|
|
72
|
+
async commitStateAndOperation({ operation, nextState, expectedPreviousVersion }) {
|
|
73
|
+
if (operation?.id && operations.some((entry) => entry?.id === operation.id)) throw new Error(`Duplicate operation id ${operation.id}`);
|
|
74
|
+
if (Number.isInteger(expectedPreviousVersion)) {
|
|
75
|
+
const currentVersion = Number.isInteger(state?.version) ? state.version : 0;
|
|
76
|
+
if (currentVersion !== expectedPreviousVersion) throw new Error(`Expected previous room version ${expectedPreviousVersion}, found ${currentVersion}`);
|
|
77
|
+
if (nextState?.version !== expectedPreviousVersion + 1) throw new Error("Next room state version must advance by one");
|
|
78
|
+
}
|
|
79
|
+
state = clone(nextState);
|
|
80
|
+
operations.push(clone(operation));
|
|
81
|
+
persist();
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const operationLog = {
|
|
86
|
+
entries: operations,
|
|
87
|
+
async append(operation) {
|
|
88
|
+
if (operation?.id && operations.some((entry) => entry?.id === operation.id)) throw new Error(`Duplicate operation id ${operation.id}`);
|
|
89
|
+
operations.push(clone(operation));
|
|
90
|
+
persist();
|
|
91
|
+
},
|
|
92
|
+
async removeById(operationId) {
|
|
93
|
+
const index = operations.findIndex((entry) => entry?.id === operationId);
|
|
94
|
+
if (index < 0) return false;
|
|
95
|
+
operations.splice(index, 1);
|
|
96
|
+
persist();
|
|
97
|
+
return true;
|
|
98
|
+
},
|
|
99
|
+
async list() {
|
|
100
|
+
return operations.map(clone);
|
|
101
|
+
},
|
|
102
|
+
async findById(operationId) {
|
|
103
|
+
const found = operations.find((entry) => entry?.id === operationId || entry?.clientOperationId === operationId);
|
|
104
|
+
return found ? clone(found) : null;
|
|
105
|
+
},
|
|
106
|
+
async persist() {
|
|
107
|
+
persist();
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
filePath,
|
|
113
|
+
store,
|
|
114
|
+
operationLog,
|
|
115
|
+
persist,
|
|
116
|
+
snapshot() {
|
|
117
|
+
return {
|
|
118
|
+
state: clone(state),
|
|
119
|
+
operations: operations.map(clone)
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
module.exports = {
|
|
126
|
+
createRelayRoomRuntimePersistence,
|
|
127
|
+
loadRelayRoomRuntimeSnapshot,
|
|
128
|
+
roomRuntimeFile
|
|
129
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
function relayPeerStatus(peer, peerId, startPeer) {
|
|
2
|
+
return {
|
|
3
|
+
peerId,
|
|
4
|
+
state: !startPeer ? "disabled" : peer?.open ? "online" : "offline"
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function relayStorageStatus(config, storage, relayStats) {
|
|
9
|
+
const name = storage?.constructor?.name;
|
|
10
|
+
return {
|
|
11
|
+
backend: name === "SqliteRelayStorage" ? "sqlite" : name ? "custom" : "memory",
|
|
12
|
+
ok: true,
|
|
13
|
+
events: relayStats.events,
|
|
14
|
+
quotas: {
|
|
15
|
+
maxEvents: config.maxEvents,
|
|
16
|
+
maxEventsPerRoom: config.maxEventsPerRoom,
|
|
17
|
+
maxBytesPerRoom: config.maxBytesPerRoom,
|
|
18
|
+
maxEventsPerPubkeyWindow: config.maxEventsPerPubkeyWindow,
|
|
19
|
+
pubkeyQuotaWindowSeconds: config.pubkeyQuotaWindowSeconds
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function relaySfuOperationalStatus(config, sfuStats) {
|
|
25
|
+
return {
|
|
26
|
+
state: !config.sfuEnabled ? "disabled" : sfuStats.open ? "available" : "offline",
|
|
27
|
+
enabled: sfuStats.enabled,
|
|
28
|
+
participants: sfuStats.participants,
|
|
29
|
+
maxParticipants: sfuStats.maxParticipants
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function relayClaims(config, includeKnownRelays = true) {
|
|
34
|
+
const claims = [];
|
|
35
|
+
if (config.relayClaim) claims.push(config.relayClaim);
|
|
36
|
+
if (includeKnownRelays && typeof config.relayTrustStore?.claims === "function") {
|
|
37
|
+
claims.push(...config.relayTrustStore.claims());
|
|
38
|
+
}
|
|
39
|
+
return claims;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function createRelayStatus(deps) {
|
|
43
|
+
const {
|
|
44
|
+
config,
|
|
45
|
+
startedAt,
|
|
46
|
+
storage,
|
|
47
|
+
mesh,
|
|
48
|
+
relay,
|
|
49
|
+
sfu,
|
|
50
|
+
peerReconnects,
|
|
51
|
+
clientConnections,
|
|
52
|
+
hostConnections,
|
|
53
|
+
roomPeer,
|
|
54
|
+
relayPeer
|
|
55
|
+
} = deps;
|
|
56
|
+
|
|
57
|
+
function relayLoad() {
|
|
58
|
+
const meshStats = mesh.stats();
|
|
59
|
+
return {
|
|
60
|
+
clients: clientConnections.size,
|
|
61
|
+
roomHosts: hostConnections.size,
|
|
62
|
+
relayConnections: meshStats.relayConnections,
|
|
63
|
+
knownRelays: meshStats.knownRelays,
|
|
64
|
+
icedRelays: meshStats.icedRelays,
|
|
65
|
+
activeRelayFanout: meshStats.activeRelayFanout,
|
|
66
|
+
relayHealth: meshStats.relayHealth || [],
|
|
67
|
+
sfu: sfu.stats(),
|
|
68
|
+
peerReconnects: peerReconnects.summary(),
|
|
69
|
+
discovery: meshStats.discovery
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function operationalStatus() {
|
|
74
|
+
const meshStats = mesh.stats();
|
|
75
|
+
const relayStats = relay.stats();
|
|
76
|
+
return {
|
|
77
|
+
uptimeSeconds: Math.floor((Date.now() - startedAt) / 1000),
|
|
78
|
+
roomPeer: relayPeerStatus(roomPeer(), config.relayPeerId, config.startPeer),
|
|
79
|
+
meshPeer: relayPeerStatus(relayPeer(), config.relayMeshPeerId, config.startPeer),
|
|
80
|
+
host: {
|
|
81
|
+
state: hostConnections.size > 0 ? "online" : "offline",
|
|
82
|
+
registered: hostConnections.size
|
|
83
|
+
},
|
|
84
|
+
clients: {
|
|
85
|
+
connected: clientConnections.size
|
|
86
|
+
},
|
|
87
|
+
relays: {
|
|
88
|
+
connected: meshStats.relayConnections,
|
|
89
|
+
known: meshStats.knownRelays,
|
|
90
|
+
iced: meshStats.icedRelays,
|
|
91
|
+
health: meshStats.relayHealth || []
|
|
92
|
+
},
|
|
93
|
+
storage: relayStorageStatus(config, storage, relayStats),
|
|
94
|
+
sfu: relaySfuOperationalStatus(config, sfu.stats())
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function relayStatusMessage(requestId, options = {}) {
|
|
99
|
+
const includeKnownRelays = options.includeKnownRelays !== false;
|
|
100
|
+
return {
|
|
101
|
+
type: "relay.status.ok",
|
|
102
|
+
requestId,
|
|
103
|
+
relayAddress: config.relayAddress,
|
|
104
|
+
roomPeerId: config.relayPeerId,
|
|
105
|
+
relayMeshPeerId: config.relayMeshPeerId,
|
|
106
|
+
relayClaim: config.relayClaim,
|
|
107
|
+
relayClaims: relayClaims(config, includeKnownRelays),
|
|
108
|
+
relayHints: includeKnownRelays ? mesh.relayHints() : [config.relayAddress],
|
|
109
|
+
icedRelayHints: includeKnownRelays ? mesh.icedRelayHints() : [],
|
|
110
|
+
relayHealth: includeKnownRelays ? mesh.relayHealth() : mesh.relayHealth().filter((item) => item.local),
|
|
111
|
+
sfu: sfu.status(),
|
|
112
|
+
health: operationalStatus(),
|
|
113
|
+
load: relayLoad()
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function healthDocument() {
|
|
118
|
+
const status = operationalStatus();
|
|
119
|
+
return {
|
|
120
|
+
ok: true,
|
|
121
|
+
protocol: "matterhorn-peer-relay",
|
|
122
|
+
version: 1,
|
|
123
|
+
uptimeSeconds: status.uptimeSeconds,
|
|
124
|
+
relayPeerId: config.relayPeerId,
|
|
125
|
+
relayMeshPeerId: config.relayMeshPeerId,
|
|
126
|
+
relayAddress: config.relayAddress,
|
|
127
|
+
relayClaim: config.relayClaim,
|
|
128
|
+
relayClaims: relayClaims(config),
|
|
129
|
+
peerJsSignaling: config.peerJsSignaling,
|
|
130
|
+
roomPeerOpen: !config.startPeer || Boolean(roomPeer()?.open),
|
|
131
|
+
relayMeshPeerOpen: !config.startPeer || Boolean(relayPeer()?.open),
|
|
132
|
+
roomHosts: hostConnections.size,
|
|
133
|
+
clients: clientConnections.size,
|
|
134
|
+
knownRelays: mesh.stats().knownRelays,
|
|
135
|
+
icedRelays: mesh.stats().icedRelays,
|
|
136
|
+
relayHints: mesh.relayHints(),
|
|
137
|
+
icedRelayHints: mesh.icedRelayHints(),
|
|
138
|
+
relayHealth: mesh.relayHealth(),
|
|
139
|
+
sfu: sfu.status(),
|
|
140
|
+
relayLoad: relayLoad(),
|
|
141
|
+
status,
|
|
142
|
+
...relay.stats()
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
healthDocument,
|
|
148
|
+
operationalStatus,
|
|
149
|
+
relayLoad,
|
|
150
|
+
relayStatusMessage
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = {
|
|
155
|
+
createRelayStatus,
|
|
156
|
+
relayPeerStatus,
|
|
157
|
+
relayClaims,
|
|
158
|
+
relaySfuOperationalStatus,
|
|
159
|
+
relayStorageStatus
|
|
160
|
+
};
|