@sebastienrousseau/crypto-server 0.0.2 → 0.0.8
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 +292 -123
- package/dist/@types/types.d.ts +34 -14
- package/dist/@types/types.d.ts.map +1 -1
- package/dist/@types/types.js +15 -0
- package/dist/config/auth-policy.d.ts +2 -0
- package/dist/config/auth-policy.d.ts.map +1 -0
- package/dist/config/auth-policy.js +18 -0
- package/dist/config/constants.d.ts +54 -0
- package/dist/config/constants.d.ts.map +1 -0
- package/dist/config/constants.js +101 -0
- package/dist/config/env.d.ts +16 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +74 -0
- package/dist/enterprise/metering.d.ts +19 -0
- package/dist/enterprise/metering.d.ts.map +1 -0
- package/dist/enterprise/metering.js +159 -0
- package/dist/enterprise/types.d.ts +78 -0
- package/dist/enterprise/types.d.ts.map +1 -0
- package/dist/enterprise/types.js +3 -0
- package/dist/index.js +38 -74
- package/dist/lib/auth.d.ts +14 -0
- package/dist/lib/auth.d.ts.map +1 -0
- package/dist/lib/auth.js +120 -0
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +6 -5
- package/dist/lib/telemetry.d.ts +4 -0
- package/dist/lib/telemetry.d.ts.map +1 -0
- package/dist/lib/telemetry.js +92 -0
- package/dist/lib/version.d.ts +2 -0
- package/dist/lib/version.d.ts.map +1 -0
- package/dist/lib/version.js +7 -0
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +19 -4
- package/dist/routes/probes.d.ts +4 -0
- package/dist/routes/probes.d.ts.map +1 -0
- package/dist/routes/probes.js +52 -0
- package/dist/routes/v1/decrypt.d.ts +2 -2
- package/dist/routes/v1/decrypt.d.ts.map +1 -1
- package/dist/routes/v1/decrypt.js +49 -6
- package/dist/routes/v1/encrypt.d.ts +2 -2
- package/dist/routes/v1/encrypt.d.ts.map +1 -1
- package/dist/routes/v1/encrypt.js +48 -6
- package/dist/routes/v1/generate.d.ts +2 -2
- package/dist/routes/v1/generate.d.ts.map +1 -1
- package/dist/routes/v1/generate.js +85 -13
- package/dist/routes/v1/index.d.ts +4 -0
- package/dist/routes/v1/index.d.ts.map +1 -0
- package/dist/routes/v1/index.js +19 -0
- package/dist/routes/v1/revoke.d.ts +2 -2
- package/dist/routes/v1/revoke.d.ts.map +1 -1
- package/dist/routes/v1/revoke.js +57 -5
- package/dist/routes/v1/verify.d.ts +4 -0
- package/dist/routes/v1/verify.d.ts.map +1 -0
- package/dist/routes/v1/verify.js +60 -0
- package/dist/routes/v2/algorithms.d.ts +4 -0
- package/dist/routes/v2/algorithms.d.ts.map +1 -0
- package/dist/routes/v2/algorithms.js +15 -0
- package/dist/routes/v2/compliance.d.ts +31 -0
- package/dist/routes/v2/compliance.d.ts.map +1 -0
- package/dist/routes/v2/compliance.js +223 -0
- package/dist/routes/v2/encrypt.d.ts +4 -0
- package/dist/routes/v2/encrypt.d.ts.map +1 -0
- package/dist/routes/v2/encrypt.js +62 -0
- package/dist/routes/v2/hash.d.ts +4 -0
- package/dist/routes/v2/hash.d.ts.map +1 -0
- package/dist/routes/v2/hash.js +34 -0
- package/dist/routes/v2/index.d.ts +4 -0
- package/dist/routes/v2/index.d.ts.map +1 -0
- package/dist/routes/v2/index.js +44 -0
- package/dist/routes/v2/kdf.d.ts +4 -0
- package/dist/routes/v2/kdf.d.ts.map +1 -0
- package/dist/routes/v2/kdf.js +54 -0
- package/dist/routes/v2/key-wrap.d.ts +4 -0
- package/dist/routes/v2/key-wrap.d.ts.map +1 -0
- package/dist/routes/v2/key-wrap.js +97 -0
- package/dist/routes/v2/keys.d.ts +4 -0
- package/dist/routes/v2/keys.d.ts.map +1 -0
- package/dist/routes/v2/keys.js +88 -0
- package/dist/routes/v2/mac.d.ts +4 -0
- package/dist/routes/v2/mac.d.ts.map +1 -0
- package/dist/routes/v2/mac.js +102 -0
- package/dist/routes/v2/multi-recipient.d.ts +4 -0
- package/dist/routes/v2/multi-recipient.d.ts.map +1 -0
- package/dist/routes/v2/multi-recipient.js +88 -0
- package/dist/routes/v2/password-encrypt.d.ts +4 -0
- package/dist/routes/v2/password-encrypt.d.ts.map +1 -0
- package/dist/routes/v2/password-encrypt.js +92 -0
- package/dist/routes/v2/password.d.ts +4 -0
- package/dist/routes/v2/password.d.ts.map +1 -0
- package/dist/routes/v2/password.js +109 -0
- package/dist/routes/v2/pq-hash-sign.d.ts +4 -0
- package/dist/routes/v2/pq-hash-sign.d.ts.map +1 -0
- package/dist/routes/v2/pq-hash-sign.js +131 -0
- package/dist/routes/v2/pq-sign.d.ts +4 -0
- package/dist/routes/v2/pq-sign.d.ts.map +1 -0
- package/dist/routes/v2/pq-sign.js +117 -0
- package/dist/routes/v2/pq.d.ts +4 -0
- package/dist/routes/v2/pq.d.ts.map +1 -0
- package/dist/routes/v2/pq.js +149 -0
- package/dist/routes/v2/sealedbox.d.ts +4 -0
- package/dist/routes/v2/sealedbox.d.ts.map +1 -0
- package/dist/routes/v2/sealedbox.js +145 -0
- package/dist/routes/v2/secretbox.d.ts +4 -0
- package/dist/routes/v2/secretbox.d.ts.map +1 -0
- package/dist/routes/v2/secretbox.js +94 -0
- package/dist/routes/v2/signing.d.ts +4 -0
- package/dist/routes/v2/signing.d.ts.map +1 -0
- package/dist/routes/v2/signing.js +66 -0
- package/dist/routes/v2/stream.d.ts +4 -0
- package/dist/routes/v2/stream.d.ts.map +1 -0
- package/dist/routes/v2/stream.js +206 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +124 -0
- package/dist/utils/route-helpers.d.ts +15 -0
- package/dist/utils/route-helpers.d.ts.map +1 -0
- package/dist/utils/route-helpers.js +42 -0
- package/dist/utils/validation.d.ts +29 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +162 -0
- package/package.json +103 -140
- package/dist/@types/types.js.map +0 -1
- package/dist/COPYRIGHT +0 -14
- package/dist/Report.txt +0 -90
- package/dist/config/endpoints.config.d.ts +0 -7
- package/dist/config/endpoints.config.d.ts.map +0 -1
- package/dist/config/endpoints.config.js +0 -8
- package/dist/config/endpoints.config.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/logger.js.map +0 -1
- package/dist/package.json +0 -141
- package/dist/routes/index.js.map +0 -1
- package/dist/routes/v1/decrypt.js.map +0 -1
- package/dist/routes/v1/encrypt.js.map +0 -1
- package/dist/routes/v1/generate.js.map +0 -1
- package/dist/routes/v1/revoke.js.map +0 -1
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { FastifyServerOptions } from "fastify";
|
|
2
|
+
import { FastifyCompressOptions } from "@fastify/compress";
|
|
3
|
+
import type { FastifyCorsOptions } from "@fastify/cors";
|
|
4
|
+
import type { FastifyHelmetOptions } from "@fastify/helmet";
|
|
5
|
+
export declare const LIB_VERSION: string;
|
|
6
|
+
export declare const HOST: string;
|
|
7
|
+
export declare const PORT: string | number;
|
|
8
|
+
export declare const PROTOCOL: string;
|
|
9
|
+
export declare const consoleOutput: string[];
|
|
10
|
+
export declare const fastifyOptions: FastifyServerOptions;
|
|
11
|
+
export declare const compressOptions: FastifyCompressOptions;
|
|
12
|
+
export declare const helmetOptions: FastifyHelmetOptions;
|
|
13
|
+
export declare const corsOptions: FastifyCorsOptions;
|
|
14
|
+
export declare const PROBE_PATHS: ReadonlySet<string>;
|
|
15
|
+
export declare const isProbePath: (url: string) => boolean;
|
|
16
|
+
export declare const rateLimitOptions: {
|
|
17
|
+
global: boolean;
|
|
18
|
+
max: number;
|
|
19
|
+
timeWindow: string;
|
|
20
|
+
allowList: (req: {
|
|
21
|
+
url: string;
|
|
22
|
+
}) => boolean;
|
|
23
|
+
nameSpace: string;
|
|
24
|
+
addHeaders: {
|
|
25
|
+
"x-ratelimit-limit": boolean;
|
|
26
|
+
"x-ratelimit-remaining": boolean;
|
|
27
|
+
"x-ratelimit-reset": boolean;
|
|
28
|
+
"retry-after": boolean;
|
|
29
|
+
};
|
|
30
|
+
errorResponseBuilder(req: {
|
|
31
|
+
ip: string;
|
|
32
|
+
log: {
|
|
33
|
+
warn: (msg: string) => void;
|
|
34
|
+
};
|
|
35
|
+
}, context: {
|
|
36
|
+
statusCode: number;
|
|
37
|
+
max: number;
|
|
38
|
+
after: string;
|
|
39
|
+
ttl: number;
|
|
40
|
+
}): {
|
|
41
|
+
statusCode: number;
|
|
42
|
+
code: number;
|
|
43
|
+
error: string;
|
|
44
|
+
message: string;
|
|
45
|
+
date: number;
|
|
46
|
+
expiresIn: number;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
export declare const resolveRateLimitMax: () => number;
|
|
50
|
+
export declare const healthCheckOptions: {
|
|
51
|
+
healthcheckUrl: string;
|
|
52
|
+
exposeUptime: boolean;
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/config/constants.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAQ5D,eAAO,MAAM,WAAW,QAA+B,CAAC;AAMxD,eAAO,MAAM,IAAI,QAAqC,CAAC;AAMvD,eAAO,MAAM,IAAI,iBAA8B,CAAC;AAMhD,eAAO,MAAM,QAAQ,QAAoC,CAAC;AAsB1D,eAAO,MAAM,aAAa,UAMzB,CAAC;AAMF,eAAO,MAAM,cAAc,EAAE,oBAa5B,CAAC;AAOF,eAAO,MAAM,eAAe,EAAE,sBAM7B,CAAC;AAOF,eAAO,MAAM,aAAa,EAAE,oBAG3B,CAAC;AAOF,eAAO,MAAM,WAAW,EAAE,kBAOzB,CAAC;AAOF,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,MAAM,CAK1C,CAAC;AAKH,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,OACN,CAAC;AAMrC,eAAO,MAAM,gBAAgB;;;;qBAgBV;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,KAAG,OAAO;;;;;;;;8BAiBnC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE;YAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;SAAE,CAAA;KAAE,WAChD;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE;;;;;;;;CAoB3E,CAAC;AAOF,eAAO,MAAM,mBAAmB,QAAO,MAKtC,CAAC;AAMF,eAAO,MAAM,kBAAkB;;;CAK9B,CAAC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.healthCheckOptions = exports.resolveRateLimitMax = exports.rateLimitOptions = exports.isProbePath = exports.PROBE_PATHS = exports.corsOptions = exports.helmetOptions = exports.compressOptions = exports.fastifyOptions = exports.consoleOutput = exports.PROTOCOL = exports.PORT = exports.HOST = exports.LIB_VERSION = void 0;
|
|
4
|
+
const pack = require("../../package.json");
|
|
5
|
+
exports.LIB_VERSION = JSON.stringify(pack.version);
|
|
6
|
+
exports.HOST = process.env["HOST"] ?? "localhost";
|
|
7
|
+
exports.PORT = process.env["PORT"] ?? 3000;
|
|
8
|
+
exports.PROTOCOL = process.env["PROTOCOL"] ?? "http";
|
|
9
|
+
const parseTrustProxy = () => {
|
|
10
|
+
const raw = process.env["TRUSTED_PROXY_CIDRS"];
|
|
11
|
+
if (!raw)
|
|
12
|
+
return false;
|
|
13
|
+
return raw
|
|
14
|
+
.split(",")
|
|
15
|
+
.map((entry) => entry.trim())
|
|
16
|
+
.filter((entry) => entry.length > 0);
|
|
17
|
+
};
|
|
18
|
+
exports.consoleOutput = [
|
|
19
|
+
`\n → protocol: ${exports.PROTOCOL}`,
|
|
20
|
+
`\n → hostname: ${exports.HOST}`,
|
|
21
|
+
`\n → port: ${exports.PORT}`,
|
|
22
|
+
`\n → version: ${exports.LIB_VERSION}`,
|
|
23
|
+
"\n",
|
|
24
|
+
];
|
|
25
|
+
exports.fastifyOptions = {
|
|
26
|
+
bodyLimit: 256 * 1024,
|
|
27
|
+
caseSensitive: true,
|
|
28
|
+
connectionTimeout: 30_000,
|
|
29
|
+
disableRequestLogging: false,
|
|
30
|
+
ignoreTrailingSlash: false,
|
|
31
|
+
keepAliveTimeout: 5000,
|
|
32
|
+
logger: true,
|
|
33
|
+
maxParamLength: 100,
|
|
34
|
+
onConstructorPoisoning: "error",
|
|
35
|
+
onProtoPoisoning: "error",
|
|
36
|
+
return503OnClosing: true,
|
|
37
|
+
trustProxy: parseTrustProxy(),
|
|
38
|
+
};
|
|
39
|
+
exports.compressOptions = {
|
|
40
|
+
global: true,
|
|
41
|
+
threshold: 2048,
|
|
42
|
+
zlibOptions: {
|
|
43
|
+
level: 6,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
exports.helmetOptions = {
|
|
47
|
+
global: true,
|
|
48
|
+
contentSecurityPolicy: false,
|
|
49
|
+
};
|
|
50
|
+
exports.corsOptions = {
|
|
51
|
+
origin: process.env["CORS_ORIGIN"]
|
|
52
|
+
? process.env["CORS_ORIGIN"].split(",").map((o) => o.trim())
|
|
53
|
+
: false,
|
|
54
|
+
methods: ["GET", "POST"],
|
|
55
|
+
allowedHeaders: ["Content-Type", "x-api-key"],
|
|
56
|
+
credentials: true,
|
|
57
|
+
};
|
|
58
|
+
exports.PROBE_PATHS = new Set([
|
|
59
|
+
"/health",
|
|
60
|
+
"/live",
|
|
61
|
+
"/ready",
|
|
62
|
+
"/metrics",
|
|
63
|
+
]);
|
|
64
|
+
const isProbePath = (url) => exports.PROBE_PATHS.has(url.split("?")[0]);
|
|
65
|
+
exports.isProbePath = isProbePath;
|
|
66
|
+
exports.rateLimitOptions = {
|
|
67
|
+
global: true,
|
|
68
|
+
max: 10,
|
|
69
|
+
timeWindow: "1 minute",
|
|
70
|
+
allowList: (req) => (0, exports.isProbePath)(req.url),
|
|
71
|
+
nameSpace: "crypto-server-rate-limit-",
|
|
72
|
+
addHeaders: {
|
|
73
|
+
"x-ratelimit-limit": true,
|
|
74
|
+
"x-ratelimit-remaining": true,
|
|
75
|
+
"x-ratelimit-reset": true,
|
|
76
|
+
"retry-after": true,
|
|
77
|
+
},
|
|
78
|
+
errorResponseBuilder(req, context) {
|
|
79
|
+
req.log.warn(`${req.ip} have been rateLimited`);
|
|
80
|
+
return {
|
|
81
|
+
statusCode: context.statusCode,
|
|
82
|
+
code: context.statusCode,
|
|
83
|
+
error: "Too Many Requests",
|
|
84
|
+
message: `Only ${context.max} requests are allowed per ${context.after}. Try again soon.`,
|
|
85
|
+
date: Date.now(),
|
|
86
|
+
expiresIn: context.ttl,
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
const resolveRateLimitMax = () => {
|
|
91
|
+
const parsed = Number(process.env["RATE_LIMIT_MAX"]);
|
|
92
|
+
return Number.isSafeInteger(parsed) && parsed > 0
|
|
93
|
+
? parsed
|
|
94
|
+
: exports.rateLimitOptions.max;
|
|
95
|
+
};
|
|
96
|
+
exports.resolveRateLimitMax = resolveRateLimitMax;
|
|
97
|
+
exports.healthCheckOptions = {
|
|
98
|
+
healthcheckUrl: "/health",
|
|
99
|
+
exposeUptime: true,
|
|
100
|
+
};
|
|
101
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface Config {
|
|
2
|
+
readonly host: string;
|
|
3
|
+
readonly port: number;
|
|
4
|
+
readonly protocol: "http" | "https";
|
|
5
|
+
readonly nodeEnv: "development" | "production" | "test";
|
|
6
|
+
readonly logLevel: "error" | "warn" | "info" | "debug";
|
|
7
|
+
readonly apiKey: string | undefined;
|
|
8
|
+
readonly corsOrigins: string[] | false;
|
|
9
|
+
readonly trustProxy: string[] | false;
|
|
10
|
+
readonly keyDir: string | undefined;
|
|
11
|
+
readonly keyOutDir: string | undefined;
|
|
12
|
+
readonly shutdownTimeoutMs: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function loadConfig(): Config;
|
|
15
|
+
export declare const config: Config;
|
|
16
|
+
//# sourceMappingURL=env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/config/env.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,MAAM;IAErB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAEpC,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,YAAY,GAAG,MAAM,CAAC;IAExD,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IAEvD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAEpC,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAEvC,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAEtC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAEpC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAEvC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACpC;AA8BD,wBAAgB,UAAU,IAAI,MAAM,CAuDnC;AAKD,eAAO,MAAM,MAAM,EAAE,MAAqB,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.config = void 0;
|
|
4
|
+
exports.loadConfig = loadConfig;
|
|
5
|
+
const auth_policy_1 = require("./auth-policy");
|
|
6
|
+
function optional(name, fallback) {
|
|
7
|
+
return process.env[name] ?? fallback;
|
|
8
|
+
}
|
|
9
|
+
function parsePort(val) {
|
|
10
|
+
const port = parseInt(val, 10);
|
|
11
|
+
if (!Number.isFinite(port) || port < 0 || port > 65535) {
|
|
12
|
+
throw new Error(`Invalid PORT: ${val} (must be 0–65535)`);
|
|
13
|
+
}
|
|
14
|
+
return port;
|
|
15
|
+
}
|
|
16
|
+
function parseCsvList(val) {
|
|
17
|
+
if (!val)
|
|
18
|
+
return false;
|
|
19
|
+
const entries = val
|
|
20
|
+
.split(",")
|
|
21
|
+
.map((s) => s.trim())
|
|
22
|
+
.filter((s) => s.length > 0);
|
|
23
|
+
return entries.length > 0 ? entries : false;
|
|
24
|
+
}
|
|
25
|
+
function loadConfig() {
|
|
26
|
+
const errors = [];
|
|
27
|
+
const host = optional("HOST", "localhost");
|
|
28
|
+
const portStr = optional("PORT", "3000");
|
|
29
|
+
const protocol = optional("PROTOCOL", "http");
|
|
30
|
+
const nodeEnv = optional("NODE_ENV", "development");
|
|
31
|
+
const logLevel = optional("LOG_LEVEL", "info");
|
|
32
|
+
if (!["http", "https"].includes(protocol)) {
|
|
33
|
+
errors.push(`PROTOCOL must be "http" or "https", got "${protocol}"`);
|
|
34
|
+
}
|
|
35
|
+
if (!["development", "production", "test"].includes(nodeEnv)) {
|
|
36
|
+
errors.push(`NODE_ENV must be "development", "production", or "test", got "${nodeEnv}"`);
|
|
37
|
+
}
|
|
38
|
+
if (!["error", "warn", "info", "debug"].includes(logLevel)) {
|
|
39
|
+
errors.push(`LOG_LEVEL must be one of error|warn|info|debug, got "${logLevel}"`);
|
|
40
|
+
}
|
|
41
|
+
const authError = (0, auth_policy_1.authConfigError)(process.env);
|
|
42
|
+
if (authError)
|
|
43
|
+
errors.push(authError);
|
|
44
|
+
let port = 3000;
|
|
45
|
+
try {
|
|
46
|
+
port = parsePort(portStr);
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
errors.push(e.message);
|
|
50
|
+
}
|
|
51
|
+
if (errors.length > 0) {
|
|
52
|
+
const msg = [
|
|
53
|
+
"Configuration validation failed:",
|
|
54
|
+
...errors.map((e) => ` - ${e}`),
|
|
55
|
+
].join("\n");
|
|
56
|
+
console.error(msg);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
return Object.freeze({
|
|
60
|
+
host,
|
|
61
|
+
port,
|
|
62
|
+
protocol,
|
|
63
|
+
nodeEnv,
|
|
64
|
+
logLevel,
|
|
65
|
+
apiKey: process.env["CRYPTO_API_KEY"] || undefined,
|
|
66
|
+
corsOrigins: parseCsvList(process.env["CORS_ORIGIN"]),
|
|
67
|
+
trustProxy: parseCsvList(process.env["TRUSTED_PROXY_CIDRS"]),
|
|
68
|
+
keyDir: process.env["CRYPTO_KEY_DIR"] || undefined,
|
|
69
|
+
keyOutDir: process.env["CRYPTO_KEY_OUT_DIR"] || undefined,
|
|
70
|
+
shutdownTimeoutMs: parseInt(optional("SHUTDOWN_TIMEOUT_MS", "30000"), 10),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
exports.config = loadConfig();
|
|
74
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { FastifyInstance } from "fastify";
|
|
2
|
+
import type { LicenseTier, MeteringCheckResult, TenantQuota } from "./types";
|
|
3
|
+
export declare const TIER_QUOTAS: Record<LicenseTier, TenantQuota>;
|
|
4
|
+
export declare const DEFAULT_MAX_TENANTS = 10000;
|
|
5
|
+
export declare class MeteringEngine {
|
|
6
|
+
private readonly maxTenants;
|
|
7
|
+
private readonly tenants;
|
|
8
|
+
constructor(maxTenants?: number);
|
|
9
|
+
get size(): number;
|
|
10
|
+
resolveTier(claim?: unknown): LicenseTier;
|
|
11
|
+
private getUsage;
|
|
12
|
+
private evictIfFull;
|
|
13
|
+
private refillTokens;
|
|
14
|
+
checkRequest(tenantKey: string, tier: LicenseTier, contentLength?: number, nowMs?: number): MeteringCheckResult;
|
|
15
|
+
reset(): void;
|
|
16
|
+
}
|
|
17
|
+
export declare const defaultMeteringEngine: MeteringEngine;
|
|
18
|
+
export declare const registerMetering: (app: FastifyInstance, engine?: MeteringEngine) => void;
|
|
19
|
+
//# sourceMappingURL=metering.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metering.d.ts","sourceRoot":"","sources":["../../src/enterprise/metering.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AAE7E,OAAO,KAAK,EACV,WAAW,EACX,mBAAmB,EACnB,WAAW,EAEZ,MAAM,SAAS,CAAC;AAWjB,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,WAAW,CAsBxD,CAAC;AAGF,eAAO,MAAM,mBAAmB,QAAS,CAAC;AAY1C,qBAAa,cAAc;IAKvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAJ7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;gBAIvC,UAAU,GAAE,MAA4B;IAI3D,IAAW,IAAI,IAAI,MAAM,CAExB;IASM,WAAW,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,WAAW;IAOhD,OAAO,CAAC,QAAQ;IAwBhB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,YAAY;IAqBb,YAAY,CACjB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,WAAW,EACjB,aAAa,CAAC,EAAE,MAAM,EACtB,KAAK,GAAE,MAAmB,GACzB,mBAAmB;IA4Df,KAAK,IAAI,IAAI;CAGrB;AAGD,eAAO,MAAM,qBAAqB,gBAAuB,CAAC;AA6B1D,eAAO,MAAM,gBAAgB,GAC3B,KAAK,eAAe,EACpB,SAAQ,cAAqC,KAC5C,IAgCF,CAAC"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerMetering = exports.defaultMeteringEngine = exports.MeteringEngine = exports.DEFAULT_MAX_TENANTS = exports.TIER_QUOTAS = void 0;
|
|
4
|
+
const constants_1 = require("../config/constants");
|
|
5
|
+
exports.TIER_QUOTAS = {
|
|
6
|
+
community: {
|
|
7
|
+
tier: "community",
|
|
8
|
+
maxRequestsPerMinute: 60,
|
|
9
|
+
maxBytesPerPayload: 1024 * 1024,
|
|
10
|
+
allowQuantumOperations: true,
|
|
11
|
+
allowComplianceAudit: true,
|
|
12
|
+
},
|
|
13
|
+
enterprise: {
|
|
14
|
+
tier: "enterprise",
|
|
15
|
+
maxRequestsPerMinute: 1000,
|
|
16
|
+
maxBytesPerPayload: 10 * 1024 * 1024,
|
|
17
|
+
allowQuantumOperations: true,
|
|
18
|
+
allowComplianceAudit: true,
|
|
19
|
+
},
|
|
20
|
+
sovereign: {
|
|
21
|
+
tier: "sovereign",
|
|
22
|
+
maxRequestsPerMinute: 10000,
|
|
23
|
+
maxBytesPerPayload: 50 * 1024 * 1024,
|
|
24
|
+
allowQuantumOperations: true,
|
|
25
|
+
allowComplianceAudit: true,
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
exports.DEFAULT_MAX_TENANTS = 10_000;
|
|
29
|
+
const ANONYMOUS_TENANT = "anonymous";
|
|
30
|
+
class MeteringEngine {
|
|
31
|
+
maxTenants;
|
|
32
|
+
tenants = new Map();
|
|
33
|
+
constructor(maxTenants = exports.DEFAULT_MAX_TENANTS) {
|
|
34
|
+
this.maxTenants = maxTenants;
|
|
35
|
+
}
|
|
36
|
+
get size() {
|
|
37
|
+
return this.tenants.size;
|
|
38
|
+
}
|
|
39
|
+
resolveTier(claim) {
|
|
40
|
+
return typeof claim === "string" && Object.hasOwn(exports.TIER_QUOTAS, claim)
|
|
41
|
+
? claim
|
|
42
|
+
: "community";
|
|
43
|
+
}
|
|
44
|
+
getUsage(tenantKey, tier, nowMs) {
|
|
45
|
+
let usage = this.tenants.get(tenantKey);
|
|
46
|
+
if (usage) {
|
|
47
|
+
this.tenants.delete(tenantKey);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
this.evictIfFull();
|
|
51
|
+
usage = {
|
|
52
|
+
tokens: exports.TIER_QUOTAS[tier].maxRequestsPerMinute,
|
|
53
|
+
lastRefillMs: nowMs,
|
|
54
|
+
totalRequests: 0,
|
|
55
|
+
totalBytes: 0,
|
|
56
|
+
tier,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
this.tenants.set(tenantKey, usage);
|
|
60
|
+
return usage;
|
|
61
|
+
}
|
|
62
|
+
evictIfFull() {
|
|
63
|
+
if (this.tenants.size < this.maxTenants)
|
|
64
|
+
return;
|
|
65
|
+
const oldest = this.tenants.keys().next().value;
|
|
66
|
+
this.tenants.delete(oldest);
|
|
67
|
+
}
|
|
68
|
+
refillTokens(usage, quota, nowMs) {
|
|
69
|
+
const elapsedMs = nowMs - usage.lastRefillMs;
|
|
70
|
+
if (elapsedMs <= 0)
|
|
71
|
+
return;
|
|
72
|
+
const refillRatePerMs = quota.maxRequestsPerMinute / 60000;
|
|
73
|
+
const tokensToAdd = elapsedMs * refillRatePerMs;
|
|
74
|
+
usage.tokens = Math.min(quota.maxRequestsPerMinute, usage.tokens + tokensToAdd);
|
|
75
|
+
usage.lastRefillMs = nowMs;
|
|
76
|
+
}
|
|
77
|
+
checkRequest(tenantKey, tier, contentLength, nowMs = Date.now()) {
|
|
78
|
+
const quota = exports.TIER_QUOTAS[tier];
|
|
79
|
+
const usage = this.getUsage(tenantKey, tier, nowMs);
|
|
80
|
+
this.refillTokens(usage, quota, nowMs);
|
|
81
|
+
if (contentLength !== undefined &&
|
|
82
|
+
contentLength > quota.maxBytesPerPayload) {
|
|
83
|
+
const resetSeconds = Math.ceil((60000 - ((nowMs - usage.lastRefillMs) % 60000)) / 1000);
|
|
84
|
+
return {
|
|
85
|
+
allowed: false,
|
|
86
|
+
limit: quota.maxRequestsPerMinute,
|
|
87
|
+
remaining: Math.floor(Math.max(0, usage.tokens)),
|
|
88
|
+
resetSeconds: Math.max(1, resetSeconds),
|
|
89
|
+
tier,
|
|
90
|
+
error: `Payload size (${contentLength} bytes) exceeds tier limit of ${quota.maxBytesPerPayload} bytes`,
|
|
91
|
+
statusCode: 413,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (usage.tokens < 1) {
|
|
95
|
+
const resetSeconds = Math.ceil((60000 - ((nowMs - usage.lastRefillMs) % 60000)) / 1000);
|
|
96
|
+
return {
|
|
97
|
+
allowed: false,
|
|
98
|
+
limit: quota.maxRequestsPerMinute,
|
|
99
|
+
remaining: 0,
|
|
100
|
+
resetSeconds: Math.max(1, resetSeconds),
|
|
101
|
+
tier,
|
|
102
|
+
error: `Tenant rate limit exceeded for tier '${tier}'. Limit: ${quota.maxRequestsPerMinute} req/min.`,
|
|
103
|
+
statusCode: 429,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
usage.tokens -= 1;
|
|
107
|
+
usage.totalRequests += 1;
|
|
108
|
+
if (contentLength !== undefined && contentLength > 0) {
|
|
109
|
+
usage.totalBytes += contentLength;
|
|
110
|
+
}
|
|
111
|
+
const resetSeconds = Math.ceil((60000 - ((nowMs - usage.lastRefillMs) % 60000)) / 1000);
|
|
112
|
+
return {
|
|
113
|
+
allowed: true,
|
|
114
|
+
limit: quota.maxRequestsPerMinute,
|
|
115
|
+
remaining: Math.floor(Math.max(0, usage.tokens)),
|
|
116
|
+
resetSeconds: Math.max(1, resetSeconds),
|
|
117
|
+
tier,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
reset() {
|
|
121
|
+
this.tenants.clear();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
exports.MeteringEngine = MeteringEngine;
|
|
125
|
+
exports.defaultMeteringEngine = new MeteringEngine();
|
|
126
|
+
const isUnmeteredPath = (url) => {
|
|
127
|
+
const pathname = url.split("?")[0];
|
|
128
|
+
return ((0, constants_1.isProbePath)(pathname) ||
|
|
129
|
+
pathname.startsWith("/docs") ||
|
|
130
|
+
pathname === "/favicon.ico");
|
|
131
|
+
};
|
|
132
|
+
const parseContentLength = (raw) => raw !== undefined && !Number.isNaN(Number(raw)) ? Number(raw) : undefined;
|
|
133
|
+
const registerMetering = (app, engine = new MeteringEngine()) => {
|
|
134
|
+
app.addHook("preHandler", async (request, reply) => {
|
|
135
|
+
if (isUnmeteredPath(request.url))
|
|
136
|
+
return;
|
|
137
|
+
const auth = request.auth;
|
|
138
|
+
const result = engine.checkRequest(auth?.sub ?? ANONYMOUS_TENANT, engine.resolveTier(auth?.tier), parseContentLength(request.headers["content-length"]));
|
|
139
|
+
reply.header("X-Tenant-RateLimit-Limit", result.limit);
|
|
140
|
+
reply.header("X-Tenant-RateLimit-Remaining", result.remaining);
|
|
141
|
+
reply.header("X-Tenant-RateLimit-Reset", result.resetSeconds);
|
|
142
|
+
reply.header("X-License-Tier", result.tier);
|
|
143
|
+
if (!result.allowed) {
|
|
144
|
+
const code = result.statusCode;
|
|
145
|
+
if (code === 429)
|
|
146
|
+
reply.header("Retry-After", result.resetSeconds);
|
|
147
|
+
return reply.code(code).send({
|
|
148
|
+
statusCode: code,
|
|
149
|
+
error: code === 413 ? "Payload Too Large" : "Too Many Requests",
|
|
150
|
+
message: result.error,
|
|
151
|
+
tier: result.tier,
|
|
152
|
+
limit: result.limit,
|
|
153
|
+
resetSeconds: result.resetSeconds,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
};
|
|
158
|
+
exports.registerMetering = registerMetering;
|
|
159
|
+
//# sourceMappingURL=metering.js.map
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export type LicenseTier = "community" | "enterprise" | "sovereign";
|
|
2
|
+
export interface TenantQuota {
|
|
3
|
+
readonly tier: LicenseTier;
|
|
4
|
+
readonly maxRequestsPerMinute: number;
|
|
5
|
+
readonly maxBytesPerPayload: number;
|
|
6
|
+
readonly allowQuantumOperations: boolean;
|
|
7
|
+
readonly allowComplianceAudit: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface TenantUsage {
|
|
10
|
+
tokens: number;
|
|
11
|
+
lastRefillMs: number;
|
|
12
|
+
totalRequests: number;
|
|
13
|
+
totalBytes: number;
|
|
14
|
+
tier: LicenseTier;
|
|
15
|
+
}
|
|
16
|
+
export interface MeteringCheckResult {
|
|
17
|
+
readonly allowed: boolean;
|
|
18
|
+
readonly limit: number;
|
|
19
|
+
readonly remaining: number;
|
|
20
|
+
readonly resetSeconds: number;
|
|
21
|
+
readonly tier: LicenseTier;
|
|
22
|
+
readonly error?: string;
|
|
23
|
+
readonly statusCode?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface AlgorithmDeprecationRecord {
|
|
26
|
+
readonly algorithm: string;
|
|
27
|
+
readonly category: "asymmetric" | "symmetric" | "hash" | "pqc";
|
|
28
|
+
readonly sunsetDate: string;
|
|
29
|
+
readonly recommendedMigration: string;
|
|
30
|
+
}
|
|
31
|
+
export interface CryptographicAssetRecord {
|
|
32
|
+
readonly package: string;
|
|
33
|
+
readonly version: string;
|
|
34
|
+
readonly status: "production" | "hardened";
|
|
35
|
+
readonly fipsCompliance: string;
|
|
36
|
+
}
|
|
37
|
+
export interface DoraComplianceScorecard {
|
|
38
|
+
readonly standard: "DORA (EU 2022/2554)";
|
|
39
|
+
readonly article: "Article 9 & Article 13";
|
|
40
|
+
readonly complianceScore: number;
|
|
41
|
+
readonly status: "Compliant" | "Substantially Compliant" | "Action Required";
|
|
42
|
+
readonly quantumResistanceRatio: number;
|
|
43
|
+
readonly activePrimitivesCount: number;
|
|
44
|
+
readonly postQuantumPrimitivesCount: number;
|
|
45
|
+
readonly algorithmDeprecationSchedule: ReadonlyArray<AlgorithmDeprecationRecord>;
|
|
46
|
+
readonly cryptographicInventory: ReadonlyArray<CryptographicAssetRecord>;
|
|
47
|
+
readonly timestamp: string;
|
|
48
|
+
}
|
|
49
|
+
export interface CbomPayload {
|
|
50
|
+
readonly bomFormat: "CycloneDX";
|
|
51
|
+
readonly specVersion: "1.6";
|
|
52
|
+
readonly serialNumber: string;
|
|
53
|
+
readonly version: number;
|
|
54
|
+
readonly metadata: {
|
|
55
|
+
readonly timestamp: string;
|
|
56
|
+
readonly tools: ReadonlyArray<{
|
|
57
|
+
readonly vendor: string;
|
|
58
|
+
readonly name: string;
|
|
59
|
+
readonly version: string;
|
|
60
|
+
}>;
|
|
61
|
+
readonly component: {
|
|
62
|
+
readonly type: "framework";
|
|
63
|
+
readonly name: string;
|
|
64
|
+
readonly version: string;
|
|
65
|
+
readonly description: string;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
readonly components: ReadonlyArray<{
|
|
69
|
+
readonly type: "cryptographic-asset";
|
|
70
|
+
readonly name: string;
|
|
71
|
+
readonly version: string;
|
|
72
|
+
readonly properties: ReadonlyArray<{
|
|
73
|
+
readonly name: string;
|
|
74
|
+
readonly value: string;
|
|
75
|
+
}>;
|
|
76
|
+
}>;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/enterprise/types.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,82 +1,46 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
4
|
};
|
|
28
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const fastify_healthcheck_1 = __importDefault(require("fastify-healthcheck"));
|
|
6
|
+
const telemetry_1 = require("./lib/telemetry");
|
|
7
|
+
(0, telemetry_1.initTelemetry)();
|
|
8
|
+
const env_1 = require("./config/env");
|
|
9
|
+
const server_1 = require("./server");
|
|
34
10
|
const logger_1 = __importDefault(require("./lib/logger"));
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
logger_1.default.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
});
|
|
66
|
-
app.get("/", async () => {
|
|
67
|
-
return { hello: "Hello Crypto Server!" };
|
|
68
|
-
});
|
|
69
|
-
(0, routes_1.default)(app);
|
|
70
|
-
const start = async () => {
|
|
71
|
-
try {
|
|
72
|
-
await app.listen(PORT, HOST).then(() => {
|
|
73
|
-
logger_1.default.info(`Server listening on http://${HOST}:${PORT}/`);
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
catch (err) {
|
|
77
|
-
app.log.error(err);
|
|
78
|
-
process.exit(1);
|
|
79
|
-
}
|
|
11
|
+
const main = async () => {
|
|
12
|
+
const server = await (0, server_1.init)();
|
|
13
|
+
await server.ready();
|
|
14
|
+
await server.listen({ port: env_1.config.port, host: env_1.config.host });
|
|
15
|
+
logger_1.default.info(`Server listening on ${env_1.config.protocol}://${env_1.config.host}:${env_1.config.port}/`);
|
|
16
|
+
logger_1.default.info(`Swagger docs at ${env_1.config.protocol}://${env_1.config.host}:${env_1.config.port}/docs`);
|
|
17
|
+
let isShuttingDown = false;
|
|
18
|
+
const shutdown = async (signal) => {
|
|
19
|
+
if (isShuttingDown)
|
|
20
|
+
return;
|
|
21
|
+
isShuttingDown = true;
|
|
22
|
+
logger_1.default.info(`Received ${signal} — shutting down gracefully...`);
|
|
23
|
+
const forceTimer = setTimeout(() => {
|
|
24
|
+
logger_1.default.error(`Shutdown timed out after ${env_1.config.shutdownTimeoutMs}ms — forcing exit`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}, env_1.config.shutdownTimeoutMs);
|
|
27
|
+
forceTimer.unref();
|
|
28
|
+
try {
|
|
29
|
+
await server.close();
|
|
30
|
+
await (0, telemetry_1.shutdownTelemetry)();
|
|
31
|
+
logger_1.default.info("All connections drained. Goodbye.");
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
logger_1.default.error("Error during shutdown", err instanceof Error ? err : undefined);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
40
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
80
41
|
};
|
|
81
|
-
|
|
42
|
+
main().catch((err) => {
|
|
43
|
+
logger_1.default.error(`Server failed to start: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
});
|
|
82
46
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyRequest, FastifyReply } from "fastify";
|
|
2
|
+
export declare const SCOPES: readonly ["crypto:encrypt", "crypto:decrypt", "crypto:sign", "crypto:verify", "crypto:hash", "crypto:kdf", "crypto:keys", "crypto:admin"];
|
|
3
|
+
export type Scope = (typeof SCOPES)[number];
|
|
4
|
+
export interface AuthPayload {
|
|
5
|
+
sub: string;
|
|
6
|
+
scopes: Scope[];
|
|
7
|
+
iat?: number;
|
|
8
|
+
exp?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function registerAuth(app: FastifyInstance): Promise<void>;
|
|
11
|
+
export declare function authenticate(request: FastifyRequest, reply: FastifyReply): Promise<AuthPayload | null>;
|
|
12
|
+
export declare function hasScope(payload: AuthPayload, required: Scope): boolean;
|
|
13
|
+
export declare function requireScope(payload: AuthPayload, scope: Scope, reply: FastifyReply): boolean;
|
|
14
|
+
//# sourceMappingURL=auth.d.ts.map
|