@push.rocks/smartproxy 27.20.0 → 27.22.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/changelog.md +45 -0
- package/dist_rust/rustproxy_linux_amd64 +0 -0
- package/dist_rust/rustproxy_linux_amd64.tsrust-build.json +14 -0
- package/dist_rust/rustproxy_linux_arm64 +0 -0
- package/dist_rust/rustproxy_linux_arm64.tsrust-build.json +14 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +15 -3
- package/dist_ts/proxies/smart-proxy/models/metrics-types.d.ts +2 -0
- package/dist_ts/proxies/smart-proxy/models/rust-types.d.ts +1 -0
- package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.d.ts +5 -1
- package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.js +37 -10
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +2 -0
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +5 -1
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +14 -0
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +51 -11
- package/package.json +7 -7
- package/readme.md +7 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/proxies/smart-proxy/models/interfaces.ts +15 -3
- package/ts/proxies/smart-proxy/models/metrics-types.ts +2 -0
- package/ts/proxies/smart-proxy/models/rust-types.ts +1 -0
- package/ts/proxies/smart-proxy/rust-metrics-adapter.ts +30 -8
- package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +5 -0
- package/ts/proxies/smart-proxy/smart-proxy.ts +56 -12
- package/readme.hints.md +0 -561
- package/readme.metrics.md +0 -484
- package/readme.plan.md +0 -0
|
@@ -25,6 +25,10 @@ import type { IRustCertificateStatus, IRustChallengeOptions, IRustProxyOptions,
|
|
|
25
25
|
|
|
26
26
|
type TChallengeProvider = plugins.smartchallenge.IChallengeProvider;
|
|
27
27
|
|
|
28
|
+
/** What one certProvisionFunction call amounted to, in the order each sweep logs the tally. */
|
|
29
|
+
const certProvisionOutcomes = ['loaded', 'http01', 'deferred', 'failed', 'stopped', 'unrecognized'] as const;
|
|
30
|
+
type TCertProvisionOutcome = (typeof certProvisionOutcomes)[number];
|
|
31
|
+
|
|
28
32
|
/**
|
|
29
33
|
* SmartProxy - Rust-backed proxy engine with TypeScript configuration API.
|
|
30
34
|
*
|
|
@@ -319,7 +323,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
319
323
|
}
|
|
320
324
|
|
|
321
325
|
// Stop metrics polling
|
|
322
|
-
this.metricsAdapter.stopPolling();
|
|
326
|
+
await this.metricsAdapter.stopPolling();
|
|
323
327
|
|
|
324
328
|
// Remove exit listener before killing to avoid spurious error events
|
|
325
329
|
this.bridge.removeAllListeners('exit');
|
|
@@ -330,7 +334,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
330
334
|
} catch {
|
|
331
335
|
// Ignore if already stopped
|
|
332
336
|
}
|
|
333
|
-
this.bridge.
|
|
337
|
+
await this.bridge.terminate();
|
|
334
338
|
|
|
335
339
|
// Stop socket handler relay
|
|
336
340
|
if (this.socketHandlerServer) {
|
|
@@ -489,6 +493,24 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
489
493
|
return this.bridge.getCertificateStatus(routeName);
|
|
490
494
|
}
|
|
491
495
|
|
|
496
|
+
/**
|
|
497
|
+
* Lift the provisioning failure cooldown of one domain, so the next sweep
|
|
498
|
+
* (`updateRoutes()` or the renewal check) calls `certProvisionFunction` for it
|
|
499
|
+
* again instead of waiting out `certProvisionFailureCooldownMs`. Use it when the
|
|
500
|
+
* cause of a failure was resolved outside SmartProxy, for example after a
|
|
501
|
+
* certificate for the domain was issued out of band.
|
|
502
|
+
*
|
|
503
|
+
* @param domain The provisioning domain exactly as `certProvisionFunction`
|
|
504
|
+
* receives it and `certificate-failed` reports it.
|
|
505
|
+
* @returns true if the domain was cooling down, false if it was not (never
|
|
506
|
+
* failed, or its cooldown had already elapsed).
|
|
507
|
+
*/
|
|
508
|
+
public invalidateCertProvisionCooldown(domain: string): boolean {
|
|
509
|
+
const wasCoolingDown = this.isInFailureCooldown(domain, Date.now());
|
|
510
|
+
this.certFailureAt.delete(domain);
|
|
511
|
+
return wasCoolingDown;
|
|
512
|
+
}
|
|
513
|
+
|
|
492
514
|
/**
|
|
493
515
|
* Get the metrics interface.
|
|
494
516
|
*/
|
|
@@ -582,7 +604,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
582
604
|
// --- Private helpers ---
|
|
583
605
|
|
|
584
606
|
private async cleanupRuntimeResourcesAfterStartFailure(didSpawn: boolean): Promise<void> {
|
|
585
|
-
this.metricsAdapter.stopPolling();
|
|
607
|
+
await this.metricsAdapter.stopPolling();
|
|
586
608
|
|
|
587
609
|
if (this.nftablesManager) {
|
|
588
610
|
await this.nftablesManager.cleanup().catch((err) => {
|
|
@@ -616,7 +638,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
616
638
|
if (didSpawn) {
|
|
617
639
|
this.bridge.removeAllListeners('exit');
|
|
618
640
|
await this.bridge.stopProxy().catch(() => undefined);
|
|
619
|
-
this.bridge.
|
|
641
|
+
await this.bridge.terminate();
|
|
620
642
|
}
|
|
621
643
|
}
|
|
622
644
|
|
|
@@ -985,6 +1007,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
985
1007
|
* For routes with certificate: 'auto', call certProvisionFunction if set.
|
|
986
1008
|
* If the callback returns a cert object, load it into Rust.
|
|
987
1009
|
* If it returns 'http01', let Rust handle ACME.
|
|
1010
|
+
* If it returns 'defer', leave the domain for the next sweep.
|
|
988
1011
|
*/
|
|
989
1012
|
private async provisionCertificatesViaCallback(skipDomains: Set<string> = new Set()): Promise<void> {
|
|
990
1013
|
const provisionFn = this.settings.certProvisionFunction;
|
|
@@ -1045,16 +1068,28 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
1045
1068
|
const concurrency = this.settings.certProvisionConcurrency ?? 4;
|
|
1046
1069
|
const semaphore = new ConcurrencySemaphore(concurrency);
|
|
1047
1070
|
|
|
1048
|
-
const promises = tasks.map(async ({ domain, route }) => {
|
|
1071
|
+
const promises = tasks.map(async ({ domain, route }): Promise<TCertProvisionOutcome> => {
|
|
1049
1072
|
await semaphore.acquire();
|
|
1050
1073
|
try {
|
|
1051
|
-
await this.provisionSingleDomain(domain, route, provisionFn);
|
|
1074
|
+
return await this.provisionSingleDomain(domain, route, provisionFn);
|
|
1052
1075
|
} finally {
|
|
1053
1076
|
semaphore.release();
|
|
1054
1077
|
}
|
|
1055
1078
|
});
|
|
1056
1079
|
|
|
1057
|
-
|
|
1080
|
+
// provisionSingleDomain settles provisioning errors itself; it rejects only
|
|
1081
|
+
// when a certificate-failed listener throws, after the failure was recorded.
|
|
1082
|
+
const settled = await Promise.allSettled(promises);
|
|
1083
|
+
const tally = new Map<TCertProvisionOutcome, number>();
|
|
1084
|
+
for (const result of settled) {
|
|
1085
|
+
const outcome = result.status === 'fulfilled' ? result.value : 'failed';
|
|
1086
|
+
tally.set(outcome, (tally.get(outcome) ?? 0) + 1);
|
|
1087
|
+
}
|
|
1088
|
+
const summary = certProvisionOutcomes
|
|
1089
|
+
.filter((outcome) => tally.has(outcome))
|
|
1090
|
+
.map((outcome) => `${tally.get(outcome)} ${outcome}`)
|
|
1091
|
+
.join(', ');
|
|
1092
|
+
logger.log('info', `Cert provisioning sweep finished: ${summary}`, { component: 'smart-proxy' });
|
|
1058
1093
|
}
|
|
1059
1094
|
|
|
1060
1095
|
/**
|
|
@@ -1065,8 +1100,8 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
1065
1100
|
domain: string,
|
|
1066
1101
|
route: IRouteConfig,
|
|
1067
1102
|
provisionFn: (domain: string, eventComms: ICertProvisionEventComms) => Promise<TSmartProxyCertProvisionObject>,
|
|
1068
|
-
): Promise<
|
|
1069
|
-
if (this.stopping) return;
|
|
1103
|
+
): Promise<TCertProvisionOutcome> {
|
|
1104
|
+
if (this.stopping) return 'stopped';
|
|
1070
1105
|
|
|
1071
1106
|
let expiryDate: string | undefined;
|
|
1072
1107
|
let source = 'certProvisionFunction';
|
|
@@ -1088,7 +1123,13 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
1088
1123
|
`Certificate provisioning timed out for ${domain} after ${timeoutMs}ms`,
|
|
1089
1124
|
);
|
|
1090
1125
|
|
|
1091
|
-
if (this.stopping) return;
|
|
1126
|
+
if (this.stopping) return 'stopped';
|
|
1127
|
+
|
|
1128
|
+
// A deferral is not a failure: no cooldown, no event, no ACME fallback.
|
|
1129
|
+
if (result === 'defer') {
|
|
1130
|
+
logger.log('info', `Certificate provisioning deferred for ${domain}; the next sweep retries it`, { component: 'smart-proxy' });
|
|
1131
|
+
return 'deferred';
|
|
1132
|
+
}
|
|
1092
1133
|
|
|
1093
1134
|
if (result === 'http01') {
|
|
1094
1135
|
if (route.name) {
|
|
@@ -1100,11 +1141,11 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
1100
1141
|
'Note: Rust ACME is disabled when certProvisionFunction is set.', { component: 'smart-proxy' });
|
|
1101
1142
|
}
|
|
1102
1143
|
}
|
|
1103
|
-
return;
|
|
1144
|
+
return 'http01';
|
|
1104
1145
|
}
|
|
1105
1146
|
|
|
1106
1147
|
if (result && typeof result === 'object') {
|
|
1107
|
-
if (this.stopping) return;
|
|
1148
|
+
if (this.stopping) return 'stopped';
|
|
1108
1149
|
|
|
1109
1150
|
const certObj = result as ISmartProxyCertProvisionCertificate;
|
|
1110
1151
|
await this.bridge.loadCertificate(
|
|
@@ -1135,7 +1176,9 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
1135
1176
|
expiryDate: expiryDate || (certObj.validUntil ? new Date(certObj.validUntil).toISOString() : undefined),
|
|
1136
1177
|
source,
|
|
1137
1178
|
} satisfies ICertificateIssuedEvent);
|
|
1179
|
+
return 'loaded';
|
|
1138
1180
|
}
|
|
1181
|
+
return 'unrecognized';
|
|
1139
1182
|
} catch (err: any) {
|
|
1140
1183
|
logger.log('warn', `certProvisionFunction failed for ${domain}: ${err.message}`, { component: 'smart-proxy' });
|
|
1141
1184
|
|
|
@@ -1161,6 +1204,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
1161
1204
|
: ' — default self-signed fallback cert will be used'), { component: 'smart-proxy' });
|
|
1162
1205
|
}
|
|
1163
1206
|
}
|
|
1207
|
+
return 'failed';
|
|
1164
1208
|
}
|
|
1165
1209
|
}
|
|
1166
1210
|
|