@constructive-io/graphql-server 5.13.2 → 5.13.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/errors/graphql-response.d.ts +21 -0
- package/errors/graphql-response.js +26 -0
- package/esm/errors/graphql-response.js +23 -0
- package/esm/middleware/auth.js +6 -13
- package/esm/middleware/captcha.js +4 -12
- package/middleware/auth.js +6 -13
- package/middleware/captcha.js +4 -12
- package/package.json +15 -15
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL-over-HTTP error responses.
|
|
3
|
+
*
|
|
4
|
+
* Middleware that short-circuits a GraphQL request (auth, captcha, ...) must
|
|
5
|
+
* still answer with a valid GraphQL response body. Building that body by hand
|
|
6
|
+
* is how a code ends up shipped without a top-level `message`: clients render
|
|
7
|
+
* `errors[].message`, so an error carrying only `extensions.code` surfaces as
|
|
8
|
+
* an empty string. Route every such response through here so the message and
|
|
9
|
+
* extensions both come from the canonical error registry.
|
|
10
|
+
*
|
|
11
|
+
* @module errors/graphql-response
|
|
12
|
+
*/
|
|
13
|
+
import type { ConstructiveError } from '@constructive-io/errors';
|
|
14
|
+
import type { Response } from 'express';
|
|
15
|
+
/**
|
|
16
|
+
* Send a {@link ConstructiveError} as a GraphQL error response.
|
|
17
|
+
*
|
|
18
|
+
* Uses HTTP 200 per the GraphQL-over-HTTP convention: transport succeeded, the
|
|
19
|
+
* operation did not. The error's own `http` hint travels in `extensions`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function respondWithGraphQLError(res: Response, error: ConstructiveError): void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GraphQL-over-HTTP error responses.
|
|
4
|
+
*
|
|
5
|
+
* Middleware that short-circuits a GraphQL request (auth, captcha, ...) must
|
|
6
|
+
* still answer with a valid GraphQL response body. Building that body by hand
|
|
7
|
+
* is how a code ends up shipped without a top-level `message`: clients render
|
|
8
|
+
* `errors[].message`, so an error carrying only `extensions.code` surfaces as
|
|
9
|
+
* an empty string. Route every such response through here so the message and
|
|
10
|
+
* extensions both come from the canonical error registry.
|
|
11
|
+
*
|
|
12
|
+
* @module errors/graphql-response
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.respondWithGraphQLError = respondWithGraphQLError;
|
|
16
|
+
/**
|
|
17
|
+
* Send a {@link ConstructiveError} as a GraphQL error response.
|
|
18
|
+
*
|
|
19
|
+
* Uses HTTP 200 per the GraphQL-over-HTTP convention: transport succeeded, the
|
|
20
|
+
* operation did not. The error's own `http` hint travels in `extensions`.
|
|
21
|
+
*/
|
|
22
|
+
function respondWithGraphQLError(res, error) {
|
|
23
|
+
res.status(200).json({
|
|
24
|
+
errors: [{ message: error.message, extensions: error.toExtensions() }],
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL-over-HTTP error responses.
|
|
3
|
+
*
|
|
4
|
+
* Middleware that short-circuits a GraphQL request (auth, captcha, ...) must
|
|
5
|
+
* still answer with a valid GraphQL response body. Building that body by hand
|
|
6
|
+
* is how a code ends up shipped without a top-level `message`: clients render
|
|
7
|
+
* `errors[].message`, so an error carrying only `extensions.code` surfaces as
|
|
8
|
+
* an empty string. Route every such response through here so the message and
|
|
9
|
+
* extensions both come from the canonical error registry.
|
|
10
|
+
*
|
|
11
|
+
* @module errors/graphql-response
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Send a {@link ConstructiveError} as a GraphQL error response.
|
|
15
|
+
*
|
|
16
|
+
* Uses HTTP 200 per the GraphQL-over-HTTP convention: transport succeeded, the
|
|
17
|
+
* operation did not. The error's own `http` hint travels in `extensions`.
|
|
18
|
+
*/
|
|
19
|
+
export function respondWithGraphQLError(res, error) {
|
|
20
|
+
res.status(200).json({
|
|
21
|
+
errors: [{ message: error.message, extensions: error.toExtensions() }],
|
|
22
|
+
});
|
|
23
|
+
}
|
package/esm/middleware/auth.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { errors } from '@constructive-io/errors';
|
|
1
2
|
import { getNodeEnv } from '@pgpmjs/env';
|
|
2
3
|
import { Logger } from '@pgpmjs/logger';
|
|
3
4
|
import { getPgPool } from 'pg-cache';
|
|
4
5
|
import pgQueryContext from 'pg-query-context';
|
|
6
|
+
import { respondWithGraphQLError } from '../errors/graphql-response';
|
|
5
7
|
import './types'; // for Request type
|
|
6
8
|
const log = new Logger('auth');
|
|
7
9
|
const isDev = () => getNodeEnv() === 'development';
|
|
@@ -80,9 +82,7 @@ export const createAuthenticateMiddleware = (opts) => {
|
|
|
80
82
|
log.info(`[auth] Query result: rowCount=${result?.rowCount}`);
|
|
81
83
|
if (result?.rowCount === 0) {
|
|
82
84
|
log.info('[auth] No rows returned, returning UNAUTHENTICATED');
|
|
83
|
-
res.
|
|
84
|
-
errors: [{ extensions: { code: 'UNAUTHENTICATED' } }],
|
|
85
|
-
});
|
|
85
|
+
respondWithGraphQLError(res, errors.UNAUTHENTICATED());
|
|
86
86
|
return;
|
|
87
87
|
}
|
|
88
88
|
token = result.rows[0];
|
|
@@ -90,16 +90,9 @@ export const createAuthenticateMiddleware = (opts) => {
|
|
|
90
90
|
}
|
|
91
91
|
catch (e) {
|
|
92
92
|
log.error('[auth] Auth error:', e.message);
|
|
93
|
-
res.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
extensions: {
|
|
97
|
-
code: 'BAD_TOKEN_DEFINITION',
|
|
98
|
-
message: e.message,
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
],
|
|
102
|
-
});
|
|
93
|
+
respondWithGraphQLError(res, errors.INTERNAL_FAILURE({
|
|
94
|
+
details: isDev() ? e.message : 'authentication failed',
|
|
95
|
+
}));
|
|
103
96
|
return;
|
|
104
97
|
}
|
|
105
98
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { errors } from '@constructive-io/errors';
|
|
1
2
|
import { Logger } from '@pgpmjs/logger';
|
|
3
|
+
import { respondWithGraphQLError } from '../errors/graphql-response';
|
|
2
4
|
import './types'; // for Request type
|
|
3
5
|
const log = new Logger('captcha');
|
|
4
6
|
/** Google reCAPTCHA verification endpoint */
|
|
@@ -87,22 +89,12 @@ export const createCaptchaMiddleware = () => {
|
|
|
87
89
|
}
|
|
88
90
|
const captchaToken = req.get(CAPTCHA_HEADER);
|
|
89
91
|
if (!captchaToken) {
|
|
90
|
-
res.
|
|
91
|
-
errors: [{
|
|
92
|
-
message: 'CAPTCHA verification required',
|
|
93
|
-
extensions: { code: 'CAPTCHA_REQUIRED' },
|
|
94
|
-
}],
|
|
95
|
-
});
|
|
92
|
+
respondWithGraphQLError(res, errors.CAPTCHA_REQUIRED());
|
|
96
93
|
return;
|
|
97
94
|
}
|
|
98
95
|
const valid = await verifyToken(captchaToken, secretKey);
|
|
99
96
|
if (!valid) {
|
|
100
|
-
res.
|
|
101
|
-
errors: [{
|
|
102
|
-
message: 'CAPTCHA verification failed',
|
|
103
|
-
extensions: { code: 'CAPTCHA_FAILED' },
|
|
104
|
-
}],
|
|
105
|
-
});
|
|
97
|
+
respondWithGraphQLError(res, errors.CAPTCHA_FAILED());
|
|
106
98
|
return;
|
|
107
99
|
}
|
|
108
100
|
log.info(`[captcha] Verified for operation=${opName}`);
|
package/middleware/auth.js
CHANGED
|
@@ -4,10 +4,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createAuthenticateMiddleware = void 0;
|
|
7
|
+
const errors_1 = require("@constructive-io/errors");
|
|
7
8
|
const env_1 = require("@pgpmjs/env");
|
|
8
9
|
const logger_1 = require("@pgpmjs/logger");
|
|
9
10
|
const pg_cache_1 = require("pg-cache");
|
|
10
11
|
const pg_query_context_1 = __importDefault(require("pg-query-context"));
|
|
12
|
+
const graphql_response_1 = require("../errors/graphql-response");
|
|
11
13
|
require("./types"); // for Request type
|
|
12
14
|
const log = new logger_1.Logger('auth');
|
|
13
15
|
const isDev = () => (0, env_1.getNodeEnv)() === 'development';
|
|
@@ -86,9 +88,7 @@ const createAuthenticateMiddleware = (opts) => {
|
|
|
86
88
|
log.info(`[auth] Query result: rowCount=${result?.rowCount}`);
|
|
87
89
|
if (result?.rowCount === 0) {
|
|
88
90
|
log.info('[auth] No rows returned, returning UNAUTHENTICATED');
|
|
89
|
-
res.
|
|
90
|
-
errors: [{ extensions: { code: 'UNAUTHENTICATED' } }],
|
|
91
|
-
});
|
|
91
|
+
(0, graphql_response_1.respondWithGraphQLError)(res, errors_1.errors.UNAUTHENTICATED());
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
94
94
|
token = result.rows[0];
|
|
@@ -96,16 +96,9 @@ const createAuthenticateMiddleware = (opts) => {
|
|
|
96
96
|
}
|
|
97
97
|
catch (e) {
|
|
98
98
|
log.error('[auth] Auth error:', e.message);
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
extensions: {
|
|
103
|
-
code: 'BAD_TOKEN_DEFINITION',
|
|
104
|
-
message: e.message,
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
],
|
|
108
|
-
});
|
|
99
|
+
(0, graphql_response_1.respondWithGraphQLError)(res, errors_1.errors.INTERNAL_FAILURE({
|
|
100
|
+
details: isDev() ? e.message : 'authentication failed',
|
|
101
|
+
}));
|
|
109
102
|
return;
|
|
110
103
|
}
|
|
111
104
|
}
|
package/middleware/captcha.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createCaptchaMiddleware = void 0;
|
|
4
|
+
const errors_1 = require("@constructive-io/errors");
|
|
4
5
|
const logger_1 = require("@pgpmjs/logger");
|
|
6
|
+
const graphql_response_1 = require("../errors/graphql-response");
|
|
5
7
|
require("./types"); // for Request type
|
|
6
8
|
const log = new logger_1.Logger('captcha');
|
|
7
9
|
/** Google reCAPTCHA verification endpoint */
|
|
@@ -90,22 +92,12 @@ const createCaptchaMiddleware = () => {
|
|
|
90
92
|
}
|
|
91
93
|
const captchaToken = req.get(CAPTCHA_HEADER);
|
|
92
94
|
if (!captchaToken) {
|
|
93
|
-
res.
|
|
94
|
-
errors: [{
|
|
95
|
-
message: 'CAPTCHA verification required',
|
|
96
|
-
extensions: { code: 'CAPTCHA_REQUIRED' },
|
|
97
|
-
}],
|
|
98
|
-
});
|
|
95
|
+
(0, graphql_response_1.respondWithGraphQLError)(res, errors_1.errors.CAPTCHA_REQUIRED());
|
|
99
96
|
return;
|
|
100
97
|
}
|
|
101
98
|
const valid = await verifyToken(captchaToken, secretKey);
|
|
102
99
|
if (!valid) {
|
|
103
|
-
res.
|
|
104
|
-
errors: [{
|
|
105
|
-
message: 'CAPTCHA verification failed',
|
|
106
|
-
extensions: { code: 'CAPTCHA_FAILED' },
|
|
107
|
-
}],
|
|
108
|
-
});
|
|
100
|
+
(0, graphql_response_1.respondWithGraphQLError)(res, errors_1.errors.CAPTCHA_FAILED());
|
|
109
101
|
return;
|
|
110
102
|
}
|
|
111
103
|
log.info(`[captcha] Verified for operation=${opName}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constructive-io/graphql-server",
|
|
3
|
-
"version": "5.13.
|
|
3
|
+
"version": "5.13.5",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "Constructive GraphQL Server",
|
|
6
6
|
"main": "index.js",
|
|
@@ -42,19 +42,19 @@
|
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@constructive-io/csrf": "^0.25.1",
|
|
45
|
-
"@constructive-io/errors": "^0.7.
|
|
46
|
-
"@constructive-io/express-context": "^0.22.
|
|
47
|
-
"@constructive-io/graphql-env": "^3.27.
|
|
48
|
-
"@constructive-io/graphql-types": "^3.26.
|
|
45
|
+
"@constructive-io/errors": "^0.7.3",
|
|
46
|
+
"@constructive-io/express-context": "^0.22.5",
|
|
47
|
+
"@constructive-io/graphql-env": "^3.27.5",
|
|
48
|
+
"@constructive-io/graphql-types": "^3.26.5",
|
|
49
49
|
"@constructive-io/query-builder": "^3.8.1",
|
|
50
50
|
"@constructive-io/s3-utils": "^2.28.1",
|
|
51
51
|
"@constructive-io/url-domains": "^2.27.1",
|
|
52
52
|
"@graphile-contrib/pg-many-to-many": "2.0.0-rc.2",
|
|
53
|
-
"@pgpmjs/env": "^2.39.
|
|
53
|
+
"@pgpmjs/env": "^2.39.3",
|
|
54
54
|
"@pgpmjs/logger": "^2.22.1",
|
|
55
|
-
"@pgpmjs/server-utils": "^3.23.
|
|
56
|
-
"@pgpmjs/types": "^2.48.
|
|
57
|
-
"agentic-server": "0.20.
|
|
55
|
+
"@pgpmjs/server-utils": "^3.23.5",
|
|
56
|
+
"@pgpmjs/types": "^2.48.2",
|
|
57
|
+
"agentic-server": "0.20.5",
|
|
58
58
|
"cors": "^2.8.6",
|
|
59
59
|
"deepmerge": "^4.3.1",
|
|
60
60
|
"express": "^5.2.1",
|
|
@@ -63,16 +63,16 @@
|
|
|
63
63
|
"grafserv": "1.0.0",
|
|
64
64
|
"graphile-build": "5.0.2",
|
|
65
65
|
"graphile-build-pg": "5.0.2",
|
|
66
|
-
"graphile-cache": "^4.8.
|
|
66
|
+
"graphile-cache": "^4.8.5",
|
|
67
67
|
"graphile-config": "1.0.1",
|
|
68
|
-
"graphile-function-bindings": "^1.9.
|
|
69
|
-
"graphile-settings": "^6.10.
|
|
68
|
+
"graphile-function-bindings": "^1.9.9",
|
|
69
|
+
"graphile-settings": "^6.10.4",
|
|
70
70
|
"graphile-utils": "5.0.1",
|
|
71
71
|
"graphql": "16.13.0",
|
|
72
72
|
"graphql-upload": "^13.0.0",
|
|
73
73
|
"lru-cache": "^11.2.7",
|
|
74
74
|
"pg": "^8.21.0",
|
|
75
|
-
"pg-cache": "^3.23.
|
|
75
|
+
"pg-cache": "^3.23.5",
|
|
76
76
|
"pg-env": "^1.26.1",
|
|
77
77
|
"pg-query-context": "^2.27.1",
|
|
78
78
|
"pg-sql2": "5.0.1",
|
|
@@ -88,10 +88,10 @@
|
|
|
88
88
|
"@types/pg": "^8.20.0",
|
|
89
89
|
"@types/request-ip": "^0.0.41",
|
|
90
90
|
"cookie-parser": "^1.4.7",
|
|
91
|
-
"graphile-test": "5.9.
|
|
91
|
+
"graphile-test": "5.9.9",
|
|
92
92
|
"makage": "^0.3.0",
|
|
93
93
|
"nodemon": "^3.1.14",
|
|
94
94
|
"ts-node": "^10.9.2"
|
|
95
95
|
},
|
|
96
|
-
"gitHead": "
|
|
96
|
+
"gitHead": "947334d361e9f4c48d451f1c7312b2d705ea154e"
|
|
97
97
|
}
|