@nodefony/http 10.0.0-beta.3 → 10.0.0-beta.5
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/_virtual/{_@oxc-project_runtime@0.153.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorate.js +1 -2
- package/dist/_virtual/{_@oxc-project_runtime@0.153.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorateParam.js +1 -2
- package/dist/index.js +13 -10
- package/dist/nodefony/command/assetsPublishCommand.js +5 -3
- package/dist/nodefony/command/certificatesCommand.js +5 -3
- package/dist/nodefony/command/networkCommand.js +5 -3
- package/dist/nodefony/command/proxyGenerateCommand.js +8 -3
- package/dist/nodefony/config/config.js +3 -3
- package/dist/nodefony/config/defineModuleConfig.js +1 -2
- package/dist/nodefony/interfaces/IRequestTrace.js +1 -0
- package/dist/nodefony/service/HttpAdminApi.js +2 -2
- package/dist/nodefony/service/ProfilerAdminApi.js +13 -3
- package/dist/nodefony/service/audit-logger.js +23 -30
- package/dist/nodefony/service/certificates.js +97 -13
- package/dist/nodefony/service/error-renderer.js +29 -12
- package/dist/nodefony/service/http-kernel.js +198 -50
- package/dist/nodefony/service/pretty-request-logger.js +15 -13
- package/dist/nodefony/service/request-logger.js +24 -16
- package/dist/nodefony/service/reverse-proxy.js +3 -3
- package/dist/nodefony/service/servers/clientError.js +22 -3
- package/dist/nodefony/service/servers/server-h2c.js +126 -0
- package/dist/nodefony/service/servers/server-http.js +5 -8
- package/dist/nodefony/service/servers/server-https.js +12 -24
- package/dist/nodefony/service/servers/server-static.js +3 -3
- package/dist/nodefony/service/servers/server-websocket-secure.js +10 -18
- package/dist/nodefony/service/servers/server-websocket.js +10 -18
- package/dist/nodefony/service/servers/serverShutdown.js +78 -18
- package/dist/nodefony/service/servers/wsHeartbeat.js +1 -2
- package/dist/nodefony/service/sessions/sessions-service.js +7 -14
- package/dist/nodefony/service/trace.js +1 -2
- package/dist/nodefony/service/upload/upload-service.js +5 -8
- package/dist/nodefony/service/x509.js +1 -2
- package/dist/nodefony/src/assets/collectAssets.js +1 -2
- package/dist/nodefony/src/assets/prebuiltUi.js +1 -2
- package/dist/nodefony/src/context/Context.js +78 -43
- package/dist/nodefony/src/context/domainMatcher.js +1 -2
- package/dist/nodefony/src/context/forwarded.js +1 -2
- package/dist/nodefony/src/context/http/HttpContext.js +28 -6
- package/dist/nodefony/src/context/http/Request.js +33 -29
- package/dist/nodefony/src/context/http/Response.js +23 -6
- package/dist/nodefony/src/context/http/SseStream.js +1 -2
- package/dist/nodefony/src/context/http/parser.js +36 -10
- package/dist/nodefony/src/context/http/urlFastPath.js +1 -2
- package/dist/nodefony/src/context/http2/Request.js +4 -8
- package/dist/nodefony/src/context/http2/Response.js +41 -9
- package/dist/nodefony/src/context/metaData.js +1 -2
- package/dist/nodefony/src/context/requestId.js +1 -2
- package/dist/nodefony/src/context/requestTrace.js +104 -0
- package/dist/nodefony/src/context/responseEnded.js +1 -2
- package/dist/nodefony/src/context/timingMode.js +21 -0
- package/dist/nodefony/src/context/transport.js +45 -0
- package/dist/nodefony/src/context/trustProxy.js +1 -2
- package/dist/nodefony/src/context/unsentResponse.js +30 -0
- package/dist/nodefony/src/context/websocket/Response.js +33 -6
- package/dist/nodefony/src/context/websocket/WebsocketContext.js +104 -25
- package/dist/nodefony/src/context/websocket/isWebsocketContext.js +17 -0
- package/dist/nodefony/src/context/websocket/websocketUrl.js +1 -2
- package/dist/nodefony/src/context/websocket/wsBackpressure.js +1 -2
- package/dist/nodefony/src/context/websocket/wsCloseReason.js +1 -2
- package/dist/nodefony/src/context/websocket/wsLogContent.js +1 -2
- package/dist/nodefony/src/cookies/cookie.js +3 -7
- package/dist/nodefony/src/errors/httpError.js +1 -2
- package/dist/nodefony/src/errors/thrownMessage.js +1 -2
- package/dist/nodefony/src/profiler/FrameProfile.js +23 -2
- package/dist/nodefony/src/profiler/Profiler.js +104 -32
- package/dist/nodefony/src/profiler/aggregate.js +442 -0
- package/dist/nodefony/src/proxy/forward.js +29 -5
- package/dist/nodefony/src/proxy/generateProxyConfig.js +14 -6
- package/dist/nodefony/src/proxy/rules.js +12 -6
- package/dist/nodefony/src/rateLimit/MemoryRateLimitStore.js +1 -2
- package/dist/nodefony/src/rateLimit/WsConnectionCounter.js +1 -2
- package/dist/nodefony/src/rateLimit/rateLimitFilters.js +1 -2
- package/dist/nodefony/src/servers/http2Limits.js +50 -0
- package/dist/nodefony/src/servers/kernelServers.js +12 -3
- package/dist/nodefony/src/servers/portBinder.js +41 -10
- package/dist/nodefony/src/servers/upgradeDispatch.js +86 -7
- package/dist/nodefony/src/session/session.js +2 -3
- package/dist/nodefony/src/session/sessionStoreFailure.js +1 -2
- package/dist/nodefony/src/session/storage/MemorySessionStorage.js +1 -2
- package/dist/nodefony/src/session/storage/RevocationGuardStorage.js +1 -2
- package/dist/nodefony/src/session/storage/sessionFilters.js +1 -2
- package/dist/nodefony/src/session/storage/sessionSort.js +1 -2
- package/dist/types/index.d.ts +9 -3
- package/dist/types/nodefony/config/config.d.ts +4 -0
- package/dist/types/nodefony/interfaces/IContext.d.ts +72 -4
- package/dist/types/nodefony/interfaces/IRequest.d.ts +9 -4
- package/dist/types/nodefony/interfaces/IRequestTrace.d.ts +56 -0
- package/dist/types/nodefony/interfaces/IReverseProxy.d.ts +8 -0
- package/dist/types/nodefony/interfaces/IRouting.d.ts +26 -0
- package/dist/types/nodefony/interfaces/index.d.ts +3 -2
- package/dist/types/nodefony/service/ProfilerAdminApi.d.ts +3 -0
- package/dist/types/nodefony/service/audit-logger.d.ts +3 -11
- package/dist/types/nodefony/service/certificates.d.ts +55 -4
- package/dist/types/nodefony/service/error-renderer.d.ts +31 -0
- package/dist/types/nodefony/service/http-kernel.d.ts +96 -10
- package/dist/types/nodefony/service/servers/clientError.d.ts +16 -0
- package/dist/types/nodefony/service/servers/server-h2c.d.ts +51 -0
- package/dist/types/nodefony/service/servers/server-http.d.ts +3 -3
- package/dist/types/nodefony/service/servers/server-https.d.ts +4 -4
- package/dist/types/nodefony/service/servers/server-websocket-secure.d.ts +1 -3
- package/dist/types/nodefony/service/servers/server-websocket.d.ts +1 -3
- package/dist/types/nodefony/service/servers/serverShutdown.d.ts +30 -16
- package/dist/types/nodefony/src/context/Context.d.ts +38 -5
- package/dist/types/nodefony/src/context/http/HttpContext.d.ts +16 -2
- package/dist/types/nodefony/src/context/http/Request.d.ts +20 -15
- package/dist/types/nodefony/src/context/http/Response.d.ts +11 -0
- package/dist/types/nodefony/src/context/http/parser.d.ts +18 -2
- package/dist/types/nodefony/src/context/http2/Request.d.ts +1 -1
- package/dist/types/nodefony/src/context/http2/Response.d.ts +27 -5
- package/dist/types/nodefony/src/context/requestTrace.d.ts +64 -0
- package/dist/types/nodefony/src/context/timingMode.d.ts +23 -0
- package/dist/types/nodefony/src/context/transport.d.ts +53 -0
- package/dist/types/nodefony/src/context/unsentResponse.d.ts +16 -0
- package/dist/types/nodefony/src/context/websocket/Response.d.ts +18 -2
- package/dist/types/nodefony/src/context/websocket/WebsocketContext.d.ts +58 -8
- package/dist/types/nodefony/src/context/websocket/isWebsocketContext.d.ts +15 -0
- package/dist/types/nodefony/src/profiler/FrameProfile.d.ts +28 -12
- package/dist/types/nodefony/src/profiler/Profiler.d.ts +79 -111
- package/dist/types/nodefony/src/profiler/aggregate.d.ts +132 -0
- package/dist/types/nodefony/src/proxy/forward.d.ts +14 -2
- package/dist/types/nodefony/src/proxy/generateProxyConfig.d.ts +16 -0
- package/dist/types/nodefony/src/proxy/rules.d.ts +3 -0
- package/dist/types/nodefony/src/servers/http2Limits.d.ts +37 -0
- package/dist/types/nodefony/src/servers/kernelServers.d.ts +10 -1
- package/dist/types/nodefony/src/servers/portBinder.d.ts +29 -5
- package/dist/types/nodefony/src/servers/upgradeDispatch.d.ts +33 -3
- package/docs/cookies.md +85 -78
- package/docs/diagnostics-channel.md +303 -0
- package/docs/index.md +6 -1
- package/docs/observabilite.md +159 -81
- package/docs/rate-limit.md +83 -83
- package/docs/reverse-proxy.md +72 -70
- package/docs/servers.md +338 -224
- package/docs/session.md +179 -179
- package/docs/sse.md +24 -20
- package/docs/upload.md +120 -117
- package/package.json +8 -9
- package/dist/_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorateMetadata.js +0 -6
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/decorate.js
|
|
2
2
|
function __decorate(decorators, target, key, desc) {
|
|
3
3
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
4
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
5
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
6
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
7
|
}
|
|
8
|
-
//#endregion
|
|
9
8
|
export { __decorate as default };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/decorateParam.js
|
|
2
2
|
function __decorateParam(paramIndex, decorator) {
|
|
3
3
|
return function(target, key) {
|
|
4
4
|
decorator(target, key, paramIndex);
|
|
5
5
|
};
|
|
6
6
|
}
|
|
7
|
-
//#endregion
|
|
8
7
|
export { __decorateParam as default };
|
package/dist/index.js
CHANGED
|
@@ -2,11 +2,13 @@ import { normalizePrefix, parseProxyOrigin, proxyMountProblems } from "./nodefon
|
|
|
2
2
|
import config, { httpConfigSchema, httpOverlaySchema } from "./nodefony/config/config.js";
|
|
3
3
|
import { defineHttpConfig, httpConfigJsonSchema } from "./nodefony/config/defineModuleConfig.js";
|
|
4
4
|
import { createHttpAdminApi } from "./nodefony/service/HttpAdminApi.js";
|
|
5
|
+
import { ACTION_SELF_PHASE, NO_ROUTE, N_PLUS_ONE_MIN_REPEATS, TOOLING_URL_PREFIX, aggregateProfiles, createProfileAggregator, isFailedProfile, sqlShape } from "./nodefony/src/profiler/aggregate.js";
|
|
5
6
|
import { createProfilerAdminApi } from "./nodefony/service/ProfilerAdminApi.js";
|
|
6
7
|
import { Profiler } from "./nodefony/src/profiler/Profiler.js";
|
|
7
8
|
import HttpError from "./nodefony/src/errors/httpError.js";
|
|
8
9
|
import { MemoryRateLimitStore } from "./nodefony/src/rateLimit/MemoryRateLimitStore.js";
|
|
9
10
|
import { compileDomainPattern, compileDomainPatterns, compileTrustedHosts, isDomainAllowed } from "./nodefony/src/context/domainMatcher.js";
|
|
11
|
+
import { REQUEST_TRACE_CHANNEL } from "./nodefony/src/context/requestTrace.js";
|
|
10
12
|
import Cookie from "./nodefony/src/cookies/cookie.js";
|
|
11
13
|
import Context from "./nodefony/src/context/Context.js";
|
|
12
14
|
import { decideSend, readBackpressureOptions } from "./nodefony/src/context/websocket/wsBackpressure.js";
|
|
@@ -19,15 +21,15 @@ import Http2Request from "./nodefony/src/context/http2/Request.js";
|
|
|
19
21
|
import Http2Response from "./nodefony/src/context/http2/Response.js";
|
|
20
22
|
import { SSE_HEARTBEAT_MS, SseStream, acceptsEventStream, openSseStream } from "./nodefony/src/context/http/SseStream.js";
|
|
21
23
|
import HttpContext from "./nodefony/src/context/http/HttpContext.js";
|
|
22
|
-
import DefaultErrorRenderer, { schemaMismatchOf } from "./nodefony/service/error-renderer.js";
|
|
23
|
-
import DefaultRequestLogger from "./nodefony/service/request-logger.js";
|
|
24
|
+
import DefaultErrorRenderer, { schemaMismatchOf, toValidationFields } from "./nodefony/service/error-renderer.js";
|
|
24
25
|
import JsonAuditLogger from "./nodefony/service/audit-logger.js";
|
|
26
|
+
import DefaultRequestLogger from "./nodefony/service/request-logger.js";
|
|
25
27
|
import PrettyRequestLogger from "./nodefony/service/pretty-request-logger.js";
|
|
26
|
-
import
|
|
27
|
-
import __decorate from "./_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorate.js";
|
|
28
|
+
import __decorate from "./_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorate.js";
|
|
28
29
|
import http_kernel_default from "./nodefony/service/http-kernel.js";
|
|
29
30
|
import server_http_default from "./nodefony/service/servers/server-http.js";
|
|
30
31
|
import server_https_default from "./nodefony/service/servers/server-https.js";
|
|
32
|
+
import server_h2c_default from "./nodefony/service/servers/server-h2c.js";
|
|
31
33
|
import server_websocket_default from "./nodefony/service/servers/server-websocket.js";
|
|
32
34
|
import server_websocket_secure_default from "./nodefony/service/servers/server-websocket-secure.js";
|
|
33
35
|
import Statics from "./nodefony/service/servers/server-static.js";
|
|
@@ -41,9 +43,10 @@ import { SESSION_COLUMN_ALIASES, SESSION_DEFAULT_ORDER, SESSION_DEFAULT_ORDER_SQ
|
|
|
41
43
|
import sessions_service_default from "./nodefony/service/sessions/sessions-service.js";
|
|
42
44
|
import Certificate, { trustOutboundAuthorities } from "./nodefony/service/certificates.js";
|
|
43
45
|
import upload_service_default from "./nodefony/service/upload/upload-service.js";
|
|
46
|
+
import { isWebsocketContext } from "./nodefony/src/context/websocket/isWebsocketContext.js";
|
|
44
47
|
import { PrebuiltUi, resolveUiDelivery } from "./nodefony/src/assets/prebuiltUi.js";
|
|
45
|
-
import {
|
|
46
|
-
|
|
48
|
+
import { Module, services } from "nodefony";
|
|
49
|
+
// index.ts
|
|
47
50
|
let Http = class Http extends Module {
|
|
48
51
|
overlaySchema = httpOverlaySchema;
|
|
49
52
|
constructor(kernel) {
|
|
@@ -75,7 +78,7 @@ let Http = class Http extends Module {
|
|
|
75
78
|
const input = this.options;
|
|
76
79
|
const options = defineHttpConfig(input ?? {}, this.kernel);
|
|
77
80
|
this.options = options;
|
|
78
|
-
await trustOutboundAuthorities(options.certificates.extraCaFiles, this.kernel?.path ?? process.cwd(), (message, severity) => this.log(message, severity));
|
|
81
|
+
await trustOutboundAuthorities(options.certificates.extraCaFiles, this.kernel?.path ?? process.cwd(), (message, severity) => this.log(message, severity), { development: this.kernel?.environment === "development" });
|
|
79
82
|
return this;
|
|
80
83
|
}
|
|
81
84
|
/**
|
|
@@ -107,10 +110,10 @@ Http = __decorate([services([
|
|
|
107
110
|
ReverseProxy,
|
|
108
111
|
server_http_default,
|
|
109
112
|
server_https_default,
|
|
113
|
+
server_h2c_default,
|
|
110
114
|
server_websocket_default,
|
|
111
115
|
server_websocket_secure_default,
|
|
112
116
|
upload_service_default
|
|
113
|
-
])
|
|
117
|
+
])], Http);
|
|
114
118
|
var http_default = Http;
|
|
115
|
-
|
|
116
|
-
export { Context, Cookie, DefaultErrorRenderer, DefaultRequestLogger, FrameProfile, Http2Request, Http2Response, HttpContext, HttpError, http_kernel_default as HttpKernel, HttpRequest, HttpResponse, HttpResponse as Response, JsonAuditLogger, MemoryRateLimitStore, PrebuiltUi, PrettyRequestLogger, Profiler, ReverseProxy, SESSION_COLUMN_ALIASES, SESSION_DEFAULT_ORDER, SESSION_DEFAULT_ORDER_SQL, SESSION_SORTABLE_FIELDS, SSE_HEARTBEAT_MS, Session, sessions_service_default as SessionsService, SseStream, WebsocketContext, WebsocketResponse, WebsocketResponse as wsResponse, acceptsEventStream, compileDomainPattern, compileDomainPatterns, compileTrustedHosts, decideSend, http_default as default, defineHttpConfig, httpConfigJsonSchema, httpConfigSchema, isDomainAllowed, normalizePrefix, openSseStream, parseProxyOrigin, proxyMountProblems, readBackpressureOptions, resolveUiDelivery, schemaMismatchOf };
|
|
119
|
+
export { ACTION_SELF_PHASE, Context, Cookie, DefaultErrorRenderer, DefaultRequestLogger, FrameProfile, Http2Request, Http2Response, HttpContext, HttpError, http_kernel_default as HttpKernel, HttpRequest, HttpResponse, HttpResponse as Response, JsonAuditLogger, MemoryRateLimitStore, NO_ROUTE, N_PLUS_ONE_MIN_REPEATS, PrebuiltUi, PrettyRequestLogger, Profiler, REQUEST_TRACE_CHANNEL, ReverseProxy, SESSION_COLUMN_ALIASES, SESSION_DEFAULT_ORDER, SESSION_DEFAULT_ORDER_SQL, SESSION_SORTABLE_FIELDS, SSE_HEARTBEAT_MS, Session, sessions_service_default as SessionsService, SseStream, TOOLING_URL_PREFIX, WebsocketContext, WebsocketResponse, WebsocketResponse as wsResponse, acceptsEventStream, aggregateProfiles, compileDomainPattern, compileDomainPatterns, compileTrustedHosts, createProfileAggregator, decideSend, http_default as default, defineHttpConfig, httpConfigJsonSchema, httpConfigSchema, isDomainAllowed, isFailedProfile, isWebsocketContext, normalizePrefix, openSseStream, parseProxyOrigin, proxyMountProblems, readBackpressureOptions, resolveUiDelivery, schemaMismatchOf, sqlShape, toValidationFields };
|
|
@@ -3,7 +3,7 @@ import { Command } from "nodefony";
|
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import fsp from "node:fs/promises";
|
|
5
5
|
import { isAbsolute, join, resolve } from "node:path";
|
|
6
|
-
|
|
6
|
+
// nodefony/command/assetsPublishCommand.ts
|
|
7
7
|
const options = {
|
|
8
8
|
helpGroup: "FRONT ET RÉSEAU",
|
|
9
9
|
showBanner: false,
|
|
@@ -35,7 +35,10 @@ async function countFiles(dir) {
|
|
|
35
35
|
*/
|
|
36
36
|
var AssetsPublish = class extends Command {
|
|
37
37
|
constructor(cli) {
|
|
38
|
-
super("assets:publish", "rassemble les fichiers statiques pour un CDN", cli,
|
|
38
|
+
super("assets:publish", "rassemble les fichiers statiques pour un CDN", cli, {
|
|
39
|
+
...options,
|
|
40
|
+
examples: ["nodefony assets:publish", "nodefony assets:publish --out dist-assets --clean"]
|
|
41
|
+
});
|
|
39
42
|
this.addOption("-o, --out <dir>", "output directory (default ./dist-assets)");
|
|
40
43
|
this.addOption("--clean", "remove the output directory first");
|
|
41
44
|
this.addOption("--json", "print a machine-readable summary");
|
|
@@ -96,5 +99,4 @@ var AssetsPublish = class extends Command {
|
|
|
96
99
|
return sources;
|
|
97
100
|
}
|
|
98
101
|
};
|
|
99
|
-
//#endregion
|
|
100
102
|
export { AssetsPublish as default };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from "nodefony";
|
|
2
2
|
import { inspect } from "node:util";
|
|
3
|
-
|
|
3
|
+
// nodefony/command/certificatesCommand.ts
|
|
4
4
|
const options = {
|
|
5
5
|
helpGroup: "FRONT ET RÉSEAU",
|
|
6
6
|
showBanner: false,
|
|
@@ -16,7 +16,10 @@ const options = {
|
|
|
16
16
|
*/
|
|
17
17
|
var Certificates = class extends Command {
|
|
18
18
|
constructor(cli) {
|
|
19
|
-
super("http:certificates", "engendre ou inspecte le certificat TLS de dev", cli,
|
|
19
|
+
super("http:certificates", "engendre ou inspecte le certificat TLS de dev", cli, {
|
|
20
|
+
...options,
|
|
21
|
+
examples: ["nodefony http:certificates", "nodefony http:certificates --force"]
|
|
22
|
+
});
|
|
20
23
|
this.addOption("-f, --force", "regenerate even if a valid certificate already exists");
|
|
21
24
|
this.addOption("-j, --json", "output the certificate summary as JSON");
|
|
22
25
|
}
|
|
@@ -42,5 +45,4 @@ Requête vérifiée (NE PAS utiliser -k / rejectUnauthorized:false) :
|
|
|
42
45
|
return this;
|
|
43
46
|
}
|
|
44
47
|
};
|
|
45
|
-
//#endregion
|
|
46
48
|
export { Certificates as default };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from "nodefony";
|
|
2
2
|
import { inspect } from "node:util";
|
|
3
|
-
|
|
3
|
+
// nodefony/command/networkCommand.ts
|
|
4
4
|
const options = {
|
|
5
5
|
helpGroup: "FRONT ET RÉSEAU",
|
|
6
6
|
showBanner: false,
|
|
@@ -8,7 +8,10 @@ const options = {
|
|
|
8
8
|
};
|
|
9
9
|
var Network = class extends Command {
|
|
10
10
|
constructor(cli) {
|
|
11
|
-
super("http:network", "les interfaces réseau et adresses de cette machine", cli,
|
|
11
|
+
super("http:network", "les interfaces réseau et adresses de cette machine", cli, {
|
|
12
|
+
...options,
|
|
13
|
+
examples: ["nodefony http:network", "nodefony http:network eth0 --json"]
|
|
14
|
+
});
|
|
12
15
|
this.addArgument("[interface]", "Selection interface example eth0 ");
|
|
13
16
|
this.addOption("-j, --json", "get json");
|
|
14
17
|
}
|
|
@@ -23,5 +26,4 @@ var Network = class extends Command {
|
|
|
23
26
|
return this;
|
|
24
27
|
}
|
|
25
28
|
};
|
|
26
|
-
//#endregion
|
|
27
29
|
export { Network as default };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { resolveTrustedHostNames } from "../src/context/domainMatcher.js";
|
|
2
|
+
import { asServersConfig, configuredServerPort } from "../src/servers/kernelServers.js";
|
|
2
3
|
import { defaultIntrospection, generateHaproxyConfig, generateNginxConfig } from "../src/proxy/generateProxyConfig.js";
|
|
3
4
|
import { planAssetPublish } from "../src/assets/collectAssets.js";
|
|
4
5
|
import { Command } from "nodefony";
|
|
5
6
|
import fsp from "node:fs/promises";
|
|
6
7
|
import { isAbsolute, resolve } from "node:path";
|
|
7
|
-
|
|
8
|
+
// nodefony/command/proxyGenerateCommand.ts
|
|
8
9
|
const options = {
|
|
9
10
|
helpGroup: "FRONT ET RÉSEAU",
|
|
10
11
|
showBanner: false,
|
|
@@ -18,7 +19,10 @@ const options = {
|
|
|
18
19
|
*/
|
|
19
20
|
var ProxyGenerate = class extends Command {
|
|
20
21
|
constructor(cli) {
|
|
21
|
-
super("proxy:generate", "engendre une configuration nginx ou haproxy", cli,
|
|
22
|
+
super("proxy:generate", "engendre une configuration nginx ou haproxy", cli, {
|
|
23
|
+
...options,
|
|
24
|
+
examples: ["nodefony proxy:generate nginx", "nodefony proxy:generate haproxy --out haproxy.cfg"]
|
|
25
|
+
});
|
|
22
26
|
this.addArgument("<target>", "nginx | haproxy");
|
|
23
27
|
this.addOption("-o, --out <file>", "write to file instead of stdout");
|
|
24
28
|
this.addOption("-b, --backend <host>", "backend host the proxy connects to (default 127.0.0.1)");
|
|
@@ -67,12 +71,14 @@ var ProxyGenerate = class extends Command {
|
|
|
67
71
|
backendHost: opts.backend ?? "127.0.0.1",
|
|
68
72
|
httpPort: Number(servers?.http?.port) || defaultIntrospection.httpPort,
|
|
69
73
|
httpsPort: Number(servers?.https?.port) || defaultIntrospection.httpsPort,
|
|
74
|
+
h2cPort: configuredServerPort(asServersConfig(this.kernel?.options.servers), "h2c") ?? null,
|
|
70
75
|
staticRoots,
|
|
71
76
|
mounts,
|
|
72
77
|
listen: opts.listen ? Number(opts.listen) : defaultIntrospection.listen,
|
|
73
78
|
reencrypt: Boolean(opts.reencrypt),
|
|
74
79
|
maxBodyBytes: Number(httpOpts.maxBodySize) || 0,
|
|
75
80
|
keepaliveIntervalMs: Number(httpOpts.websocket?.keepaliveInterval) || 0,
|
|
81
|
+
readinessPath: httpOpts.health?.enabled === false ? null : httpOpts.health?.readinessPath ?? "/readyz",
|
|
76
82
|
tls: opts.tlsCert && opts.tlsKey ? {
|
|
77
83
|
certPath: opts.tlsCert,
|
|
78
84
|
keyPath: opts.tlsKey,
|
|
@@ -81,5 +87,4 @@ var ProxyGenerate = class extends Command {
|
|
|
81
87
|
};
|
|
82
88
|
}
|
|
83
89
|
};
|
|
84
|
-
//#endregion
|
|
85
90
|
export { ProxyGenerate as default };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { normalizePrefix, proxyMountProblems } from "../src/proxy/rules.js";
|
|
2
2
|
import { overlayField } from "nodefony";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
|
|
4
|
+
// nodefony/config/config.ts
|
|
5
5
|
/**
|
|
6
6
|
* @nodefony/http — CONFIGURATION DU MODULE (schéma Zod = source unique).
|
|
7
7
|
*
|
|
@@ -297,7 +297,8 @@ const proxyMountSchema = z.strictObject({
|
|
|
297
297
|
preserveHost: z.boolean().default(false).describe("Transmet le `Host` du client au lieu de celui de la cible. Défaut false : l'amont reçoit son propre nom (ce qu'attend un serveur qui filtre les noms d'hôte), le nom d'origine part en `X-Forwarded-Host`."),
|
|
298
298
|
stripHeaders: z.array(z.string()).default([]).describe("En-têtes de requête à ne PAS transmettre (ex. `[\"cookie\", \"authorization\"]` vers un amont qui n'a pas à voir la session). Les en-têtes de connexion (RFC 9110 §7.6.1) ne le sont jamais."),
|
|
299
299
|
timeoutMs: z.number().optional().describe("Délai d'inactivité avec l'amont (ms) — dépassé avant la réponse (ou avant le `101` d'un upgrade WebSocket) : 504. Absent = `proxy.timeoutMs`. Ne s'applique pas à un WebSocket établi."),
|
|
300
|
-
secure: z.boolean().default(true).describe("Vérifie le certificat d'une cible https. `false` n'est accepté que vers la boucle locale (`127.0.0.0/8`, `localhost`, `::1`) ; ailleurs, fournir l'autorité de l'amont (`certificates.extraCaFiles`, ou `NODE_EXTRA_CA_CERTS`).")
|
|
300
|
+
secure: z.boolean().default(true).describe("Vérifie le certificat d'une cible https. `false` n'est accepté que vers la boucle locale (`127.0.0.0/8`, `localhost`, `::1`) ; ailleurs, fournir l'autorité de l'amont (`certificates.extraCaFiles`, ou `NODE_EXTRA_CA_CERTS`)."),
|
|
301
|
+
accessLog: z.enum(["all", "errors"]).default("all").describe("Ligne d'accès des requêtes relayées : `all` (défaut) les écrit toutes, `errors` seulement celles en échec (statut ≥ 400) — pour un amont qui sert beaucoup de petits fichiers.")
|
|
301
302
|
}).describe("Un montage du proxy inverse : un préfixe d'URL relayé vers un amont.");
|
|
302
303
|
const proxySchema = z.strictObject({
|
|
303
304
|
timeoutMs: z.number().int().positive().default(3e4).describe("Délai d'inactivité par défaut de tous les montages (ms) : un amont muet au-delà rend 504 Gateway Timeout. Défaut 30 s."),
|
|
@@ -393,5 +394,4 @@ const httpOverlaySchema = z.strictObject({
|
|
|
393
394
|
* valides par construction ; passés au `super(..., config)` du Module class.
|
|
394
395
|
*/
|
|
395
396
|
const config = httpConfigSchema.parse({});
|
|
396
|
-
//#endregion
|
|
397
397
|
export { config as default, httpConfigSchema, httpOverlaySchema };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { httpConfigSchema } from "./config.js";
|
|
2
2
|
import { parseModuleConfig } from "nodefony";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
|
|
4
|
+
// nodefony/config/defineModuleConfig.ts
|
|
5
5
|
/**
|
|
6
6
|
* Injecte les défauts dérivés du kernel APRÈS le parse (le schéma reste pur).
|
|
7
7
|
*
|
|
@@ -89,5 +89,4 @@ function defineHttpConfig(config = {}, kernel) {
|
|
|
89
89
|
function httpConfigJsonSchema() {
|
|
90
90
|
return z.toJSONSchema(httpConfigSchema);
|
|
91
91
|
}
|
|
92
|
-
//#endregion
|
|
93
92
|
export { defineHttpConfig, httpConfigJsonSchema };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SESSION_FACETS, SESSION_FILTERS, SESSION_STATS_FILTERS } from "../src/session/storage/sessionFilters.js";
|
|
2
2
|
import { RATE_LIMIT_FILTERS } from "../src/rateLimit/rateLimitFilters.js";
|
|
3
3
|
import { parseFilters, parsePageQuery } from "nodefony";
|
|
4
|
-
|
|
4
|
+
// nodefony/service/HttpAdminApi.ts
|
|
5
5
|
/** Nom du service HttpKernel dans le container (source unique). */
|
|
6
6
|
const HTTP_KERNEL_SERVICE = "HttpKernel";
|
|
7
7
|
/**
|
|
@@ -62,6 +62,7 @@ function createHttpAdminApi(module) {
|
|
|
62
62
|
const serverServices = [
|
|
63
63
|
"server-http",
|
|
64
64
|
"server-https",
|
|
65
|
+
"server-h2c",
|
|
65
66
|
"server-websocket",
|
|
66
67
|
"server-websocket-secure",
|
|
67
68
|
"server-static"
|
|
@@ -371,5 +372,4 @@ function createHttpAdminApi(module) {
|
|
|
371
372
|
adminEndpoints: () => endpoints
|
|
372
373
|
};
|
|
373
374
|
}
|
|
374
|
-
//#endregion
|
|
375
375
|
export { createHttpAdminApi };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { aggregateProfiles } from "../src/profiler/aggregate.js";
|
|
2
|
+
// nodefony/service/ProfilerAdminApi.ts
|
|
2
3
|
/**
|
|
3
4
|
* Producteur `IAdminApi` du **profiler** — exposé sous `/nodefony/profiler/api/*`.
|
|
4
5
|
*
|
|
@@ -9,6 +10,9 @@
|
|
|
9
10
|
*
|
|
10
11
|
* Endpoints :
|
|
11
12
|
* - `GET /nodefony/profiler/api/recent` → derniers profils (résumés, récent → ancien)
|
|
13
|
+
* `?connection=<requestId>` : les seules frames de cette socket WebSocket
|
|
14
|
+
* - `GET /nodefony/profiler/api/summary` → les profils de l'anneau agrégés par
|
|
15
|
+
* route (percentiles, phases, formes SQL, N+1, erreurs — `aggregateProfiles`)
|
|
12
16
|
* - `GET /nodefony/profiler/api/{id}` → profil complet (phases) d'un requestId
|
|
13
17
|
* - `DELETE /nodefony/profiler/api/recent` → vide le ring buffer
|
|
14
18
|
*
|
|
@@ -32,9 +36,11 @@ function createProfilerAdminApi(profiler) {
|
|
|
32
36
|
const raw = query.limit;
|
|
33
37
|
const n = typeof raw === "string" ? parseInt(raw, 10) : NaN;
|
|
34
38
|
const limit = Number.isFinite(n) && n > 0 ? Math.min(n, 200) : 60;
|
|
39
|
+
const of = query.connection;
|
|
40
|
+
const connection = typeof of === "string" && of.length > 0 && of.length <= 200 ? of : void 0;
|
|
35
41
|
return {
|
|
36
42
|
count: profiler.size,
|
|
37
|
-
entries: profiler.recent(limit)
|
|
43
|
+
entries: profiler.recent(limit, connection)
|
|
38
44
|
};
|
|
39
45
|
}
|
|
40
46
|
},
|
|
@@ -47,6 +53,11 @@ function createProfilerAdminApi(profiler) {
|
|
|
47
53
|
return { cleared: true };
|
|
48
54
|
}
|
|
49
55
|
},
|
|
56
|
+
{
|
|
57
|
+
path: "summary",
|
|
58
|
+
summary: "Profiles aggregated by route: percentiles, SQL, N+1, errors",
|
|
59
|
+
handler: () => aggregateProfiles(profiler.since(0).entries)
|
|
60
|
+
},
|
|
50
61
|
{
|
|
51
62
|
path: "{id}",
|
|
52
63
|
summary: "Full profile (phase timeline) for a requestId",
|
|
@@ -70,5 +81,4 @@ function createProfilerAdminApi(profiler) {
|
|
|
70
81
|
adminEndpoints: () => endpoints
|
|
71
82
|
};
|
|
72
83
|
}
|
|
73
|
-
//#endregion
|
|
74
84
|
export { createProfilerAdminApi, createProfilerAdminApi as default };
|
|
@@ -1,18 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
* @returns durée en millisecondes, ou `null`
|
|
10
|
-
*/
|
|
11
|
-
function computeDurationMs(phases) {
|
|
12
|
-
const first = phases.at(0);
|
|
13
|
-
if (first === void 0 || typeof first.startMs !== "number") return null;
|
|
14
|
-
return performance.now() - first.startMs;
|
|
15
|
-
}
|
|
1
|
+
import { handshakeRefusalStatus } from "../src/context/websocket/WebsocketContext.js";
|
|
2
|
+
// nodefony/service/audit-logger.ts
|
|
3
|
+
const statusOf = (context) => {
|
|
4
|
+
return context.response?.statusCode ?? null;
|
|
5
|
+
};
|
|
6
|
+
const headersOf = (context) => {
|
|
7
|
+
return context.request?.headers ?? {};
|
|
8
|
+
};
|
|
16
9
|
/**
|
|
17
10
|
* Severity derived from HTTP status code — RFC 9110 categories.
|
|
18
11
|
* 1xx/2xx/3xx → INFO ; 4xx → WARNING ; 5xx → ERROR.
|
|
@@ -65,20 +58,20 @@ var JsonAuditLogger = class {
|
|
|
65
58
|
const ctx = context;
|
|
66
59
|
if (!this.nominalEnabled) {
|
|
67
60
|
if (error ?? ctx.error) return true;
|
|
68
|
-
const status = ctx
|
|
61
|
+
const status = statusOf(ctx);
|
|
69
62
|
return status !== null && status >= 400;
|
|
70
63
|
}
|
|
71
64
|
if (this.sampleRate <= 1) return true;
|
|
72
65
|
if (error ?? ctx.error) return true;
|
|
73
|
-
const status = ctx
|
|
66
|
+
const status = statusOf(ctx);
|
|
74
67
|
if (status !== null && status >= 400) return true;
|
|
75
68
|
this.sampleCounter = (this.sampleCounter + 1) % this.sampleRate;
|
|
76
69
|
return this.sampleCounter === 0;
|
|
77
70
|
}
|
|
78
71
|
renderHttp(context, error) {
|
|
79
72
|
const ctx = context;
|
|
80
|
-
const status = ctx
|
|
81
|
-
const headers = ctx
|
|
73
|
+
const status = statusOf(ctx);
|
|
74
|
+
const headers = headersOf(ctx);
|
|
82
75
|
const err = error ?? ctx.error ?? null;
|
|
83
76
|
const entry = {
|
|
84
77
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -89,10 +82,10 @@ var JsonAuditLogger = class {
|
|
|
89
82
|
method: ctx.method,
|
|
90
83
|
url: ctx.url,
|
|
91
84
|
status,
|
|
92
|
-
durationMs:
|
|
85
|
+
durationMs: ctx.stopClock(),
|
|
93
86
|
remoteAddress: ctx.remoteAddress ?? null,
|
|
94
|
-
host: ctx.getHost
|
|
95
|
-
userAgent: ctx.getUserAgent
|
|
87
|
+
host: ctx.getHost() ?? null,
|
|
88
|
+
userAgent: ctx.getUserAgent() ?? null,
|
|
96
89
|
hasAuthorization: Boolean(headers["authorization"]),
|
|
97
90
|
hasCookie: Boolean(headers["cookie"]),
|
|
98
91
|
phases: ctx.phases.length ? ctx.phases.map((p) => ({
|
|
@@ -109,8 +102,9 @@ var JsonAuditLogger = class {
|
|
|
109
102
|
}
|
|
110
103
|
renderWebsocket(context, error, acceptedProtocol) {
|
|
111
104
|
const ctx = context;
|
|
112
|
-
const
|
|
113
|
-
const
|
|
105
|
+
const refused = error ? handshakeRefusalStatus(ctx, error) : null;
|
|
106
|
+
const status = refused ?? statusOf(ctx);
|
|
107
|
+
const headers = headersOf(ctx);
|
|
114
108
|
const entry = {
|
|
115
109
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
116
110
|
requestId: ctx.requestId,
|
|
@@ -120,10 +114,10 @@ var JsonAuditLogger = class {
|
|
|
120
114
|
method: ctx.method,
|
|
121
115
|
url: ctx.url,
|
|
122
116
|
status,
|
|
123
|
-
durationMs:
|
|
117
|
+
durationMs: ctx.stopClock(),
|
|
124
118
|
remoteAddress: ctx.remoteAddress ?? null,
|
|
125
|
-
host: ctx.getHost
|
|
126
|
-
userAgent: ctx.getUserAgent
|
|
119
|
+
host: ctx.getHost() ?? null,
|
|
120
|
+
userAgent: ctx.getUserAgent() ?? null,
|
|
127
121
|
hasAuthorization: Boolean(headers["authorization"]),
|
|
128
122
|
hasCookie: Boolean(headers["cookie"]),
|
|
129
123
|
phases: ctx.phases.length ? ctx.phases.map((p) => ({
|
|
@@ -135,7 +129,7 @@ var JsonAuditLogger = class {
|
|
|
135
129
|
};
|
|
136
130
|
return {
|
|
137
131
|
text: JSON.stringify(entry),
|
|
138
|
-
severity: error ? "ERROR" : "INFO",
|
|
132
|
+
severity: refused !== null ? severityFromStatus(refused) : error ? "ERROR" : "INFO",
|
|
139
133
|
msgid: "audit"
|
|
140
134
|
};
|
|
141
135
|
}
|
|
@@ -157,5 +151,4 @@ var JsonAuditLogger = class {
|
|
|
157
151
|
return entry;
|
|
158
152
|
}
|
|
159
153
|
};
|
|
160
|
-
|
|
161
|
-
export { computeDurationMs, JsonAuditLogger as default, severityFromStatus };
|
|
154
|
+
export { JsonAuditLogger as default, severityFromStatus, statusOf };
|
|
@@ -7,7 +7,7 @@ import path, { resolve } from "node:path";
|
|
|
7
7
|
import tls from "node:tls";
|
|
8
8
|
import { promisify } from "node:util";
|
|
9
9
|
import { execFile } from "node:child_process";
|
|
10
|
-
|
|
10
|
+
// nodefony/service/certificates.ts
|
|
11
11
|
const execFileAsync = promisify(execFile);
|
|
12
12
|
const generateKeyPairAsync = promisify(generateKeyPair);
|
|
13
13
|
/** Compare deux PEM sans dépendre des fins de ligne ni des espaces. */
|
|
@@ -66,12 +66,16 @@ function splitPemCertificates(pem) {
|
|
|
66
66
|
*
|
|
67
67
|
* @param file - chemin du fichier, absolu ou relatif à `root`
|
|
68
68
|
* @param root - racine de l'application
|
|
69
|
+
* Chaque refus dit le problème, d'où vient le chemin quand on le sait, puis le
|
|
70
|
+
* geste qui répare ({@link caFileRemedy}).
|
|
71
|
+
*
|
|
69
72
|
* @param store - accès aux ancres ; `node:tls` par défaut
|
|
73
|
+
* @param origin - phrase qui dit d'où vient le chemin (` Valeur venue de …`), insérée dans un refus
|
|
70
74
|
* @returns le bilan de l'ajout
|
|
71
75
|
* @throws BootConfigurationError - fichier illisible, sans certificat, ou Node
|
|
72
76
|
* incapable d'élargir ses ancres en cours d'exécution
|
|
73
77
|
*/
|
|
74
|
-
async function trustCaFile(file, root, store = tls) {
|
|
78
|
+
async function trustCaFile(file, root, store = tls, origin = "") {
|
|
75
79
|
const absolute = path.resolve(root, file);
|
|
76
80
|
let content;
|
|
77
81
|
try {
|
|
@@ -79,10 +83,10 @@ async function trustCaFile(file, root, store = tls) {
|
|
|
79
83
|
} catch (error) {
|
|
80
84
|
const code = error instanceof Error && "code" in error ? error.code : void 0;
|
|
81
85
|
const reason = typeof code === "string" ? code : error instanceof Error ? error.message : String(error);
|
|
82
|
-
throw new BootConfigurationError(
|
|
86
|
+
throw new BootConfigurationError(`${unreadableProblem(absolute, reason)}${origin} ${caFileRemedy(reason)}`);
|
|
83
87
|
}
|
|
84
88
|
const certificates = splitPemCertificates(content);
|
|
85
|
-
if (certificates.length === 0) throw new BootConfigurationError(`http.certificates.extraCaFiles : « ${absolute} » ne contient aucun certificat PEM (-----BEGIN CERTIFICATE-----)
|
|
89
|
+
if (certificates.length === 0) throw new BootConfigurationError(`http.certificates.extraCaFiles : « ${absolute} » ne contient aucun certificat PEM (-----BEGIN CERTIFICATE-----).${origin} ${caFileRemedy("NOPEM")}`);
|
|
86
90
|
let added = 0;
|
|
87
91
|
let present = 0;
|
|
88
92
|
for (const pem of certificates) {
|
|
@@ -97,27 +101,107 @@ async function trustCaFile(file, root, store = tls) {
|
|
|
97
101
|
present
|
|
98
102
|
};
|
|
99
103
|
}
|
|
104
|
+
/** Première phrase d'un refus de lecture : le fichier et la cause en un mot. */
|
|
105
|
+
function unreadableProblem(absolute, reason) {
|
|
106
|
+
return `http.certificates.extraCaFiles : « ${absolute} » est illisible (${reason}).`;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Le geste qui répare une autorité déclarée qu'on ne peut pas reconnaître.
|
|
110
|
+
*
|
|
111
|
+
* Un refus au démarrage qui ne dit que la cause envoie chercher ; celui-ci dit
|
|
112
|
+
* quoi faire. Pure : éprouvée cause par cause, y compris celles qu'une machine
|
|
113
|
+
* de test ne sait pas produire (un droit refusé ne l'est pas à `root`, ni
|
|
114
|
+
* sous Windows).
|
|
115
|
+
*
|
|
116
|
+
* @param cause - code système (`ENOENT`, `EACCES`, `EPERM`, `EISDIR`),
|
|
117
|
+
* `NOPEM` pour un fichier lu sans certificat, ou tout autre motif
|
|
118
|
+
* @returns la phrase du geste
|
|
119
|
+
*/
|
|
120
|
+
function caFileRemedy(cause) {
|
|
121
|
+
switch (cause) {
|
|
122
|
+
case "ENOENT": return "Poser le fichier à ce chemin (absolu, ou relatif à la racine de l'application), ou retirer la déclaration.";
|
|
123
|
+
case "EACCES":
|
|
124
|
+
case "EPERM": return "Le compte qui lance l'application n'a pas le droit de le lire : lui donner ce droit (sous Kubernetes, monter le ConfigMap ou le Secret lisible par l'utilisateur du pod).";
|
|
125
|
+
case "EISDIR": return "C'est un dossier : désigner le fichier PEM qu'il contient.";
|
|
126
|
+
case "NOPEM": return "Le fichier doit porter le CERTIFICAT de l'autorité (pas sa clé privée), au format PEM ; un fichier DER se convertit par `openssl x509 -inform der -in autorite.der -out autorite.pem`.";
|
|
127
|
+
default: return "Vérifier le chemin et les droits du fichier, ou retirer la déclaration.";
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Dossier où Nodefony range les certificats qu'il fabrique en développement,
|
|
132
|
+
* relatif au répertoire courant.
|
|
133
|
+
*
|
|
134
|
+
* @returns le chemin absolu de `nodefony/config/certificates`
|
|
135
|
+
*/
|
|
136
|
+
function certificatesDirectory() {
|
|
137
|
+
return resolve(".", "nodefony", "config", "certificates");
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Chemin de l'autorité de DÉVELOPPEMENT : le certificat racine que Nodefony
|
|
141
|
+
* fabrique lui-même pour signer le certificat du serveur local. Généré, jamais
|
|
142
|
+
* versionné — une seule convention, que le service et la confiance sortante lisent.
|
|
143
|
+
*
|
|
144
|
+
* @param directory - dossier des certificats ; {@link certificatesDirectory} par défaut
|
|
145
|
+
* @returns le chemin absolu du fichier PEM de l'autorité
|
|
146
|
+
*/
|
|
147
|
+
function developmentAuthorityPath(directory = certificatesDirectory()) {
|
|
148
|
+
return resolve(directory, "ca", "nodefony-root-ca.crt.pem");
|
|
149
|
+
}
|
|
150
|
+
/** Vrai si le fichier n'existe pas (et seulement dans ce cas). */
|
|
151
|
+
async function isMissing(file) {
|
|
152
|
+
try {
|
|
153
|
+
await fsp.stat(file);
|
|
154
|
+
return false;
|
|
155
|
+
} catch (error) {
|
|
156
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
100
159
|
/**
|
|
101
160
|
* Ajoute aux ancres TLS du processus les autorités déclarées
|
|
102
161
|
* (`certificates.extraCaFiles`), au plus tôt : appelé à l'enregistrement du
|
|
103
162
|
* module, avant tout service — toute requête sortante, commande console
|
|
104
163
|
* comprise, les trouve en place.
|
|
105
164
|
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
165
|
+
* L'autorité de DÉVELOPPEMENT déclarée mais pas encore engendrée est ignorée en
|
|
166
|
+
* développement, et le journal le dit : elle n'a signé aucun certificat, il n'y
|
|
167
|
+
* a donc rien à lui faire confiance, et le serveur de développement la pose
|
|
168
|
+
* lui-même quand il la fabrique ({@link Certificate.generateServerCertificates}).
|
|
169
|
+
* Rien n'est fabriqué ici : une commande console n'écrit pas de certificat.
|
|
170
|
+
* Une autre autorité absente, ou n'importe laquelle hors développement, reste
|
|
171
|
+
* un refus — qui nomme la variable d'origine et le geste qui répare
|
|
172
|
+
* ({@link caFileRemedy}).
|
|
108
173
|
*
|
|
109
174
|
* @param files - `certificates.extraCaFiles`
|
|
110
175
|
* @param root - racine de l'application
|
|
111
176
|
* @param log - journal du module
|
|
177
|
+
* @param context - décor (environnement, autorité de dev, ancres)
|
|
112
178
|
* @throws BootConfigurationError - cf {@link trustCaFile}
|
|
113
179
|
*/
|
|
114
|
-
async function trustOutboundAuthorities(files, root, log) {
|
|
180
|
+
async function trustOutboundAuthorities(files, root, log, context = {}) {
|
|
181
|
+
const developmentAuthority = context.developmentAuthority ?? developmentAuthorityPath();
|
|
115
182
|
for (const file of files) {
|
|
116
|
-
const
|
|
183
|
+
const absolute = path.resolve(root, file);
|
|
184
|
+
const isDevelopmentAuthority = absolute === developmentAuthority;
|
|
185
|
+
if (isDevelopmentAuthority && context.development === true && await isMissing(absolute)) {
|
|
186
|
+
log(`autorité de développement ${absolute} pas encore engendrée : ignorée. Elle naît au premier \`npx nodefony development\` (ou \`npx nodefony http:certificates\`), et le serveur de développement lui fait alors confiance de lui-même.`, "INFO");
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const origin = originOf(absolute, root, context.env ?? process.env);
|
|
190
|
+
if (isDevelopmentAuthority && await isMissing(absolute)) throw new BootConfigurationError(`${unreadableProblem(absolute, "ENOENT")}${origin} C'est l'autorité de DÉVELOPPEMENT : Nodefony la fabrique au premier \`npx nodefony development\`, jamais hors développement. Pour essayer la production sur ce poste, l'engendrer une fois (\`npx nodefony http:certificates\`) puis relancer ; sinon retirer la déclaration — une production réelle n'en a pas besoin.`);
|
|
191
|
+
const trust = await trustCaFile(file, root, context.store, origin);
|
|
117
192
|
log(`autorité sortante ${trust.file} : ${trust.added} ajoutée(s), ${trust.present} déjà présente(s)`, "INFO");
|
|
118
193
|
}
|
|
119
194
|
}
|
|
120
195
|
/**
|
|
196
|
+
* Nomme la variable d'où vient le chemin, quand c'est `NF_TLS_CA_FILE` — la
|
|
197
|
+
* déclaration que pose le gabarit d'application. Constaté sur la valeur, jamais
|
|
198
|
+
* supposé : une configuration écrite à la main n'est pas attribuée à la variable.
|
|
199
|
+
*/
|
|
200
|
+
function originOf(absolute, root, env) {
|
|
201
|
+
const declared = env.NF_TLS_CA_FILE;
|
|
202
|
+
return declared && path.resolve(root, declared) === absolute ? " Valeur venue de NF_TLS_CA_FILE (`.env` ou environnement du processus)." : "";
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
121
205
|
* Vrai si `host` est une IP littérale (IPv4 `n.n.n.n` ou IPv6 — contient `:`).
|
|
122
206
|
* Une IP doit aller en SAN `iPAddress`, jamais en `dNSName` (RFC 9525).
|
|
123
207
|
*/
|
|
@@ -166,9 +250,9 @@ var Certificate = class Certificate extends Service {
|
|
|
166
250
|
publicKeyPem = "";
|
|
167
251
|
/** CAROOT résolu de mkcert (null tant que non détecté / indisponible). */
|
|
168
252
|
mkcertCaRoot = null;
|
|
169
|
-
path =
|
|
253
|
+
path = certificatesDirectory();
|
|
170
254
|
serverPath = resolve(this.path, "server");
|
|
171
|
-
caPath =
|
|
255
|
+
caPath = developmentAuthorityPath(this.path);
|
|
172
256
|
publicKeyPath = resolve(this.path, "server", "publickey.pem");
|
|
173
257
|
privateKeyPath = resolve(this.path, "server", "privkey.pem");
|
|
174
258
|
certPath = resolve(this.path, "server", "cert.pem");
|
|
@@ -480,7 +564,8 @@ var Certificate = class Certificate extends Service {
|
|
|
480
564
|
return Buffer.from(await fsp.readFile(value, "utf8"));
|
|
481
565
|
}
|
|
482
566
|
createFullChain() {
|
|
483
|
-
|
|
567
|
+
const pem = this.cert?.toString().trim() ?? "";
|
|
568
|
+
return pem === "" ? "" : `${pem}\n`;
|
|
484
569
|
}
|
|
485
570
|
async readCerticates() {
|
|
486
571
|
for (const file of this.files) try {
|
|
@@ -634,5 +719,4 @@ var Certificate = class Certificate extends Service {
|
|
|
634
719
|
}
|
|
635
720
|
}
|
|
636
721
|
};
|
|
637
|
-
|
|
638
|
-
export { addDefaultCaCertificate, Certificate as default, splitPemCertificates, trustCaFile, trustOutboundAuthorities };
|
|
722
|
+
export { addDefaultCaCertificate, caFileRemedy, certificatesDirectory, Certificate as default, developmentAuthorityPath, splitPemCertificates, trustCaFile, trustOutboundAuthorities };
|