@prosopo/api-express-router 3.1.50 → 3.1.51
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/.turbo/turbo-build$colon$cjs.log +14 -12
- package/.turbo/turbo-build$colon$tsc.log +21 -21
- package/.turbo/turbo-build.log +16 -13
- package/CHANGELOG.md +17 -0
- package/dist/_virtual/_rolldown/runtime.js +3 -0
- package/dist/apiExpressRouterFactory.js +17 -27
- package/dist/cjs/apiExpressRouterFactory.cjs +18 -28
- package/dist/cjs/endpointAdapter/apiExpressDefaultEndpointAdapter.cjs +29 -35
- package/dist/cjs/errorHandler.cjs +10 -10
- package/dist/cjs/index.cjs +14 -13
- package/dist/cjs/middlewares/authMiddleware.cjs +54 -56
- package/dist/cjs/middlewares/requestLoggerMiddleware.cjs +57 -59
- package/dist/endpointAdapter/apiExpressDefaultEndpointAdapter.js +26 -33
- package/dist/errorHandler.js +10 -10
- package/dist/index.js +8 -12
- package/dist/middlewares/authMiddleware.js +54 -57
- package/dist/middlewares/requestLoggerMiddleware.js +56 -58
- package/package.json +10 -10
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,63 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
if (headerName in req.headers && req.headers[headerName]) {
|
|
7
|
-
return req.headers[headerName].toString();
|
|
8
|
-
}
|
|
9
|
-
return void 0;
|
|
1
|
+
let _prosopo_logger = require("@prosopo/logger");
|
|
2
|
+
let uuid = require("uuid");
|
|
3
|
+
//#region src/middlewares/requestLoggerMiddleware.ts
|
|
4
|
+
var getHeaderValue = (req, headerName) => {
|
|
5
|
+
if (headerName in req.headers && req.headers[headerName]) return req.headers[headerName].toString();
|
|
10
6
|
};
|
|
11
|
-
|
|
7
|
+
var HEALTH_CHECK_PATHS = /* @__PURE__ */ new Set([
|
|
8
|
+
"/healthz",
|
|
9
|
+
"/health",
|
|
10
|
+
"/readyz"
|
|
11
|
+
]);
|
|
12
12
|
function requestLoggerMiddleware(env) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
res.on("close", () => emitFinish("close"));
|
|
60
|
-
next();
|
|
61
|
-
};
|
|
13
|
+
return (req, res, next) => {
|
|
14
|
+
const requestId = req.headers["x-request-id"] || `e-${(0, uuid.v4)()}`;
|
|
15
|
+
const user = getHeaderValue(req, "prosopo-user");
|
|
16
|
+
const siteKey = getHeaderValue(req, "prosopo-site-key");
|
|
17
|
+
const sessionId = req.body?.sessionId ? req.body.sessionId : void 0;
|
|
18
|
+
const logger = (0, _prosopo_logger.getLogger)((0, _prosopo_logger.parseLogLevel)(env.config.logLevel), "provider:request").with({
|
|
19
|
+
requestId,
|
|
20
|
+
...user ? { user } : {},
|
|
21
|
+
...siteKey ? { siteKey } : {},
|
|
22
|
+
...sessionId ? { sessionId } : {}
|
|
23
|
+
});
|
|
24
|
+
req.logger = logger;
|
|
25
|
+
req.requestId = requestId;
|
|
26
|
+
res.setHeader("x-request-id", requestId);
|
|
27
|
+
if (HEALTH_CHECK_PATHS.has(req.path)) {
|
|
28
|
+
next();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const startNs = process.hrtime.bigint();
|
|
32
|
+
logger.info(() => ({
|
|
33
|
+
msg: "Request received",
|
|
34
|
+
data: {
|
|
35
|
+
method: req.method,
|
|
36
|
+
path: req.path
|
|
37
|
+
}
|
|
38
|
+
}));
|
|
39
|
+
let finished = false;
|
|
40
|
+
const emitFinish = (outcome) => {
|
|
41
|
+
if (finished) return;
|
|
42
|
+
finished = true;
|
|
43
|
+
const durationMs = Number(process.hrtime.bigint() - startNs) / 1e6;
|
|
44
|
+
logger.info(() => ({
|
|
45
|
+
msg: "Response sent",
|
|
46
|
+
data: {
|
|
47
|
+
method: req.method,
|
|
48
|
+
path: req.path,
|
|
49
|
+
status: res.statusCode,
|
|
50
|
+
durationMs,
|
|
51
|
+
outcome
|
|
52
|
+
}
|
|
53
|
+
}));
|
|
54
|
+
};
|
|
55
|
+
res.on("finish", () => emitFinish("finish"));
|
|
56
|
+
res.on("close", () => emitFinish("close"));
|
|
57
|
+
next();
|
|
58
|
+
};
|
|
62
59
|
}
|
|
60
|
+
//#endregion
|
|
63
61
|
exports.requestLoggerMiddleware = requestLoggerMiddleware;
|
|
@@ -1,36 +1,29 @@
|
|
|
1
1
|
import { ProsopoApiError } from "@prosopo/common";
|
|
2
2
|
import { stringifyBigInts } from "@prosopo/util";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
request.logger.error(() => ({
|
|
28
|
-
err: error
|
|
29
|
-
}));
|
|
30
|
-
response.status(500).send("An internal server error occurred.");
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
export {
|
|
35
|
-
ApiExpressDefaultEndpointAdapter
|
|
3
|
+
//#region src/endpointAdapter/apiExpressDefaultEndpointAdapter.ts
|
|
4
|
+
var ApiExpressDefaultEndpointAdapter = class {
|
|
5
|
+
constructor(logLevel, errorStatusCode) {
|
|
6
|
+
this.logLevel = logLevel;
|
|
7
|
+
this.errorStatusCode = errorStatusCode;
|
|
8
|
+
}
|
|
9
|
+
async handleRequest(endpoint, request, response, next) {
|
|
10
|
+
let args;
|
|
11
|
+
try {
|
|
12
|
+
args = endpoint.getRequestArgsSchema()?.parse(request.body);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
return next(new ProsopoApiError("API.PARSE_ERROR", { context: {
|
|
15
|
+
code: 400,
|
|
16
|
+
error
|
|
17
|
+
} }));
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const responseObject = stringifyBigInts(await endpoint.processRequest(args, request.logger));
|
|
21
|
+
response.json(responseObject);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
request.logger.error(() => ({ err: error }));
|
|
24
|
+
response.status(500).send("An internal server error occurred.");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
36
27
|
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { ApiExpressDefaultEndpointAdapter };
|
package/dist/errorHandler.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { unwrapError } from "@prosopo/common";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export {
|
|
11
|
-
handleErrors
|
|
2
|
+
//#region src/errorHandler.ts
|
|
3
|
+
var handleErrors = (err, request, response, next) => {
|
|
4
|
+
const { code, statusMessage, jsonError } = unwrapError(err, request.i18n);
|
|
5
|
+
response.statusMessage = statusMessage;
|
|
6
|
+
response.set("content-type", "application/json");
|
|
7
|
+
response.status(code);
|
|
8
|
+
response.send({ error: jsonError });
|
|
9
|
+
response.end();
|
|
12
10
|
};
|
|
11
|
+
//#endregion
|
|
12
|
+
export { handleErrors };
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
|
+
import "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { handleErrors } from "./errorHandler.js";
|
|
1
3
|
import { ApiExpressRouterFactory } from "./apiExpressRouterFactory.js";
|
|
2
4
|
import { ApiExpressDefaultEndpointAdapter } from "./endpointAdapter/apiExpressDefaultEndpointAdapter.js";
|
|
3
|
-
import { handleErrors } from "./errorHandler.js";
|
|
4
5
|
import { authMiddleware, verifySignature } from "./middlewares/authMiddleware.js";
|
|
5
6
|
import { requestLoggerMiddleware } from "./middlewares/requestLoggerMiddleware.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export {
|
|
11
|
-
apiExpressRouterFactory,
|
|
12
|
-
authMiddleware,
|
|
13
|
-
createApiExpressDefaultEndpointAdapter,
|
|
14
|
-
handleErrors,
|
|
15
|
-
requestLoggerMiddleware,
|
|
16
|
-
verifySignature
|
|
7
|
+
//#region src/index.ts
|
|
8
|
+
var apiExpressRouterFactory = new ApiExpressRouterFactory();
|
|
9
|
+
var createApiExpressDefaultEndpointAdapter = (logLevel, errorStatusCode = 500) => {
|
|
10
|
+
return new ApiExpressDefaultEndpointAdapter(logLevel, errorStatusCode);
|
|
17
11
|
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { apiExpressRouterFactory, authMiddleware, createApiExpressDefaultEndpointAdapter, handleErrors, requestLoggerMiddleware, verifySignature };
|
|
@@ -1,61 +1,58 @@
|
|
|
1
|
+
import { ProsopoApiError, ProsopoEnvError } from "@prosopo/common";
|
|
1
2
|
import { hexToU8a } from "@polkadot/util";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
3
|
+
//#region src/middlewares/authMiddleware.ts
|
|
4
|
+
var authMiddleware = (pair, authAccount) => {
|
|
5
|
+
return async (req, res, next) => {
|
|
6
|
+
try {
|
|
7
|
+
const jwt = extractJWT(req);
|
|
8
|
+
if (authAccount?.jwtVerify(jwt).isValid) {
|
|
9
|
+
next();
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (pair?.jwtVerify(jwt).isValid) {
|
|
13
|
+
next();
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
res.status(401).json({ error: new ProsopoEnvError("API.UNAUTHORIZED", { context: {
|
|
17
|
+
i18n: req.i18n,
|
|
18
|
+
code: 401
|
|
19
|
+
} }) });
|
|
20
|
+
return;
|
|
21
|
+
} catch (err) {
|
|
22
|
+
req.logger.error(() => ({
|
|
23
|
+
err,
|
|
24
|
+
msg: "Auth Middleware Error"
|
|
25
|
+
}));
|
|
26
|
+
res.status(401).json({
|
|
27
|
+
error: "Unauthorized",
|
|
28
|
+
message: err
|
|
29
|
+
});
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
28
33
|
};
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
return jwt;
|
|
34
|
+
var extractJWT = (req) => {
|
|
35
|
+
const authHeader = req.headers.Authorization || req.headers.authorization;
|
|
36
|
+
if (!authHeader || typeof authHeader !== "string") throw new ProsopoApiError("GENERAL.MISSING_AUTH_HEADER", { context: {
|
|
37
|
+
error: "Missing Authorization header",
|
|
38
|
+
code: 401
|
|
39
|
+
} });
|
|
40
|
+
const jwt = authHeader.replace("Bearer ", "");
|
|
41
|
+
if (!jwt) throw new ProsopoApiError("GENERAL.INVALID_JWT", { context: {
|
|
42
|
+
error: "Missing JWT",
|
|
43
|
+
code: 400
|
|
44
|
+
} });
|
|
45
|
+
return jwt;
|
|
43
46
|
};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
signature
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
export {
|
|
59
|
-
authMiddleware,
|
|
60
|
-
verifySignature
|
|
47
|
+
var verifySignature = (signature, message, pair) => {
|
|
48
|
+
const u8Sig = hexToU8a(signature);
|
|
49
|
+
if (!pair.verify(message, u8Sig, pair.publicKey)) throw new ProsopoApiError("GENERAL.INVALID_SIGNATURE", { context: {
|
|
50
|
+
error: "Signature verification failed",
|
|
51
|
+
code: 401,
|
|
52
|
+
account: pair.address,
|
|
53
|
+
message,
|
|
54
|
+
signature
|
|
55
|
+
} });
|
|
61
56
|
};
|
|
57
|
+
//#endregion
|
|
58
|
+
export { authMiddleware, verifySignature };
|
|
@@ -1,63 +1,61 @@
|
|
|
1
1
|
import { getLogger, parseLogLevel } from "@prosopo/logger";
|
|
2
2
|
import { v4 } from "uuid";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
return void 0;
|
|
3
|
+
//#region src/middlewares/requestLoggerMiddleware.ts
|
|
4
|
+
var getHeaderValue = (req, headerName) => {
|
|
5
|
+
if (headerName in req.headers && req.headers[headerName]) return req.headers[headerName].toString();
|
|
8
6
|
};
|
|
9
|
-
|
|
7
|
+
var HEALTH_CHECK_PATHS = /* @__PURE__ */ new Set([
|
|
8
|
+
"/healthz",
|
|
9
|
+
"/health",
|
|
10
|
+
"/readyz"
|
|
11
|
+
]);
|
|
10
12
|
function requestLoggerMiddleware(env) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
res.on("close", () => emitFinish("close"));
|
|
58
|
-
next();
|
|
59
|
-
};
|
|
13
|
+
return (req, res, next) => {
|
|
14
|
+
const requestId = req.headers["x-request-id"] || `e-${v4()}`;
|
|
15
|
+
const user = getHeaderValue(req, "prosopo-user");
|
|
16
|
+
const siteKey = getHeaderValue(req, "prosopo-site-key");
|
|
17
|
+
const sessionId = req.body?.sessionId ? req.body.sessionId : void 0;
|
|
18
|
+
const logger = getLogger(parseLogLevel(env.config.logLevel), "provider:request").with({
|
|
19
|
+
requestId,
|
|
20
|
+
...user ? { user } : {},
|
|
21
|
+
...siteKey ? { siteKey } : {},
|
|
22
|
+
...sessionId ? { sessionId } : {}
|
|
23
|
+
});
|
|
24
|
+
req.logger = logger;
|
|
25
|
+
req.requestId = requestId;
|
|
26
|
+
res.setHeader("x-request-id", requestId);
|
|
27
|
+
if (HEALTH_CHECK_PATHS.has(req.path)) {
|
|
28
|
+
next();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const startNs = process.hrtime.bigint();
|
|
32
|
+
logger.info(() => ({
|
|
33
|
+
msg: "Request received",
|
|
34
|
+
data: {
|
|
35
|
+
method: req.method,
|
|
36
|
+
path: req.path
|
|
37
|
+
}
|
|
38
|
+
}));
|
|
39
|
+
let finished = false;
|
|
40
|
+
const emitFinish = (outcome) => {
|
|
41
|
+
if (finished) return;
|
|
42
|
+
finished = true;
|
|
43
|
+
const durationMs = Number(process.hrtime.bigint() - startNs) / 1e6;
|
|
44
|
+
logger.info(() => ({
|
|
45
|
+
msg: "Response sent",
|
|
46
|
+
data: {
|
|
47
|
+
method: req.method,
|
|
48
|
+
path: req.path,
|
|
49
|
+
status: res.statusCode,
|
|
50
|
+
durationMs,
|
|
51
|
+
outcome
|
|
52
|
+
}
|
|
53
|
+
}));
|
|
54
|
+
};
|
|
55
|
+
res.on("finish", () => emitFinish("finish"));
|
|
56
|
+
res.on("close", () => emitFinish("close"));
|
|
57
|
+
next();
|
|
58
|
+
};
|
|
60
59
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
};
|
|
60
|
+
//#endregion
|
|
61
|
+
export { requestLoggerMiddleware };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prosopo/api-express-router",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.51",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@polkadot/util": "13.5.7",
|
|
29
|
-
"@prosopo/api-route": "2.6.
|
|
30
|
-
"@prosopo/common": "3.1.
|
|
31
|
-
"@prosopo/logger": "2.0.
|
|
32
|
-
"@prosopo/env": "3.6.
|
|
33
|
-
"@prosopo/locale": "3.2.
|
|
34
|
-
"@prosopo/types": "4.
|
|
35
|
-
"@prosopo/util": "3.3.
|
|
29
|
+
"@prosopo/api-route": "2.6.53",
|
|
30
|
+
"@prosopo/common": "3.1.48",
|
|
31
|
+
"@prosopo/logger": "2.0.4",
|
|
32
|
+
"@prosopo/env": "3.6.20",
|
|
33
|
+
"@prosopo/locale": "3.2.8",
|
|
34
|
+
"@prosopo/types": "4.10.0",
|
|
35
|
+
"@prosopo/util": "3.3.5",
|
|
36
36
|
"@prosopo/util-crypto": "13.5.30",
|
|
37
37
|
"dotenv": "16.4.5",
|
|
38
38
|
"express": "4.21.2",
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"zod": "3.23.8"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@prosopo/config": "3.3.
|
|
44
|
+
"@prosopo/config": "3.3.5",
|
|
45
45
|
"@types/node": "22.10.2",
|
|
46
|
-
"vitest": "
|
|
46
|
+
"vitest": "4.1.10"
|
|
47
47
|
},
|
|
48
48
|
"author": "PROSOPO LIMITED <info@prosopo.io>",
|
|
49
49
|
"license": "Apache-2.0",
|