@routr/connect 2.17.0 → 2.19.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/dist/envs.d.ts +1 -0
- package/dist/envs.js +21 -1
- package/dist/handlers/register.js +4 -3
- package/dist/router.js +11 -4
- package/dist/service.js +4 -0
- package/dist/utils.d.ts +19 -0
- package/dist/utils.js +23 -1
- package/package.json +5 -5
package/dist/envs.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export declare const API_ADDR: string;
|
|
|
4
4
|
export declare const CONNECT_VERIFIER_ADDR: string;
|
|
5
5
|
export declare const CONNECT_VERIFIER_PUBLIC_KEY_PATH: string;
|
|
6
6
|
export declare const CONNECT_VERIFIER_OPTIONS: any;
|
|
7
|
+
export declare const REGISTER_DEFAULT_EXPIRES: number;
|
|
7
8
|
export declare const DEFAULT_UNTRUSTED_HEADERS_TO_STRIP: string[];
|
|
8
9
|
export declare const UNTRUSTED_HEADERS_TO_STRIP: string[];
|
|
9
10
|
export declare const TRUSTED_EDGEPORTS: string[];
|
package/dist/envs.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var _a;
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.TRUSTED_EDGEPORT_ADDRESSES = exports.TRUSTED_EDGEPORTS = exports.UNTRUSTED_HEADERS_TO_STRIP = exports.DEFAULT_UNTRUSTED_HEADERS_TO_STRIP = exports.CONNECT_VERIFIER_OPTIONS = exports.CONNECT_VERIFIER_PUBLIC_KEY_PATH = exports.CONNECT_VERIFIER_ADDR = exports.API_ADDR = exports.LOCATION_ADDR = exports.BIND_ADDR = void 0;
|
|
4
|
+
exports.TRUSTED_EDGEPORT_ADDRESSES = exports.TRUSTED_EDGEPORTS = exports.UNTRUSTED_HEADERS_TO_STRIP = exports.DEFAULT_UNTRUSTED_HEADERS_TO_STRIP = exports.REGISTER_DEFAULT_EXPIRES = exports.CONNECT_VERIFIER_OPTIONS = exports.CONNECT_VERIFIER_PUBLIC_KEY_PATH = exports.CONNECT_VERIFIER_ADDR = exports.API_ADDR = exports.LOCATION_ADDR = exports.BIND_ADDR = void 0;
|
|
5
5
|
/*
|
|
6
6
|
* Copyright (C) 2026 by Fonoster Inc (https://fonoster.com)
|
|
7
7
|
* http://github.com/fonoster/routr
|
|
@@ -22,6 +22,22 @@ exports.TRUSTED_EDGEPORT_ADDRESSES = exports.TRUSTED_EDGEPORTS = exports.UNTRUST
|
|
|
22
22
|
*/
|
|
23
23
|
const assertions_1 = require("./assertions");
|
|
24
24
|
(0, assertions_1.assertOnlyOneEnvIsSet)(["CONNECT_VERIFIER_ADDR", "CONNECT_VERIFIER_PUBLIC_KEY"]);
|
|
25
|
+
/**
|
|
26
|
+
* Parses an optional environment variable that must hold a positive integer.
|
|
27
|
+
*
|
|
28
|
+
* @param {string} name - The variable name, used in the error message
|
|
29
|
+
* @param {string} value - The raw value
|
|
30
|
+
* @return {number | undefined} the parsed value, or undefined when unset
|
|
31
|
+
*/
|
|
32
|
+
function parsePositiveInt(name, value) {
|
|
33
|
+
if (value === undefined || value.trim() === "")
|
|
34
|
+
return undefined;
|
|
35
|
+
const parsed = Number(value);
|
|
36
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
37
|
+
throw new Error(`${name} must be a positive integer, got "${value}"`);
|
|
38
|
+
}
|
|
39
|
+
return parsed;
|
|
40
|
+
}
|
|
25
41
|
exports.BIND_ADDR = (_a = process.env.BIND_ADDR) !== null && _a !== void 0 ? _a : "0.0.0.0:51904";
|
|
26
42
|
exports.LOCATION_ADDR = process.env.LOCATION_ADDR;
|
|
27
43
|
exports.API_ADDR = process.env.API_ADDR;
|
|
@@ -30,6 +46,10 @@ exports.CONNECT_VERIFIER_PUBLIC_KEY_PATH = process.env.CONNECT_VERIFIER_PUBLIC_K
|
|
|
30
46
|
exports.CONNECT_VERIFIER_OPTIONS = process.env.CONNECT_VERIFIER_OPTIONS
|
|
31
47
|
? JSON.parse(process.env.CONNECT_VERIFIER_OPTIONS)
|
|
32
48
|
: { expiresIn: "1h", algorithm: ["RS256"] };
|
|
49
|
+
// Expiry, in seconds, granted to a REGISTER that carries neither a Contact
|
|
50
|
+
// expires parameter nor an Expires header (RFC 3261 section 10.3, step 7).
|
|
51
|
+
// Unset leaves the processor's default in place.
|
|
52
|
+
exports.REGISTER_DEFAULT_EXPIRES = parsePositiveInt("REGISTER_DEFAULT_EXPIRES", process.env.REGISTER_DEFAULT_EXPIRES);
|
|
33
53
|
// Platform headers a caller must never be able to set. They are removed from
|
|
34
54
|
// requests that come from untrusted sources (e.g., the PSTN or a client using a
|
|
35
55
|
// Connect Token) before Routr adds its own. Override with a comma-separated list;
|
|
@@ -66,6 +66,7 @@ const location_1 = require("@routr/location");
|
|
|
66
66
|
const processor_1 = require("@routr/processor");
|
|
67
67
|
const common_1 = require("@routr/common");
|
|
68
68
|
const utils_1 = require("../utils");
|
|
69
|
+
const envs_1 = require("../envs");
|
|
69
70
|
const logger_1 = require("@fonoster/logger");
|
|
70
71
|
const logger = (0, logger_1.getLogger)({ service: "connect", filePath: __filename });
|
|
71
72
|
const jwtVerifier = (0, utils_1.getVerifierImpl)();
|
|
@@ -93,10 +94,10 @@ const handleRegister = (apiClient, location) => {
|
|
|
93
94
|
try {
|
|
94
95
|
yield location.addRoute({
|
|
95
96
|
aor: "aor" in peerOrAgent ? peerOrAgent.aor : processor_1.Target.getTargetAOR(request),
|
|
96
|
-
route: location_1.Helper.createRoute(request),
|
|
97
|
+
route: location_1.Helper.createRoute(request, envs_1.REGISTER_DEFAULT_EXPIRES),
|
|
97
98
|
maxContacts: peerOrAgent.maxContacts
|
|
98
99
|
});
|
|
99
|
-
res.sendRegisterOk(request);
|
|
100
|
+
res.sendRegisterOk(request, undefined, (0, utils_1.recordingMetadata)(peerOrAgent, (0, utils_1.domainOf)(peerOrAgent)));
|
|
100
101
|
}
|
|
101
102
|
catch (e) {
|
|
102
103
|
if (e.code === grpc.status.INVALID_ARGUMENT) {
|
|
@@ -118,7 +119,7 @@ const handleRegister = (apiClient, location) => {
|
|
|
118
119
|
}
|
|
119
120
|
yield location.addRoute({
|
|
120
121
|
aor: payload.aor,
|
|
121
|
-
route: location_1.Helper.createRoute(request),
|
|
122
|
+
route: location_1.Helper.createRoute(request, envs_1.REGISTER_DEFAULT_EXPIRES),
|
|
122
123
|
maxContacts: (_a = payload.maxContacts) !== null && _a !== void 0 ? _a : -1
|
|
123
124
|
});
|
|
124
125
|
res.sendRegisterOk(request);
|
package/dist/router.js
CHANGED
|
@@ -121,7 +121,11 @@ function router(location, apiClient) {
|
|
|
121
121
|
const result = (direction, route, extended) => route
|
|
122
122
|
? {
|
|
123
123
|
direction,
|
|
124
|
-
route: Object.assign(Object.assign({}, route), {
|
|
124
|
+
route: Object.assign(Object.assign({}, route), {
|
|
125
|
+
// Resource metadata links the call to an account; the recording
|
|
126
|
+
// flag comes from any resource the call touches that asks for
|
|
127
|
+
// it; trace.* keys annotate the hop in the recording
|
|
128
|
+
metadata: Object.assign(Object.assign(Object.assign(Object.assign({}, extended), (0, utils_1.recordingMetadata)(caller, (0, utils_1.domainOf)(caller), callee, (0, utils_1.domainOf)(callee))), common_1.Trace.traceAnnotations({ direction })), route.metadata) })
|
|
125
129
|
}
|
|
126
130
|
: {
|
|
127
131
|
direction,
|
|
@@ -174,7 +178,7 @@ function agentToAgent(location, req) {
|
|
|
174
178
|
*/
|
|
175
179
|
function fromPSTN(apiClient, location, callee, req) {
|
|
176
180
|
return __awaiter(this, void 0, void 0, function* () {
|
|
177
|
-
var _a;
|
|
181
|
+
var _a, _b;
|
|
178
182
|
const sessionAffinityRef = processor_1.Extensions.getHeaderValue(req, callee.sessionAffinityHeader);
|
|
179
183
|
let backend;
|
|
180
184
|
const peer = (yield apiClient.peers.findBy({
|
|
@@ -198,7 +202,8 @@ function fromPSTN(apiClient, location, callee, req) {
|
|
|
198
202
|
}
|
|
199
203
|
if (!route.headers)
|
|
200
204
|
route.headers = [];
|
|
201
|
-
(_a = callee.
|
|
205
|
+
route.metadata = Object.assign(Object.assign(Object.assign({}, route.metadata), (0, utils_1.recordingMetadata)(callee.trunk)), common_1.Trace.traceAnnotations({ trunk: (_a = callee.trunk) === null || _a === void 0 ? void 0 : _a.ref }));
|
|
206
|
+
(_b = callee.extraHeaders) === null || _b === void 0 ? void 0 : _b.forEach((prop) => {
|
|
202
207
|
const p = {
|
|
203
208
|
name: prop.name,
|
|
204
209
|
value: prop.value,
|
|
@@ -242,6 +247,7 @@ function agentToPSTN(req, agent, calleeNumber) {
|
|
|
242
247
|
edgePortRef: req.edgePortRef,
|
|
243
248
|
listeningPoints: req.listeningPoints,
|
|
244
249
|
localnets: req.localnets,
|
|
250
|
+
metadata: Object.assign(Object.assign({}, (0, utils_1.recordingMetadata)(trunk)), common_1.Trace.traceAnnotations({ trunk: trunk.ref })),
|
|
245
251
|
externalAddrs: req.externalAddrs,
|
|
246
252
|
headers: [
|
|
247
253
|
// TODO: Find a more deterministic way to re-add the Privacy header
|
|
@@ -280,7 +286,7 @@ function agentToPeer(location, callee, req) {
|
|
|
280
286
|
// eslint-disable-next-line require-jsdoc
|
|
281
287
|
function peerToPSTN(apiClient, req) {
|
|
282
288
|
return __awaiter(this, void 0, void 0, function* () {
|
|
283
|
-
var _a;
|
|
289
|
+
var _a, _b;
|
|
284
290
|
const numberTel = processor_1.Extensions.getHeaderValue(req, common_1.CommonTypes.ExtraHeader.DOD_NUMBER);
|
|
285
291
|
const privacy = processor_1.Extensions.getHeaderValue(req, common_1.CommonTypes.ExtraHeader.DOD_PRIVACY);
|
|
286
292
|
if (!numberTel) {
|
|
@@ -316,6 +322,7 @@ function peerToPSTN(apiClient, req) {
|
|
|
316
322
|
edgePortRef: req.edgePortRef,
|
|
317
323
|
listeningPoints: req.listeningPoints,
|
|
318
324
|
localnets: req.localnets,
|
|
325
|
+
metadata: Object.assign(Object.assign({}, (0, utils_1.recordingMetadata)(number.trunk)), common_1.Trace.traceAnnotations({ trunk: (_b = number.trunk) === null || _b === void 0 ? void 0 : _b.ref })),
|
|
319
326
|
externalAddrs: req.externalAddrs,
|
|
320
327
|
headers: [
|
|
321
328
|
// TODO: Find a more deterministic way to re-add the Privacy header
|
package/dist/service.js
CHANGED
|
@@ -101,11 +101,15 @@ const createDispatcher = (deps) => (req, res) => __awaiter(void 0, void 0, void
|
|
|
101
101
|
(0, utils_1.isInDialog)(req)) {
|
|
102
102
|
return forwardInDialog(req, res);
|
|
103
103
|
}
|
|
104
|
+
// Out-of-dialog REFER (RFC 3515 allows it) would be routed as a new call below;
|
|
105
|
+
// it is refused so transfer only happens on a dialog this proxy already
|
|
106
|
+
// authorized. SIP Connect 1.1 section 12.1 keeps REFER out of scope anyway.
|
|
104
107
|
switch (req.method) {
|
|
105
108
|
case common_1.Method.PUBLISH:
|
|
106
109
|
case common_1.Method.NOTIFY:
|
|
107
110
|
case common_1.Method.SUBSCRIBE:
|
|
108
111
|
case common_1.Method.MESSAGE:
|
|
112
|
+
case common_1.Method.REFER:
|
|
109
113
|
res.sendMethodNotAllowed();
|
|
110
114
|
break;
|
|
111
115
|
case common_1.Method.REGISTER:
|
package/dist/utils.d.ts
CHANGED
|
@@ -74,3 +74,22 @@ export declare const getSourceAddress: (request: MessageRequest) => string | und
|
|
|
74
74
|
* @return {boolean} true if the request comes from a trusted peer EdgePort
|
|
75
75
|
*/
|
|
76
76
|
export declare const isFromTrustedEdgePort: (request: MessageRequest, trust: EdgePortTrust) => boolean;
|
|
77
|
+
type Traceable = Pick<CC.Agent, "ref" | "trace"> | undefined | null;
|
|
78
|
+
/**
|
|
79
|
+
* Metadata that asks the dispatcher to record the call when any of the
|
|
80
|
+
* resources it touches (Trunk, Agent, Peer, Domain) has `trace.enabled`.
|
|
81
|
+
* The value is the ref of the first one that asked. The dispatcher removes
|
|
82
|
+
* the key before the message leaves.
|
|
83
|
+
*
|
|
84
|
+
* @param {Traceable[]} resources - Resources the call touches, in order of precedence
|
|
85
|
+
* @return {Record<string, string>} The metadata to merge into the route
|
|
86
|
+
*/
|
|
87
|
+
export declare const recordingMetadata: (...resources: Traceable[]) => Record<string, string>;
|
|
88
|
+
/**
|
|
89
|
+
* The domain a resource belongs to, when it carries one (Agents do).
|
|
90
|
+
*
|
|
91
|
+
* @param {object} resource - A caller or callee resource
|
|
92
|
+
* @return {CC.Domain | undefined} Its domain
|
|
93
|
+
*/
|
|
94
|
+
export declare const domainOf: (resource: unknown) => CC.Domain | undefined;
|
|
95
|
+
export {};
|
package/dist/utils.js
CHANGED
|
@@ -45,7 +45,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
45
45
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
46
|
};
|
|
47
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
-
exports.isFromTrustedEdgePort = exports.getSourceAddress = exports.getUntrustedHeadersToStrip = exports.getVerifierImpl = exports.hasXConnectObjectHeader = exports.isInDialog = exports.getSipUri = exports.getTrunkURI = exports.createTrunkAuthentication = exports.createRemotePartyId = exports.createPAssertedIdentity = exports.getRoutingDirection = exports.findResource = exports.findNumberByTelUrl = exports.findDomain = exports.isKind = void 0;
|
|
48
|
+
exports.domainOf = exports.recordingMetadata = exports.isFromTrustedEdgePort = exports.getSourceAddress = exports.getUntrustedHeadersToStrip = exports.getVerifierImpl = exports.hasXConnectObjectHeader = exports.isInDialog = exports.getSipUri = exports.getTrunkURI = exports.createTrunkAuthentication = exports.createRemotePartyId = exports.createPAssertedIdentity = exports.getRoutingDirection = exports.findResource = exports.findNumberByTelUrl = exports.findDomain = exports.isKind = void 0;
|
|
49
49
|
/*
|
|
50
50
|
* Copyright (C) 2026 by Fonoster Inc (https://fonoster.com)
|
|
51
51
|
* http://github.com/fonoster/routr
|
|
@@ -306,3 +306,25 @@ const isFromTrustedEdgePort = (request, trust) => {
|
|
|
306
306
|
});
|
|
307
307
|
};
|
|
308
308
|
exports.isFromTrustedEdgePort = isFromTrustedEdgePort;
|
|
309
|
+
/**
|
|
310
|
+
* Metadata that asks the dispatcher to record the call when any of the
|
|
311
|
+
* resources it touches (Trunk, Agent, Peer, Domain) has `trace.enabled`.
|
|
312
|
+
* The value is the ref of the first one that asked. The dispatcher removes
|
|
313
|
+
* the key before the message leaves.
|
|
314
|
+
*
|
|
315
|
+
* @param {Traceable[]} resources - Resources the call touches, in order of precedence
|
|
316
|
+
* @return {Record<string, string>} The metadata to merge into the route
|
|
317
|
+
*/
|
|
318
|
+
const recordingMetadata = (...resources) => {
|
|
319
|
+
const asked = resources.find((r) => { var _a; return ((_a = r === null || r === void 0 ? void 0 : r.trace) === null || _a === void 0 ? void 0 : _a.enabled) === true; });
|
|
320
|
+
return asked ? { [common_1.Trace.TRACE_FLAG_METADATA_KEY]: asked.ref } : {};
|
|
321
|
+
};
|
|
322
|
+
exports.recordingMetadata = recordingMetadata;
|
|
323
|
+
/**
|
|
324
|
+
* The domain a resource belongs to, when it carries one (Agents do).
|
|
325
|
+
*
|
|
326
|
+
* @param {object} resource - A caller or callee resource
|
|
327
|
+
* @return {CC.Domain | undefined} Its domain
|
|
328
|
+
*/
|
|
329
|
+
const domainOf = (resource) => resource === null || resource === void 0 ? void 0 : resource.domain;
|
|
330
|
+
exports.domainOf = domainOf;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@routr/connect",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.19.0",
|
|
4
4
|
"description": "Default processor",
|
|
5
5
|
"author": "Pedro Sanders <psanders@fonoster.com>",
|
|
6
6
|
"homepage": "https://github.com/fonoster/routr#readme",
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@fonoster/logger": "0.6.0",
|
|
23
|
-
"@routr/common": "^2.
|
|
24
|
-
"@routr/location": "^2.
|
|
25
|
-
"@routr/processor": "^2.
|
|
23
|
+
"@routr/common": "^2.19.0",
|
|
24
|
+
"@routr/location": "^2.19.0",
|
|
25
|
+
"@routr/processor": "^2.19.0",
|
|
26
26
|
"jsonwebtoken": "^9.0.3"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
@@ -41,5 +41,5 @@
|
|
|
41
41
|
"bugs": {
|
|
42
42
|
"url": "https://github.com/fonoster/routr/issues"
|
|
43
43
|
},
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "357116800e150d8bbfbd0ad2406689c5793675b1"
|
|
45
45
|
}
|