@prosopo/api-express-router 3.0.47 → 3.1.17
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 +10 -6
- package/.turbo/turbo-build$colon$tsc.log +27 -21
- package/.turbo/turbo-build.log +11 -7
- package/CHANGELOG.md +325 -0
- package/dist/cjs/endpointAdapter/apiExpressDefaultEndpointAdapter.cjs +2 -1
- package/dist/cjs/middlewares/requestLoggerMiddleware.cjs +14 -4
- package/dist/endpointAdapter/apiExpressDefaultEndpointAdapter.d.ts +1 -1
- package/dist/endpointAdapter/apiExpressDefaultEndpointAdapter.d.ts.map +1 -1
- package/dist/endpointAdapter/apiExpressDefaultEndpointAdapter.js +2 -1
- package/dist/endpointAdapter/apiExpressDefaultEndpointAdapter.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/middlewares/requestLoggerMiddleware.d.ts.map +1 -1
- package/dist/middlewares/requestLoggerMiddleware.js +11 -1
- package/dist/middlewares/requestLoggerMiddleware.js.map +1 -1
- package/dist/tests/unit/middlewares/authMiddleware.unit.test.js +2 -1
- package/dist/tests/unit/middlewares/authMiddleware.unit.test.js.map +1 -1
- package/express.d.ts +3 -1
- package/package.json +14 -8
- package/src/apiExpressRouterFactory.ts +68 -0
- package/src/endpointAdapter/apiExpressDefaultEndpointAdapter.ts +65 -0
- package/src/endpointAdapter/apiExpressEndpointAdapter.ts +28 -0
- package/src/errorHandler.ts +33 -0
- package/src/index.ts +37 -0
- package/src/middlewares/authMiddleware.ts +93 -0
- package/src/middlewares/requestLoggerMiddleware.ts +55 -0
- package/src/tests/unit/errorHandler.unit.test.ts +190 -0
- package/src/tests/unit/middlewares/authMiddleware.unit.test.ts +183 -0
- package/tsconfig.cjs.json +34 -0
- package/tsconfig.json +41 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsconfig.types.json +9 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authMiddleware.unit.test.js","sourceRoot":"","sources":["../../../../src/tests/unit/middlewares/authMiddleware.unit.test.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,
|
|
1
|
+
{"version":3,"file":"authMiddleware.unit.test.js","sourceRoot":"","sources":["../../../../src/tests/unit/middlewares/authMiddleware.unit.test.ts"],"names":[],"mappings":"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"}
|
package/express.d.ts
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
// See the License for the specific language governing permissions and
|
|
13
13
|
// limitations under the License.
|
|
14
14
|
|
|
15
|
-
import type { Logger } from "@prosopo/
|
|
15
|
+
import type { Logger } from "@prosopo/logger";
|
|
16
|
+
import type { IPInfoResponse } from "@prosopo/types";
|
|
16
17
|
import type { TFunction } from "i18next";
|
|
17
18
|
|
|
18
19
|
declare global {
|
|
@@ -27,6 +28,7 @@ declare global {
|
|
|
27
28
|
ja4: string;
|
|
28
29
|
logger: Logger;
|
|
29
30
|
requestId?: string;
|
|
31
|
+
ipInfo?: IPInfoResponse;
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
34
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prosopo/api-express-router",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.17",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -26,11 +26,12 @@
|
|
|
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/
|
|
32
|
-
"@prosopo/
|
|
33
|
-
"@prosopo/
|
|
29
|
+
"@prosopo/api-route": "2.6.46",
|
|
30
|
+
"@prosopo/common": "3.1.38",
|
|
31
|
+
"@prosopo/logger": "1.0.2",
|
|
32
|
+
"@prosopo/env": "3.5.7",
|
|
33
|
+
"@prosopo/locale": "3.2.4",
|
|
34
|
+
"@prosopo/types": "4.3.0",
|
|
34
35
|
"@prosopo/util-crypto": "13.5.29",
|
|
35
36
|
"dotenv": "16.4.5",
|
|
36
37
|
"express": "4.21.2",
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
"zod": "3.23.8"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
|
-
"@prosopo/config": "3.3.
|
|
43
|
+
"@prosopo/config": "3.3.1",
|
|
43
44
|
"@types/node": "22.10.2",
|
|
44
45
|
"vitest": "3.2.4"
|
|
45
46
|
},
|
|
@@ -49,5 +50,10 @@
|
|
|
49
50
|
"url": "https://github.com/prosopo/captcha/issues"
|
|
50
51
|
},
|
|
51
52
|
"homepage": "https://github.com/prosopo/captcha#readme",
|
|
52
|
-
"sideEffects": false
|
|
53
|
+
"sideEffects": false,
|
|
54
|
+
"repository": {
|
|
55
|
+
"type": "git",
|
|
56
|
+
"url": "git+https://github.com/prosopo/captcha.git",
|
|
57
|
+
"directory": "packages/api-express-router"
|
|
58
|
+
}
|
|
53
59
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import type { ApiRoutes, ApiRoutesProvider } from "@prosopo/api-route";
|
|
16
|
+
import {
|
|
17
|
+
type NextFunction,
|
|
18
|
+
type Request,
|
|
19
|
+
type Response,
|
|
20
|
+
Router,
|
|
21
|
+
} from "express";
|
|
22
|
+
import type { ApiExpressEndpointAdapter } from "./endpointAdapter/apiExpressEndpointAdapter.js";
|
|
23
|
+
import { handleErrors } from "./errorHandler.js";
|
|
24
|
+
|
|
25
|
+
class ApiExpressRouterFactory {
|
|
26
|
+
public createRouter(
|
|
27
|
+
routersProvider: ApiRoutesProvider,
|
|
28
|
+
apiEndpointAdapter: ApiExpressEndpointAdapter,
|
|
29
|
+
): Router {
|
|
30
|
+
const router = Router();
|
|
31
|
+
const apiRoutes = routersProvider.getRoutes();
|
|
32
|
+
|
|
33
|
+
this.registerRoutes(router, apiRoutes, apiEndpointAdapter);
|
|
34
|
+
|
|
35
|
+
// Your error handler should always be at the end of your application stack. Apparently it means not only after all
|
|
36
|
+
// app.use() but also after all your app.get() and app.post() calls.
|
|
37
|
+
// https://stackoverflow.com/a/62358794/1178971
|
|
38
|
+
router.use(handleErrors);
|
|
39
|
+
|
|
40
|
+
return router;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
protected registerRoutes(
|
|
44
|
+
router: Router,
|
|
45
|
+
routes: ApiRoutes,
|
|
46
|
+
apiEndpointAdapter: ApiExpressEndpointAdapter,
|
|
47
|
+
): void {
|
|
48
|
+
for (const [route, endpoint] of Object.entries(routes)) {
|
|
49
|
+
router.post(
|
|
50
|
+
route,
|
|
51
|
+
async (
|
|
52
|
+
request: Request,
|
|
53
|
+
response: Response,
|
|
54
|
+
next: NextFunction,
|
|
55
|
+
): Promise<void> => {
|
|
56
|
+
return await apiEndpointAdapter.handleRequest(
|
|
57
|
+
endpoint,
|
|
58
|
+
request,
|
|
59
|
+
response,
|
|
60
|
+
next,
|
|
61
|
+
);
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { ApiExpressRouterFactory };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import type { ApiEndpoint } from "@prosopo/api-route";
|
|
16
|
+
import { ProsopoApiError } from "@prosopo/common";
|
|
17
|
+
import { type LogLevel, stringifyBigInts } from "@prosopo/logger";
|
|
18
|
+
import type { NextFunction, Request, Response } from "express";
|
|
19
|
+
import type { ZodType } from "zod";
|
|
20
|
+
import type { ApiExpressEndpointAdapter } from "./apiExpressEndpointAdapter.js";
|
|
21
|
+
|
|
22
|
+
class ApiExpressDefaultEndpointAdapter implements ApiExpressEndpointAdapter {
|
|
23
|
+
public constructor(
|
|
24
|
+
private readonly logLevel: LogLevel,
|
|
25
|
+
private readonly errorStatusCode: number,
|
|
26
|
+
) {}
|
|
27
|
+
|
|
28
|
+
public async handleRequest(
|
|
29
|
+
endpoint: ApiEndpoint<ZodType | undefined>,
|
|
30
|
+
request: Request,
|
|
31
|
+
response: Response,
|
|
32
|
+
next: NextFunction,
|
|
33
|
+
): Promise<void> {
|
|
34
|
+
let args: unknown;
|
|
35
|
+
try {
|
|
36
|
+
args = endpoint.getRequestArgsSchema()?.parse(request.body);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
return next(
|
|
39
|
+
new ProsopoApiError("API.PARSE_ERROR", {
|
|
40
|
+
context: { code: 400, error: error },
|
|
41
|
+
}),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const apiEndpointResponse = await endpoint.processRequest(
|
|
47
|
+
args,
|
|
48
|
+
request.logger,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// otherwise .json() will throw "TypeError: Do not know how to serialize a BigInt" as uses JSON.stringify
|
|
52
|
+
const responseObject = stringifyBigInts(apiEndpointResponse);
|
|
53
|
+
|
|
54
|
+
response.json(responseObject);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
request.logger.error(() => ({
|
|
57
|
+
err: error,
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
response.status(500).send("An internal server error occurred.");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { ApiExpressDefaultEndpointAdapter };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import type { ApiEndpoint } from "@prosopo/api-route";
|
|
16
|
+
import type { NextFunction, Request, Response } from "express";
|
|
17
|
+
import type { ZodType } from "zod";
|
|
18
|
+
|
|
19
|
+
interface ApiExpressEndpointAdapter {
|
|
20
|
+
handleRequest(
|
|
21
|
+
endpoint: ApiEndpoint<ZodType | undefined>,
|
|
22
|
+
request: Request,
|
|
23
|
+
response: Response,
|
|
24
|
+
next: NextFunction,
|
|
25
|
+
): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type { ApiExpressEndpointAdapter };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
// We need the unused params to make express recognise this function as an error handler
|
|
16
|
+
|
|
17
|
+
import { type ProsopoApiError, unwrapError } from "@prosopo/common";
|
|
18
|
+
import type { NextFunction, Request, Response } from "express";
|
|
19
|
+
import type { ZodError } from "zod";
|
|
20
|
+
|
|
21
|
+
export const handleErrors = (
|
|
22
|
+
err: ProsopoApiError | SyntaxError | ZodError,
|
|
23
|
+
request: Request,
|
|
24
|
+
response: Response,
|
|
25
|
+
next: NextFunction,
|
|
26
|
+
) => {
|
|
27
|
+
const { code, statusMessage, jsonError } = unwrapError(err, request.i18n);
|
|
28
|
+
response.statusMessage = statusMessage;
|
|
29
|
+
response.set("content-type", "application/json");
|
|
30
|
+
response.status(code);
|
|
31
|
+
response.send({ error: jsonError });
|
|
32
|
+
response.end();
|
|
33
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import type { LogLevel } from "@prosopo/logger";
|
|
16
|
+
import { ApiExpressRouterFactory } from "./apiExpressRouterFactory.js";
|
|
17
|
+
import { ApiExpressDefaultEndpointAdapter } from "./endpointAdapter/apiExpressDefaultEndpointAdapter.js";
|
|
18
|
+
import type { ApiExpressEndpointAdapter } from "./endpointAdapter/apiExpressEndpointAdapter.js";
|
|
19
|
+
|
|
20
|
+
const apiExpressRouterFactory = new ApiExpressRouterFactory();
|
|
21
|
+
|
|
22
|
+
const createApiExpressDefaultEndpointAdapter = (
|
|
23
|
+
logLevel: LogLevel,
|
|
24
|
+
errorStatusCode = 500,
|
|
25
|
+
): ApiExpressEndpointAdapter => {
|
|
26
|
+
return new ApiExpressDefaultEndpointAdapter(logLevel, errorStatusCode);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
apiExpressRouterFactory,
|
|
31
|
+
createApiExpressDefaultEndpointAdapter,
|
|
32
|
+
type ApiExpressEndpointAdapter,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export * from "./errorHandler.js";
|
|
36
|
+
export * from "./middlewares/authMiddleware.js";
|
|
37
|
+
export * from "./middlewares/requestLoggerMiddleware.js";
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import { hexToU8a } from "@polkadot/util";
|
|
16
|
+
import { ProsopoApiError, ProsopoEnvError } from "@prosopo/common";
|
|
17
|
+
import type { KeyringPair } from "@prosopo/types";
|
|
18
|
+
import type { JWT } from "@prosopo/util-crypto";
|
|
19
|
+
import type { NextFunction, Request, Response } from "express";
|
|
20
|
+
|
|
21
|
+
export const authMiddleware = (
|
|
22
|
+
pair: KeyringPair | undefined,
|
|
23
|
+
authAccount?: KeyringPair | undefined,
|
|
24
|
+
) => {
|
|
25
|
+
return async (req: Request, res: Response, next: NextFunction) => {
|
|
26
|
+
try {
|
|
27
|
+
const jwt = extractJWT(req);
|
|
28
|
+
|
|
29
|
+
let error: ProsopoApiError | undefined;
|
|
30
|
+
|
|
31
|
+
if (authAccount?.jwtVerify(jwt).isValid) {
|
|
32
|
+
next();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (pair?.jwtVerify(jwt).isValid) {
|
|
37
|
+
next();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
res.status(401).json({
|
|
42
|
+
error: new ProsopoEnvError(error || "API.UNAUTHORIZED", {
|
|
43
|
+
context: { i18n: req.i18n, code: 401 },
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
} catch (err) {
|
|
48
|
+
req.logger.error(() => ({ err, msg: "Auth Middleware Error" }));
|
|
49
|
+
res.status(401).json({ error: "Unauthorized", message: err });
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const extractJWT = (req: Request) => {
|
|
56
|
+
const authHeader = req.headers.Authorization || req.headers.authorization;
|
|
57
|
+
|
|
58
|
+
if (!authHeader || typeof authHeader !== "string") {
|
|
59
|
+
throw new ProsopoApiError("GENERAL.MISSING_AUTH_HEADER", {
|
|
60
|
+
context: { error: "Missing Authorization header", code: 401 },
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const jwt = authHeader.replace("Bearer ", "");
|
|
65
|
+
|
|
66
|
+
if (!jwt) {
|
|
67
|
+
throw new ProsopoApiError("GENERAL.INVALID_JWT", {
|
|
68
|
+
context: { error: "Missing JWT", code: 400 },
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return jwt as JWT;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const verifySignature = (
|
|
76
|
+
signature: string,
|
|
77
|
+
message: string,
|
|
78
|
+
pair: KeyringPair,
|
|
79
|
+
) => {
|
|
80
|
+
const u8Sig = hexToU8a(signature);
|
|
81
|
+
|
|
82
|
+
if (!pair.verify(message, u8Sig, pair.publicKey)) {
|
|
83
|
+
throw new ProsopoApiError("GENERAL.INVALID_SIGNATURE", {
|
|
84
|
+
context: {
|
|
85
|
+
error: "Signature verification failed",
|
|
86
|
+
code: 401,
|
|
87
|
+
account: pair.address,
|
|
88
|
+
message,
|
|
89
|
+
signature,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import type { ProviderEnvironment } from "@prosopo/env";
|
|
16
|
+
import { getLogger, parseLogLevel } from "@prosopo/logger";
|
|
17
|
+
import type { NextFunction, Request, Response } from "express";
|
|
18
|
+
import { v4 as uuidv4 } from "uuid";
|
|
19
|
+
|
|
20
|
+
const getHeaderValue = (
|
|
21
|
+
req: Request,
|
|
22
|
+
headerName: string,
|
|
23
|
+
): string | undefined => {
|
|
24
|
+
if (headerName in req.headers && req.headers[headerName]) {
|
|
25
|
+
return req.headers[headerName].toString();
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function requestLoggerMiddleware(env: ProviderEnvironment) {
|
|
31
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
32
|
+
const requestId =
|
|
33
|
+
(req.headers["x-request-id"] as string) || `e-${uuidv4()}`; // use prefix to differentiate from other IDs
|
|
34
|
+
const user = getHeaderValue(req, "prosopo-user");
|
|
35
|
+
const siteKey = getHeaderValue(req, "prosopo-site-key");
|
|
36
|
+
const sessionId = req.body?.sessionId ? req.body.sessionId : null;
|
|
37
|
+
|
|
38
|
+
const logger = getLogger(
|
|
39
|
+
parseLogLevel(env.config.logLevel),
|
|
40
|
+
"request-logger",
|
|
41
|
+
).with({
|
|
42
|
+
requestId,
|
|
43
|
+
...(user ? { user } : {}),
|
|
44
|
+
...(siteKey ? { siteKey } : {}),
|
|
45
|
+
...(sessionId ? { sessionId } : {}),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Attach logger to the request
|
|
49
|
+
req.logger = logger;
|
|
50
|
+
req.requestId = requestId;
|
|
51
|
+
|
|
52
|
+
// Continue to next middleware
|
|
53
|
+
next();
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import { ProsopoApiError, ProsopoEnvError } from "@prosopo/common";
|
|
16
|
+
import { loadI18next } from "@prosopo/locale";
|
|
17
|
+
import type { NextFunction, Request, Response } from "express";
|
|
18
|
+
import { describe, expect, it, vi } from "vitest";
|
|
19
|
+
import { ZodError } from "zod";
|
|
20
|
+
import { handleErrors } from "../../errorHandler.js";
|
|
21
|
+
|
|
22
|
+
describe("handleErrors", async () => {
|
|
23
|
+
const i18n = await loadI18next(true);
|
|
24
|
+
await i18n.changeLanguage("en");
|
|
25
|
+
|
|
26
|
+
it("should handle ProsopoApiError", async () => {
|
|
27
|
+
const mockRequest = { i18n } as unknown as Request;
|
|
28
|
+
const mockResponse = {
|
|
29
|
+
writeHead: vi.fn().mockReturnThis(),
|
|
30
|
+
set: vi.fn().mockReturnThis(),
|
|
31
|
+
status: vi.fn().mockReturnThis(),
|
|
32
|
+
send: vi.fn(),
|
|
33
|
+
end: vi.fn(),
|
|
34
|
+
} as unknown as Response;
|
|
35
|
+
const mockNext = vi.fn() as unknown as NextFunction;
|
|
36
|
+
|
|
37
|
+
const error = new ProsopoApiError("CONTRACT.INVALID_DATA_FORMAT", {
|
|
38
|
+
context: { code: 400 },
|
|
39
|
+
i18n,
|
|
40
|
+
});
|
|
41
|
+
console.log(error);
|
|
42
|
+
|
|
43
|
+
handleErrors(error, mockRequest, mockResponse, mockNext);
|
|
44
|
+
|
|
45
|
+
expect(mockResponse.set).toHaveBeenCalledWith(
|
|
46
|
+
"content-type",
|
|
47
|
+
"application/json",
|
|
48
|
+
);
|
|
49
|
+
expect(mockResponse.send).toHaveBeenCalledWith({
|
|
50
|
+
error: {
|
|
51
|
+
code: 400,
|
|
52
|
+
key: "CONTRACT.INVALID_DATA_FORMAT",
|
|
53
|
+
message: "Invalid data format",
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
expect(mockResponse.status).toHaveBeenCalledWith(400);
|
|
57
|
+
expect(mockResponse.end).toHaveBeenCalled();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("should not return SyntaxError", async () => {
|
|
61
|
+
const mockRequest = { i18n } as unknown as Request;
|
|
62
|
+
const mockResponse = {
|
|
63
|
+
writeHead: vi.fn().mockReturnThis(),
|
|
64
|
+
set: vi.fn().mockReturnThis(),
|
|
65
|
+
status: vi.fn().mockReturnThis(),
|
|
66
|
+
send: vi.fn(),
|
|
67
|
+
end: vi.fn(),
|
|
68
|
+
} as unknown as Response;
|
|
69
|
+
const mockNext = vi.fn() as unknown as NextFunction;
|
|
70
|
+
|
|
71
|
+
const message = "Unknown API error";
|
|
72
|
+
const error = new SyntaxError(message);
|
|
73
|
+
|
|
74
|
+
handleErrors(error, mockRequest, mockResponse, mockNext);
|
|
75
|
+
|
|
76
|
+
expect(mockResponse.set).toHaveBeenCalledWith(
|
|
77
|
+
"content-type",
|
|
78
|
+
"application/json",
|
|
79
|
+
);
|
|
80
|
+
expect(mockResponse.status).toHaveBeenCalledWith(400);
|
|
81
|
+
expect(mockResponse.send).toHaveBeenCalledWith({
|
|
82
|
+
error: {
|
|
83
|
+
message,
|
|
84
|
+
key: "API.UNKNOWN",
|
|
85
|
+
code: 400,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
expect(mockResponse.end).toHaveBeenCalled();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should handle ZodError", () => {
|
|
92
|
+
const mockRequest = { i18n } as unknown as Request;
|
|
93
|
+
const mockResponse = {
|
|
94
|
+
writeHead: vi.fn().mockReturnThis(),
|
|
95
|
+
set: vi.fn().mockReturnThis(),
|
|
96
|
+
status: vi.fn().mockReturnThis(),
|
|
97
|
+
send: vi.fn(),
|
|
98
|
+
end: vi.fn(),
|
|
99
|
+
} as unknown as Response;
|
|
100
|
+
const mockNext = vi.fn() as unknown as NextFunction;
|
|
101
|
+
|
|
102
|
+
const zodError = {
|
|
103
|
+
code: "custom" as const,
|
|
104
|
+
message: "Invalid input",
|
|
105
|
+
path: ["some", "variable"],
|
|
106
|
+
};
|
|
107
|
+
const error = new ZodError([zodError]);
|
|
108
|
+
|
|
109
|
+
handleErrors(error, mockRequest, mockResponse, mockNext);
|
|
110
|
+
|
|
111
|
+
expect(mockResponse.set).toHaveBeenCalledWith(
|
|
112
|
+
"content-type",
|
|
113
|
+
"application/json",
|
|
114
|
+
);
|
|
115
|
+
expect(mockResponse.status).toHaveBeenCalledWith(400);
|
|
116
|
+
expect(mockResponse.send).toHaveBeenCalledWith({
|
|
117
|
+
error: { code: 400, key: "API.INVALID_BODY", message: [zodError] },
|
|
118
|
+
});
|
|
119
|
+
expect(mockResponse.end).toHaveBeenCalled();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("should unwrap nested ProsopoBaseError", async () => {
|
|
123
|
+
const mockRequest = { i18n } as unknown as Request;
|
|
124
|
+
const mockResponse = {
|
|
125
|
+
writeHead: vi.fn().mockReturnThis(),
|
|
126
|
+
set: vi.fn().mockReturnThis(),
|
|
127
|
+
status: vi.fn().mockReturnThis(),
|
|
128
|
+
send: vi.fn(),
|
|
129
|
+
end: vi.fn(),
|
|
130
|
+
} as unknown as Response;
|
|
131
|
+
const mockNext = vi.fn() as unknown as NextFunction;
|
|
132
|
+
|
|
133
|
+
const envError = new ProsopoEnvError("GENERAL.ENVIRONMENT_NOT_READY", {
|
|
134
|
+
i18n,
|
|
135
|
+
});
|
|
136
|
+
const apiError = new ProsopoApiError(envError);
|
|
137
|
+
|
|
138
|
+
handleErrors(apiError, mockRequest, mockResponse, mockNext);
|
|
139
|
+
|
|
140
|
+
expect(mockResponse.set).toHaveBeenCalledWith(
|
|
141
|
+
"content-type",
|
|
142
|
+
"application/json",
|
|
143
|
+
);
|
|
144
|
+
expect(mockResponse.status).toHaveBeenCalledWith(500);
|
|
145
|
+
expect(mockResponse.send).toHaveBeenCalledWith({
|
|
146
|
+
error: {
|
|
147
|
+
code: 500,
|
|
148
|
+
key: "GENERAL.ENVIRONMENT_NOT_READY",
|
|
149
|
+
message: "Environment not ready",
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
expect(mockResponse.end).toHaveBeenCalled();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("should unwrap nested ProsopoBaseErrors but not an Error that is nested inside them", async () => {
|
|
156
|
+
const mockRequest = { i18n } as unknown as Request;
|
|
157
|
+
const mockResponse = {
|
|
158
|
+
writeHead: vi.fn().mockReturnThis(),
|
|
159
|
+
set: vi.fn().mockReturnThis(),
|
|
160
|
+
status: vi.fn().mockReturnThis(),
|
|
161
|
+
send: vi.fn(),
|
|
162
|
+
end: vi.fn(),
|
|
163
|
+
} as unknown as Response;
|
|
164
|
+
const mockNext = vi.fn() as unknown as NextFunction;
|
|
165
|
+
const code = 400;
|
|
166
|
+
const key = "API.UNKNOWN";
|
|
167
|
+
|
|
168
|
+
const error = new Error("Some error");
|
|
169
|
+
const apiError = new ProsopoApiError(key, {
|
|
170
|
+
context: { code, error },
|
|
171
|
+
i18n,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
handleErrors(apiError, mockRequest, mockResponse, mockNext);
|
|
175
|
+
|
|
176
|
+
expect(mockResponse.set).toHaveBeenCalledWith(
|
|
177
|
+
"content-type",
|
|
178
|
+
"application/json",
|
|
179
|
+
);
|
|
180
|
+
expect(mockResponse.status).toHaveBeenCalledWith(code);
|
|
181
|
+
expect(mockResponse.send).toHaveBeenCalledWith({
|
|
182
|
+
error: {
|
|
183
|
+
code,
|
|
184
|
+
key,
|
|
185
|
+
message: "Unknown API error",
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
expect(mockResponse.end).toHaveBeenCalled();
|
|
189
|
+
});
|
|
190
|
+
});
|