@serve.zone/dcrouter 32.0.0 → 32.0.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.
- package/.smartconfig.json +0 -1
- package/deno.json +1 -1
- package/dist_ts/00_commitinfo_data.js +3 -3
- package/dist_ts/opsserver/handlers/gatewayclient.handler.d.ts +0 -1
- package/dist_ts/opsserver/handlers/gatewayclient.handler.js +13 -34
- package/dist_ts/opsserver/helpers/gateway-client-route-key.d.ts +26 -0
- package/dist_ts/opsserver/helpers/gateway-client-route-key.js +51 -0
- package/dist_ts_migrations/index.d.ts +1 -1
- package/dist_ts_migrations/index.js +75 -3
- package/dist_ts_web/00_commitinfo_data.js +3 -3
- package/package.json +1 -1
- package/readme.md +9 -0
- package/ts/00_commitinfo_data.ts +3 -3
- package/ts/opsserver/handlers/gatewayclient.handler.ts +19 -36
- package/ts/opsserver/helpers/gateway-client-route-key.ts +64 -0
- package/ts_web/00_commitinfo_data.ts +3 -3
|
@@ -11,6 +11,12 @@ import {
|
|
|
11
11
|
normalizeGatewayTargetHost,
|
|
12
12
|
requireGatewayMachineAuth,
|
|
13
13
|
} from '../helpers/gateway-client-auth.js';
|
|
14
|
+
import {
|
|
15
|
+
buildGatewayClientHostRouteKey,
|
|
16
|
+
buildGatewayClientRouteName,
|
|
17
|
+
parseGatewayClientHostRouteKey,
|
|
18
|
+
} from '../helpers/gateway-client-route-key.js';
|
|
19
|
+
import { logger } from '../../logger.js';
|
|
14
20
|
|
|
15
21
|
type TResolvedGatewayClientOwnership = Required<Pick<plugins.servezoneInterfaces.data.IGatewayClientOwnership,
|
|
16
22
|
'gatewayClientType' | 'gatewayClientId' | 'appId'
|
|
@@ -1171,15 +1177,22 @@ export class GatewayClientHandler {
|
|
|
1171
1177
|
const routeKey = metadata.externalKey?.startsWith(prefix)
|
|
1172
1178
|
? metadata.externalKey.slice(prefix.length)
|
|
1173
1179
|
: '';
|
|
1174
|
-
const combinedOwnership =
|
|
1180
|
+
const combinedOwnership = parseGatewayClientHostRouteKey(routeKey);
|
|
1175
1181
|
if (combinedOwnership) {
|
|
1176
1182
|
ownership.hostname = combinedOwnership.hostname;
|
|
1177
1183
|
ownership.routeRef = combinedOwnership.routeRef;
|
|
1178
|
-
} else if (routeKey.startsWith('v2:host-route:')) {
|
|
1179
|
-
// A malformed versioned key must not be exposed as a hostname.
|
|
1180
|
-
ownership.hostname = this.getRouteHostnames(routeArg.route)[0];
|
|
1181
1184
|
} else if (routeKey.startsWith('route:')) {
|
|
1182
1185
|
ownership.routeRef = routeKey.slice('route:'.length);
|
|
1186
|
+
} else if (routeKey.includes(':')) {
|
|
1187
|
+
// A hostname carries no colon, so a colon-bearing key that parses as neither of the two
|
|
1188
|
+
// other forms states ownership this router cannot prove. Report what the route actually
|
|
1189
|
+
// serves rather than the key, and say so instead of dropping the key silently.
|
|
1190
|
+
logger.log(
|
|
1191
|
+
'warn',
|
|
1192
|
+
`Gateway client route ${routeArg.id} carries a malformed route key '${routeKey}'; listing it with its route hostname only.`,
|
|
1193
|
+
{ routeId: routeArg.id },
|
|
1194
|
+
);
|
|
1195
|
+
ownership.hostname = this.getRouteHostnames(routeArg.route)[0];
|
|
1183
1196
|
} else if (routeKey) {
|
|
1184
1197
|
ownership.hostname = routeKey;
|
|
1185
1198
|
} else {
|
|
@@ -1659,8 +1672,7 @@ export class GatewayClientHandler {
|
|
|
1659
1672
|
const routeRef = ownership.routeRef?.trim();
|
|
1660
1673
|
let routeKey: string;
|
|
1661
1674
|
if (hostname && routeRef) {
|
|
1662
|
-
|
|
1663
|
-
routeKey = `v2:host-route:${payload}`;
|
|
1675
|
+
routeKey = buildGatewayClientHostRouteKey(hostname, routeRef);
|
|
1664
1676
|
} else if (hostname) {
|
|
1665
1677
|
routeKey = hostname;
|
|
1666
1678
|
} else {
|
|
@@ -1674,35 +1686,6 @@ export class GatewayClientHandler {
|
|
|
1674
1686
|
].map((part) => part.trim()).join(':');
|
|
1675
1687
|
}
|
|
1676
1688
|
|
|
1677
|
-
private parseCombinedGatewayClientRouteKey(
|
|
1678
|
-
routeKeyArg: string,
|
|
1679
|
-
): { hostname: string; routeRef: string } | undefined {
|
|
1680
|
-
const prefix = 'v2:host-route:';
|
|
1681
|
-
if (!routeKeyArg.startsWith(prefix)) {
|
|
1682
|
-
return undefined;
|
|
1683
|
-
}
|
|
1684
|
-
const payload = routeKeyArg.slice(prefix.length);
|
|
1685
|
-
if (!payload || !/^[A-Za-z0-9_-]+$/.test(payload)) {
|
|
1686
|
-
return undefined;
|
|
1687
|
-
}
|
|
1688
|
-
try {
|
|
1689
|
-
const parsed = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
|
|
1690
|
-
if (
|
|
1691
|
-
!Array.isArray(parsed)
|
|
1692
|
-
|| parsed.length !== 2
|
|
1693
|
-
|| parsed.some((value) => typeof value !== 'string' || !value.trim())
|
|
1694
|
-
) {
|
|
1695
|
-
return undefined;
|
|
1696
|
-
}
|
|
1697
|
-
return {
|
|
1698
|
-
hostname: parsed[0].trim().toLowerCase(),
|
|
1699
|
-
routeRef: parsed[1].trim(),
|
|
1700
|
-
};
|
|
1701
|
-
} catch {
|
|
1702
|
-
return undefined;
|
|
1703
|
-
}
|
|
1704
|
-
}
|
|
1705
|
-
|
|
1706
1689
|
private validateGatewayRouteConfig(
|
|
1707
1690
|
routeArg: unknown,
|
|
1708
1691
|
): plugins.servezoneInterfaces.data.IGatewayRouteConfig {
|
|
@@ -1882,7 +1865,7 @@ export class GatewayClientHandler {
|
|
|
1882
1865
|
} as any;
|
|
1883
1866
|
}
|
|
1884
1867
|
if (!normalizedRoute.name) {
|
|
1885
|
-
normalizedRoute.name =
|
|
1868
|
+
normalizedRoute.name = buildGatewayClientRouteName(externalKey);
|
|
1886
1869
|
}
|
|
1887
1870
|
return normalizedRoute;
|
|
1888
1871
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The route-key half of a gateway-client route's `externalKey`, owned here so the composer,
|
|
3
|
+
* the parser, the generated route name and the migration that rewrote stored keys all read
|
|
4
|
+
* one spelling.
|
|
5
|
+
*
|
|
6
|
+
* A stored key is `<gatewayClientType>:<gatewayClientId>:<appId>:<routeKey>`, and the route key
|
|
7
|
+
* is one of three forms: a bare hostname, `route:<routeRef>` when the client owns a route
|
|
8
|
+
* reference without a hostname, and the combined form below when it owns one hostname under a
|
|
9
|
+
* specific route reference. The combined payload is base64url so the whole key stays one opaque
|
|
10
|
+
* segment, and neither other form can be mistaken for it: a hostname carries no colon, and the
|
|
11
|
+
* route-reference form has a different prefix.
|
|
12
|
+
*/
|
|
13
|
+
export const gatewayClientHostRouteKeyPrefix = 'host-route:';
|
|
14
|
+
|
|
15
|
+
export interface IGatewayClientHostRouteKey {
|
|
16
|
+
hostname: string;
|
|
17
|
+
routeRef: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function buildGatewayClientHostRouteKey(
|
|
21
|
+
hostnameArg: string,
|
|
22
|
+
routeRefArg: string,
|
|
23
|
+
): string {
|
|
24
|
+
const payload = Buffer.from(JSON.stringify([hostnameArg, routeRefArg]), 'utf8').toString('base64url');
|
|
25
|
+
return `${gatewayClientHostRouteKeyPrefix}${payload}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Read a combined route key back. Returns undefined for every key that is not one — including a
|
|
30
|
+
* key that carries the prefix but no decodable payload, so a malformed key can never be served
|
|
31
|
+
* as ownership it does not state.
|
|
32
|
+
*/
|
|
33
|
+
export function parseGatewayClientHostRouteKey(
|
|
34
|
+
routeKeyArg: string,
|
|
35
|
+
): IGatewayClientHostRouteKey | undefined {
|
|
36
|
+
if (!routeKeyArg.startsWith(gatewayClientHostRouteKeyPrefix)) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
const payload = routeKeyArg.slice(gatewayClientHostRouteKeyPrefix.length);
|
|
40
|
+
if (!payload || !/^[A-Za-z0-9_-]+$/.test(payload)) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const parsed = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
|
|
45
|
+
if (
|
|
46
|
+
!Array.isArray(parsed)
|
|
47
|
+
|| parsed.length !== 2
|
|
48
|
+
|| parsed.some((value) => typeof value !== 'string' || !value.trim())
|
|
49
|
+
) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
hostname: parsed[0].trim().toLowerCase(),
|
|
54
|
+
routeRef: parsed[1].trim(),
|
|
55
|
+
};
|
|
56
|
+
} catch {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The route name generated for a gateway-client route whose producer sent none. */
|
|
62
|
+
export function buildGatewayClientRouteName(externalKeyArg: string): string {
|
|
63
|
+
return `gateway-client-${externalKeyArg.replace(/[^a-zA-Z0-9-]+/g, '-').slice(0, 80)}`;
|
|
64
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* autocreated commitinfo by @push.rocks/commitinfo
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
|
-
name:
|
|
6
|
-
version:
|
|
7
|
-
description:
|
|
5
|
+
name: '@serve.zone/dcrouter',
|
|
6
|
+
version: '32.0.1',
|
|
7
|
+
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
|
8
8
|
}
|