@adaptic/backend-legacy 0.0.44 → 0.0.45
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/Allocation.cjs +19 -0
- package/esm/Allocation.d.ts.map +1 -1
- package/esm/Allocation.js.map +1 -1
- package/esm/Allocation.mjs +19 -0
- package/esm/config/jwtConfig.d.ts +16 -0
- package/esm/config/jwtConfig.d.ts.map +1 -0
- package/esm/config/jwtConfig.js.map +1 -0
- package/esm/config/jwtConfig.mjs +47 -0
- package/esm/generated/typegraphql-prisma/models/Trade.d.ts +1 -1
- package/esm/generated/typegraphql-prisma/models/Trade.d.ts.map +1 -1
- package/esm/generated/typegraphql-prisma/models/Trade.js.map +1 -1
- package/esm/generated/typegraphql-prisma/models/Trade.mjs +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/crud/User/args/CreateOneUserArgs.d.ts +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/crud/User/args/CreateOneUserArgs.d.ts.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/crud/User/args/CreateOneUserArgs.js.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/crud/User/args/CreateOneUserArgs.mjs +2 -2
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeCreateInput.d.ts +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeCreateInput.d.ts.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeCreateInput.js.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeCreateInput.mjs +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeCreateManyInput.d.ts +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeCreateManyInput.d.ts.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeCreateManyInput.js.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeCreateManyInput.mjs +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeCreateWithoutActionsInput.d.ts +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeCreateWithoutActionsInput.d.ts.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeCreateWithoutActionsInput.js.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeCreateWithoutActionsInput.mjs +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeScalarWhereWithAggregatesInput.d.ts +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeScalarWhereWithAggregatesInput.d.ts.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeScalarWhereWithAggregatesInput.js.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeScalarWhereWithAggregatesInput.mjs +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateInput.d.ts +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateInput.d.ts.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateInput.js.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateInput.mjs +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateManyMutationInput.d.ts +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateManyMutationInput.d.ts.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateManyMutationInput.js.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateManyMutationInput.mjs +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateWithoutActionsInput.d.ts +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateWithoutActionsInput.d.ts.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateWithoutActionsInput.js.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateWithoutActionsInput.mjs +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeWhereInput.d.ts +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeWhereInput.d.ts.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeWhereInput.js.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeWhereInput.mjs +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeWhereUniqueInput.d.ts +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeWhereUniqueInput.d.ts.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeWhereUniqueInput.js.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/inputs/TradeWhereUniqueInput.mjs +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/outputs/CreateManyAndReturnTrade.d.ts +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/outputs/CreateManyAndReturnTrade.d.ts.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/outputs/CreateManyAndReturnTrade.js.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/outputs/CreateManyAndReturnTrade.mjs +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/outputs/TradeGroupBy.d.ts +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/outputs/TradeGroupBy.d.ts.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/outputs/TradeGroupBy.js.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/outputs/TradeGroupBy.mjs +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/outputs/TradeMaxAggregate.d.ts +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/outputs/TradeMaxAggregate.d.ts.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/outputs/TradeMaxAggregate.js.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/outputs/TradeMaxAggregate.mjs +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/outputs/TradeMinAggregate.d.ts +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/outputs/TradeMinAggregate.d.ts.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/outputs/TradeMinAggregate.js.map +1 -1
- package/esm/generated/typegraphql-prisma/resolvers/outputs/TradeMinAggregate.mjs +1 -1
- package/esm/getToken.d.ts.map +1 -1
- package/esm/getToken.js.map +1 -1
- package/esm/getToken.mjs +2 -1
- package/esm/middleware/auth.d.ts.map +1 -1
- package/esm/middleware/auth.js.map +1 -1
- package/esm/middleware/auth.mjs +8 -8
- package/esm/middleware/graphql-validation-plugin.d.ts +37 -0
- package/esm/middleware/graphql-validation-plugin.d.ts.map +1 -0
- package/esm/middleware/graphql-validation-plugin.js.map +1 -0
- package/esm/middleware/graphql-validation-plugin.mjs +163 -0
- package/esm/middleware/index.d.ts +12 -0
- package/esm/middleware/index.d.ts.map +1 -0
- package/esm/middleware/index.js.map +1 -0
- package/esm/middleware/index.mjs +16 -0
- package/esm/middleware/input-validator.d.ts +63 -0
- package/esm/middleware/input-validator.d.ts.map +1 -0
- package/esm/middleware/input-validator.js.map +1 -0
- package/esm/middleware/input-validator.mjs +210 -0
- package/esm/middleware/rate-limiter.d.ts +12 -0
- package/esm/middleware/rate-limiter.d.ts.map +1 -0
- package/esm/middleware/rate-limiter.js.map +1 -0
- package/esm/middleware/rate-limiter.mjs +68 -0
- package/esm/middleware/types.d.ts +87 -0
- package/esm/middleware/types.d.ts.map +1 -0
- package/esm/middleware/types.js.map +1 -0
- package/esm/middleware/types.mjs +13 -0
- package/esm/middleware/validation-examples.d.ts +76 -0
- package/esm/middleware/validation-examples.d.ts.map +1 -0
- package/esm/middleware/validation-examples.js.map +1 -0
- package/esm/middleware/validation-examples.mjs +373 -0
- package/esm/plugins/error-sanitizer.d.ts +37 -0
- package/esm/plugins/error-sanitizer.d.ts.map +1 -0
- package/esm/plugins/error-sanitizer.js.map +1 -0
- package/esm/plugins/error-sanitizer.mjs +184 -0
- package/esm/plugins/index.d.ts +8 -0
- package/esm/plugins/index.d.ts.map +1 -0
- package/esm/plugins/index.js.map +1 -0
- package/esm/plugins/index.mjs +8 -0
- package/esm/plugins/integration-example.d.ts +53 -0
- package/esm/plugins/integration-example.d.ts.map +1 -0
- package/esm/plugins/integration-example.js.map +1 -0
- package/esm/plugins/integration-example.mjs +88 -0
- package/esm/plugins/query-depth-limiter.d.ts +47 -0
- package/esm/plugins/query-depth-limiter.d.ts.map +1 -0
- package/esm/plugins/query-depth-limiter.js.map +1 -0
- package/esm/plugins/query-depth-limiter.mjs +146 -0
- package/esm/validators/allocation-validator.d.ts +32 -0
- package/esm/validators/allocation-validator.d.ts.map +1 -0
- package/esm/validators/allocation-validator.js.map +1 -0
- package/esm/validators/allocation-validator.mjs +80 -0
- package/generated/typegraphql-prisma/models/Trade.cjs +1 -1
- package/generated/typegraphql-prisma/models/Trade.d.ts +1 -1
- package/generated/typegraphql-prisma/models/Trade.d.ts.map +1 -1
- package/generated/typegraphql-prisma/models/Trade.js.map +1 -1
- package/generated/typegraphql-prisma/resolvers/crud/User/args/CreateOneUserArgs.cjs +2 -2
- package/generated/typegraphql-prisma/resolvers/crud/User/args/CreateOneUserArgs.d.ts +1 -1
- package/generated/typegraphql-prisma/resolvers/crud/User/args/CreateOneUserArgs.d.ts.map +1 -1
- package/generated/typegraphql-prisma/resolvers/crud/User/args/CreateOneUserArgs.js.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeCreateInput.cjs +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeCreateInput.d.ts +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeCreateInput.d.ts.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeCreateInput.js.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeCreateManyInput.cjs +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeCreateManyInput.d.ts +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeCreateManyInput.d.ts.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeCreateManyInput.js.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeCreateWithoutActionsInput.cjs +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeCreateWithoutActionsInput.d.ts +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeCreateWithoutActionsInput.d.ts.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeCreateWithoutActionsInput.js.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeScalarWhereWithAggregatesInput.cjs +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeScalarWhereWithAggregatesInput.d.ts +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeScalarWhereWithAggregatesInput.d.ts.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeScalarWhereWithAggregatesInput.js.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateInput.cjs +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateInput.d.ts +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateInput.d.ts.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateInput.js.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateManyMutationInput.cjs +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateManyMutationInput.d.ts +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateManyMutationInput.d.ts.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateManyMutationInput.js.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateWithoutActionsInput.cjs +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateWithoutActionsInput.d.ts +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateWithoutActionsInput.d.ts.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeUpdateWithoutActionsInput.js.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeWhereInput.cjs +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeWhereInput.d.ts +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeWhereInput.d.ts.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeWhereInput.js.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeWhereUniqueInput.cjs +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeWhereUniqueInput.d.ts +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeWhereUniqueInput.d.ts.map +1 -1
- package/generated/typegraphql-prisma/resolvers/inputs/TradeWhereUniqueInput.js.map +1 -1
- package/generated/typegraphql-prisma/resolvers/outputs/CreateManyAndReturnTrade.cjs +1 -1
- package/generated/typegraphql-prisma/resolvers/outputs/CreateManyAndReturnTrade.d.ts +1 -1
- package/generated/typegraphql-prisma/resolvers/outputs/CreateManyAndReturnTrade.d.ts.map +1 -1
- package/generated/typegraphql-prisma/resolvers/outputs/CreateManyAndReturnTrade.js.map +1 -1
- package/generated/typegraphql-prisma/resolvers/outputs/TradeGroupBy.cjs +1 -1
- package/generated/typegraphql-prisma/resolvers/outputs/TradeGroupBy.d.ts +1 -1
- package/generated/typegraphql-prisma/resolvers/outputs/TradeGroupBy.d.ts.map +1 -1
- package/generated/typegraphql-prisma/resolvers/outputs/TradeGroupBy.js.map +1 -1
- package/generated/typegraphql-prisma/resolvers/outputs/TradeMaxAggregate.cjs +1 -1
- package/generated/typegraphql-prisma/resolvers/outputs/TradeMaxAggregate.d.ts +1 -1
- package/generated/typegraphql-prisma/resolvers/outputs/TradeMaxAggregate.d.ts.map +1 -1
- package/generated/typegraphql-prisma/resolvers/outputs/TradeMaxAggregate.js.map +1 -1
- package/generated/typegraphql-prisma/resolvers/outputs/TradeMinAggregate.cjs +1 -1
- package/generated/typegraphql-prisma/resolvers/outputs/TradeMinAggregate.d.ts +1 -1
- package/generated/typegraphql-prisma/resolvers/outputs/TradeMinAggregate.d.ts.map +1 -1
- package/generated/typegraphql-prisma/resolvers/outputs/TradeMinAggregate.js.map +1 -1
- package/getToken.cjs +2 -1
- package/package.json +1 -1
- package/server.cjs +35 -17
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { GraphQLError } from 'graphql';
|
|
2
|
+
import { validatePercentage, validatePositiveNumber, validateNonEmpty, ValidationError, } from './input-validator.mjs';
|
|
3
|
+
/**
|
|
4
|
+
* Predefined validation rules for common field patterns
|
|
5
|
+
*/
|
|
6
|
+
const VALIDATION_RULES = [
|
|
7
|
+
// Percentage fields (0-100)
|
|
8
|
+
{
|
|
9
|
+
pattern: /.*Pct$/i,
|
|
10
|
+
validator: (value, fieldName) => {
|
|
11
|
+
if (typeof value === 'number') {
|
|
12
|
+
validatePercentage(value, fieldName);
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
description: 'Percentage fields ending with Pct',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
pattern: /.*Percent(age)?$/i,
|
|
19
|
+
validator: (value, fieldName) => {
|
|
20
|
+
if (typeof value === 'number') {
|
|
21
|
+
validatePercentage(value, fieldName);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
description: 'Percentage fields ending with Percent or Percentage',
|
|
25
|
+
},
|
|
26
|
+
// Quantity fields (must be positive)
|
|
27
|
+
{
|
|
28
|
+
pattern: /^quantity$/i,
|
|
29
|
+
validator: (value, fieldName) => {
|
|
30
|
+
if (typeof value === 'number') {
|
|
31
|
+
validatePositiveNumber(value, fieldName);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
description: 'Quantity fields',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
pattern: /.*Threshold$/i,
|
|
38
|
+
validator: (value, fieldName) => {
|
|
39
|
+
if (typeof value === 'number' && value !== 0) {
|
|
40
|
+
validatePositiveNumber(value, fieldName);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
description: 'Threshold fields',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
pattern: /^count$/i,
|
|
47
|
+
validator: (value, fieldName) => {
|
|
48
|
+
if (typeof value === 'number') {
|
|
49
|
+
validatePositiveNumber(value, fieldName);
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
description: 'Count fields',
|
|
53
|
+
},
|
|
54
|
+
// Required string fields (non-empty)
|
|
55
|
+
{
|
|
56
|
+
pattern: /^(name|title|description|symbol|type|status)$/i,
|
|
57
|
+
validator: (value, fieldName) => {
|
|
58
|
+
if (typeof value === 'string') {
|
|
59
|
+
validateNonEmpty(value, fieldName);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
description: 'Common required string fields',
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
/**
|
|
66
|
+
* Recursively validates an object's fields based on predefined rules
|
|
67
|
+
*/
|
|
68
|
+
function validateObject(obj, path = '') {
|
|
69
|
+
const errors = [];
|
|
70
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
71
|
+
const fieldPath = path ? `${path}.${key}` : key;
|
|
72
|
+
// Skip null or undefined values
|
|
73
|
+
if (value === null || value === undefined) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
// Recursively validate nested objects
|
|
77
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
78
|
+
const nestedErrors = validateObject(value, fieldPath);
|
|
79
|
+
errors.push(...nestedErrors);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
// Apply validation rules to the field
|
|
83
|
+
for (const rule of VALIDATION_RULES) {
|
|
84
|
+
if (rule.pattern.test(key)) {
|
|
85
|
+
try {
|
|
86
|
+
rule.validator(value, fieldPath);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (error instanceof ValidationError) {
|
|
90
|
+
errors.push(...error.fields);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
break; // Only apply the first matching rule
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return errors;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Apollo Server plugin that validates GraphQL mutation inputs
|
|
101
|
+
*
|
|
102
|
+
* This plugin intercepts all mutation operations and validates input fields
|
|
103
|
+
* before they reach the resolver. It applies validation rules based on field
|
|
104
|
+
* name patterns to ensure data integrity.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* const server = new ApolloServer({
|
|
109
|
+
* schema,
|
|
110
|
+
* plugins: [
|
|
111
|
+
* ApolloServerPluginDrainHttpServer({ httpServer }),
|
|
112
|
+
* createValidationPlugin(),
|
|
113
|
+
* ],
|
|
114
|
+
* });
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export function createValidationPlugin() {
|
|
118
|
+
return {
|
|
119
|
+
async requestDidStart() {
|
|
120
|
+
return {
|
|
121
|
+
async didResolveOperation(requestContext) {
|
|
122
|
+
const { operation, request } = requestContext;
|
|
123
|
+
// Only validate mutations
|
|
124
|
+
if (!operation || operation.operation !== 'mutation') {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const variables = request.variables || {};
|
|
128
|
+
const errors = [];
|
|
129
|
+
// Validate each mutation's variables
|
|
130
|
+
for (const [variableName, variableValue] of Object.entries(variables)) {
|
|
131
|
+
if (variableValue && typeof variableValue === 'object') {
|
|
132
|
+
// Check if this is a data object (common pattern in mutations)
|
|
133
|
+
const dataObj = variableValue;
|
|
134
|
+
if ('data' in dataObj && typeof dataObj.data === 'object') {
|
|
135
|
+
const validationErrors = validateObject(dataObj.data, variableName);
|
|
136
|
+
errors.push(...validationErrors);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
// Validate the entire variable object
|
|
140
|
+
const validationErrors = validateObject(dataObj, variableName);
|
|
141
|
+
errors.push(...validationErrors);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// If there are validation errors, throw before resolver execution
|
|
146
|
+
if (errors.length > 0) {
|
|
147
|
+
throw new GraphQLError(`Input validation failed for ${errors.length} field${errors.length > 1 ? 's' : ''}`, {
|
|
148
|
+
extensions: {
|
|
149
|
+
code: 'BAD_USER_INPUT',
|
|
150
|
+
validationErrors: errors,
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Export validation rules for testing and documentation
|
|
161
|
+
*/
|
|
162
|
+
export { VALIDATION_RULES };
|
|
163
|
+
//# sourceMappingURL=graphql-validation-plugin.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input Validation Middleware
|
|
3
|
+
*
|
|
4
|
+
* Provides GraphQL mutation input validation through:
|
|
5
|
+
* 1. Manual validation functions for custom resolvers
|
|
6
|
+
* 2. Automatic validation plugin for pattern-based rules
|
|
7
|
+
*/
|
|
8
|
+
export { validatePercentage, validatePositiveNumber, validateEmail, validateUrl, validateNonEmpty, validateConfidenceScore, validateFields, ValidationError, ValidationErrorDetail, } from './input-validator';
|
|
9
|
+
export { createValidationPlugin, VALIDATION_RULES, } from './graphql-validation-plugin';
|
|
10
|
+
export { authMiddleware, AuthenticatedRequest, } from './auth';
|
|
11
|
+
export { CustomValidators } from './validation-examples';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,uBAAuB,EACvB,cAAc,EACd,eAAe,EACf,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EACL,cAAc,EACd,oBAAoB,GACrB,MAAM,QAAQ,CAAC;AAGhB,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,8BAA8B;AAC9B,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,uBAAuB,EACvB,cAAc,EACd,eAAe,GAEhB,MAAM,mBAAmB,CAAC;AAE3B,mCAAmC;AACnC,OAAO,EACL,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,6BAA6B,CAAC;AAErC,mCAAmC;AACnC,OAAO,EACL,cAAc,GAEf,MAAM,QAAQ,CAAC;AAEhB,0CAA0C;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input Validation Middleware
|
|
3
|
+
*
|
|
4
|
+
* Provides GraphQL mutation input validation through:
|
|
5
|
+
* 1. Manual validation functions for custom resolvers
|
|
6
|
+
* 2. Automatic validation plugin for pattern-based rules
|
|
7
|
+
*/
|
|
8
|
+
// Export validation functions
|
|
9
|
+
export { validatePercentage, validatePositiveNumber, validateEmail, validateUrl, validateNonEmpty, validateConfidenceScore, validateFields, ValidationError, } from './input-validator.mjs';
|
|
10
|
+
// Export GraphQL validation plugin
|
|
11
|
+
export { createValidationPlugin, VALIDATION_RULES, } from './graphql-validation-plugin.mjs';
|
|
12
|
+
// Export authentication middleware
|
|
13
|
+
export { authMiddleware, } from './auth.mjs';
|
|
14
|
+
// Export example validators for reference
|
|
15
|
+
export { CustomValidators } from './validation-examples.mjs';
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { GraphQLError } from 'graphql';
|
|
2
|
+
/**
|
|
3
|
+
* Validation error details for field-level error reporting
|
|
4
|
+
*/
|
|
5
|
+
export interface ValidationErrorDetail {
|
|
6
|
+
field: string;
|
|
7
|
+
value: unknown;
|
|
8
|
+
message: string;
|
|
9
|
+
constraint: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Custom error class for validation failures
|
|
13
|
+
*/
|
|
14
|
+
export declare class ValidationError extends GraphQLError {
|
|
15
|
+
readonly fields: ValidationErrorDetail[];
|
|
16
|
+
constructor(message: string, fields: ValidationErrorDetail[]);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Validates that a number is within the 0-100 percentage range
|
|
20
|
+
* @param value - The numeric value to validate
|
|
21
|
+
* @param fieldName - The name of the field for error reporting
|
|
22
|
+
* @throws ValidationError if the value is not between 0 and 100
|
|
23
|
+
*/
|
|
24
|
+
export declare function validatePercentage(value: number, fieldName: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* Validates that a number is positive (greater than 0)
|
|
27
|
+
* @param value - The numeric value to validate
|
|
28
|
+
* @param fieldName - The name of the field for error reporting
|
|
29
|
+
* @throws ValidationError if the value is not positive
|
|
30
|
+
*/
|
|
31
|
+
export declare function validatePositiveNumber(value: number, fieldName: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* Validates that a string is a valid email format
|
|
34
|
+
* @param value - The string value to validate
|
|
35
|
+
* @throws ValidationError if the value is not a valid email
|
|
36
|
+
*/
|
|
37
|
+
export declare function validateEmail(value: string): void;
|
|
38
|
+
/**
|
|
39
|
+
* Validates that a string is a valid URL format
|
|
40
|
+
* @param value - The string value to validate
|
|
41
|
+
* @throws ValidationError if the value is not a valid URL
|
|
42
|
+
*/
|
|
43
|
+
export declare function validateUrl(value: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* Validates that a string is non-empty
|
|
46
|
+
* @param value - The string value to validate
|
|
47
|
+
* @param fieldName - The name of the field for error reporting
|
|
48
|
+
* @throws ValidationError if the value is empty or not a string
|
|
49
|
+
*/
|
|
50
|
+
export declare function validateNonEmpty(value: string, fieldName: string): void;
|
|
51
|
+
/**
|
|
52
|
+
* Validates that a confidence score is within the 0-1 range
|
|
53
|
+
* @param value - The numeric value to validate
|
|
54
|
+
* @throws ValidationError if the value is not between 0 and 1
|
|
55
|
+
*/
|
|
56
|
+
export declare function validateConfidenceScore(value: number): void;
|
|
57
|
+
/**
|
|
58
|
+
* Validates multiple fields and accumulates all validation errors
|
|
59
|
+
* @param validations - Array of validation functions to execute
|
|
60
|
+
* @throws ValidationError with all accumulated field errors
|
|
61
|
+
*/
|
|
62
|
+
export declare function validateFields(validations: Array<() => void>): void;
|
|
63
|
+
//# sourceMappingURL=input-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-validator.d.ts","sourceRoot":"","sources":["../../../src/middleware/input-validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,SAAgB,MAAM,EAAE,qBAAqB,EAAE,CAAC;gBAEpC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,EAAE;CAS7D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAsBzE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAsB7E;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAwBjD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAwB/C;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAsBvE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAsB3D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAkBnE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-validator.js","sourceRoot":"","sources":["../../../src/middleware/input-validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { GraphQLError } from 'graphql';
|
|
2
|
+
/**
|
|
3
|
+
* Custom error class for validation failures
|
|
4
|
+
*/
|
|
5
|
+
export class ValidationError extends GraphQLError {
|
|
6
|
+
fields;
|
|
7
|
+
constructor(message, fields) {
|
|
8
|
+
super(message, {
|
|
9
|
+
extensions: {
|
|
10
|
+
code: 'BAD_USER_INPUT',
|
|
11
|
+
validationErrors: fields,
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
this.fields = fields;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Validates that a number is within the 0-100 percentage range
|
|
19
|
+
* @param value - The numeric value to validate
|
|
20
|
+
* @param fieldName - The name of the field for error reporting
|
|
21
|
+
* @throws ValidationError if the value is not between 0 and 100
|
|
22
|
+
*/
|
|
23
|
+
export function validatePercentage(value, fieldName) {
|
|
24
|
+
if (typeof value !== 'number' || isNaN(value)) {
|
|
25
|
+
throw new ValidationError('Invalid percentage value', [
|
|
26
|
+
{
|
|
27
|
+
field: fieldName,
|
|
28
|
+
value,
|
|
29
|
+
message: 'Must be a valid number',
|
|
30
|
+
constraint: 'isNumber',
|
|
31
|
+
},
|
|
32
|
+
]);
|
|
33
|
+
}
|
|
34
|
+
if (value < 0 || value > 100) {
|
|
35
|
+
throw new ValidationError('Percentage out of range', [
|
|
36
|
+
{
|
|
37
|
+
field: fieldName,
|
|
38
|
+
value,
|
|
39
|
+
message: 'Must be between 0 and 100',
|
|
40
|
+
constraint: 'range',
|
|
41
|
+
},
|
|
42
|
+
]);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Validates that a number is positive (greater than 0)
|
|
47
|
+
* @param value - The numeric value to validate
|
|
48
|
+
* @param fieldName - The name of the field for error reporting
|
|
49
|
+
* @throws ValidationError if the value is not positive
|
|
50
|
+
*/
|
|
51
|
+
export function validatePositiveNumber(value, fieldName) {
|
|
52
|
+
if (typeof value !== 'number' || isNaN(value)) {
|
|
53
|
+
throw new ValidationError('Invalid numeric value', [
|
|
54
|
+
{
|
|
55
|
+
field: fieldName,
|
|
56
|
+
value,
|
|
57
|
+
message: 'Must be a valid number',
|
|
58
|
+
constraint: 'isNumber',
|
|
59
|
+
},
|
|
60
|
+
]);
|
|
61
|
+
}
|
|
62
|
+
if (value <= 0) {
|
|
63
|
+
throw new ValidationError('Number must be positive', [
|
|
64
|
+
{
|
|
65
|
+
field: fieldName,
|
|
66
|
+
value,
|
|
67
|
+
message: 'Must be greater than 0',
|
|
68
|
+
constraint: 'positive',
|
|
69
|
+
},
|
|
70
|
+
]);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Validates that a string is a valid email format
|
|
75
|
+
* @param value - The string value to validate
|
|
76
|
+
* @throws ValidationError if the value is not a valid email
|
|
77
|
+
*/
|
|
78
|
+
export function validateEmail(value) {
|
|
79
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
80
|
+
if (typeof value !== 'string') {
|
|
81
|
+
throw new ValidationError('Invalid email value', [
|
|
82
|
+
{
|
|
83
|
+
field: 'email',
|
|
84
|
+
value,
|
|
85
|
+
message: 'Must be a string',
|
|
86
|
+
constraint: 'isString',
|
|
87
|
+
},
|
|
88
|
+
]);
|
|
89
|
+
}
|
|
90
|
+
if (!emailRegex.test(value)) {
|
|
91
|
+
throw new ValidationError('Invalid email format', [
|
|
92
|
+
{
|
|
93
|
+
field: 'email',
|
|
94
|
+
value,
|
|
95
|
+
message: 'Must be a valid email address',
|
|
96
|
+
constraint: 'email',
|
|
97
|
+
},
|
|
98
|
+
]);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Validates that a string is a valid URL format
|
|
103
|
+
* @param value - The string value to validate
|
|
104
|
+
* @throws ValidationError if the value is not a valid URL
|
|
105
|
+
*/
|
|
106
|
+
export function validateUrl(value) {
|
|
107
|
+
if (typeof value !== 'string') {
|
|
108
|
+
throw new ValidationError('Invalid URL value', [
|
|
109
|
+
{
|
|
110
|
+
field: 'url',
|
|
111
|
+
value,
|
|
112
|
+
message: 'Must be a string',
|
|
113
|
+
constraint: 'isString',
|
|
114
|
+
},
|
|
115
|
+
]);
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
new URL(value);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
throw new ValidationError('Invalid URL format', [
|
|
122
|
+
{
|
|
123
|
+
field: 'url',
|
|
124
|
+
value,
|
|
125
|
+
message: 'Must be a valid URL',
|
|
126
|
+
constraint: 'url',
|
|
127
|
+
},
|
|
128
|
+
]);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Validates that a string is non-empty
|
|
133
|
+
* @param value - The string value to validate
|
|
134
|
+
* @param fieldName - The name of the field for error reporting
|
|
135
|
+
* @throws ValidationError if the value is empty or not a string
|
|
136
|
+
*/
|
|
137
|
+
export function validateNonEmpty(value, fieldName) {
|
|
138
|
+
if (typeof value !== 'string') {
|
|
139
|
+
throw new ValidationError('Invalid string value', [
|
|
140
|
+
{
|
|
141
|
+
field: fieldName,
|
|
142
|
+
value,
|
|
143
|
+
message: 'Must be a string',
|
|
144
|
+
constraint: 'isString',
|
|
145
|
+
},
|
|
146
|
+
]);
|
|
147
|
+
}
|
|
148
|
+
if (value.trim().length === 0) {
|
|
149
|
+
throw new ValidationError('String cannot be empty', [
|
|
150
|
+
{
|
|
151
|
+
field: fieldName,
|
|
152
|
+
value,
|
|
153
|
+
message: 'Must not be empty',
|
|
154
|
+
constraint: 'notEmpty',
|
|
155
|
+
},
|
|
156
|
+
]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Validates that a confidence score is within the 0-1 range
|
|
161
|
+
* @param value - The numeric value to validate
|
|
162
|
+
* @throws ValidationError if the value is not between 0 and 1
|
|
163
|
+
*/
|
|
164
|
+
export function validateConfidenceScore(value) {
|
|
165
|
+
if (typeof value !== 'number' || isNaN(value)) {
|
|
166
|
+
throw new ValidationError('Invalid confidence score', [
|
|
167
|
+
{
|
|
168
|
+
field: 'confidenceScore',
|
|
169
|
+
value,
|
|
170
|
+
message: 'Must be a valid number',
|
|
171
|
+
constraint: 'isNumber',
|
|
172
|
+
},
|
|
173
|
+
]);
|
|
174
|
+
}
|
|
175
|
+
if (value < 0 || value > 1) {
|
|
176
|
+
throw new ValidationError('Confidence score out of range', [
|
|
177
|
+
{
|
|
178
|
+
field: 'confidenceScore',
|
|
179
|
+
value,
|
|
180
|
+
message: 'Must be between 0 and 1',
|
|
181
|
+
constraint: 'range',
|
|
182
|
+
},
|
|
183
|
+
]);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Validates multiple fields and accumulates all validation errors
|
|
188
|
+
* @param validations - Array of validation functions to execute
|
|
189
|
+
* @throws ValidationError with all accumulated field errors
|
|
190
|
+
*/
|
|
191
|
+
export function validateFields(validations) {
|
|
192
|
+
const errors = [];
|
|
193
|
+
for (const validation of validations) {
|
|
194
|
+
try {
|
|
195
|
+
validation();
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
if (error instanceof ValidationError) {
|
|
199
|
+
errors.push(...error.fields);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (errors.length > 0) {
|
|
207
|
+
throw new ValidationError('Validation failed for multiple fields', errors);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=input-validator.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
/**
|
|
3
|
+
* Rate limiter for GraphQL endpoint
|
|
4
|
+
* Allows 1000 requests per 15 minutes by default (configurable via RATE_LIMIT_MAX env var)
|
|
5
|
+
*/
|
|
6
|
+
export declare const graphqlRateLimiter: (req: Request, res: Response, next: NextFunction) => void;
|
|
7
|
+
/**
|
|
8
|
+
* Rate limiter for authentication endpoints
|
|
9
|
+
* Allows 50 requests per 15 minutes (stricter for auth)
|
|
10
|
+
*/
|
|
11
|
+
export declare const authRateLimiter: (req: Request, res: Response, next: NextFunction) => void;
|
|
12
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../../src/middleware/rate-limiter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAoE1D;;;GAGG;AACH,eAAO,MAAM,kBAAkB,QArChB,OAAO,OAAO,QAAQ,QAAQ,YAAY,KAAG,IA2C1D,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,eAAe,QAjDb,OAAO,OAAO,QAAQ,QAAQ,YAAY,KAAG,IAuD1D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../../src/middleware/rate-limiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Integration: add to server.ts - app.use('/graphql', graphqlRateLimiter) and app.use('/auth', authRateLimiter)
|
|
2
|
+
/**
|
|
3
|
+
* Creates a simple in-memory rate limiter middleware
|
|
4
|
+
* @param config - Rate limit configuration
|
|
5
|
+
* @returns Express middleware function
|
|
6
|
+
*/
|
|
7
|
+
function createRateLimiter(config) {
|
|
8
|
+
const store = {};
|
|
9
|
+
// Clean up expired entries every minute
|
|
10
|
+
setInterval(() => {
|
|
11
|
+
const now = Date.now();
|
|
12
|
+
Object.keys(store).forEach((key) => {
|
|
13
|
+
if (store[key].resetTime < now) {
|
|
14
|
+
delete store[key];
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
}, 60000);
|
|
18
|
+
return (req, res, next) => {
|
|
19
|
+
const identifier = req.ip || req.connection.remoteAddress || 'unknown';
|
|
20
|
+
const now = Date.now();
|
|
21
|
+
if (!store[identifier] || store[identifier].resetTime < now) {
|
|
22
|
+
store[identifier] = {
|
|
23
|
+
count: 1,
|
|
24
|
+
resetTime: now + config.windowMs,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
store[identifier].count += 1;
|
|
29
|
+
}
|
|
30
|
+
const current = store[identifier];
|
|
31
|
+
const remaining = Math.max(0, config.max - current.count);
|
|
32
|
+
const resetTime = Math.ceil((current.resetTime - now) / 1000);
|
|
33
|
+
// Add rate limit headers
|
|
34
|
+
if (config.standardHeaders !== false) {
|
|
35
|
+
res.setHeader('X-RateLimit-Limit', config.max.toString());
|
|
36
|
+
res.setHeader('X-RateLimit-Remaining', remaining.toString());
|
|
37
|
+
res.setHeader('X-RateLimit-Reset', resetTime.toString());
|
|
38
|
+
}
|
|
39
|
+
if (current.count > config.max) {
|
|
40
|
+
res.status(429).json(config.message);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
next();
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Rate limiter for GraphQL endpoint
|
|
48
|
+
* Allows 1000 requests per 15 minutes by default (configurable via RATE_LIMIT_MAX env var)
|
|
49
|
+
*/
|
|
50
|
+
export const graphqlRateLimiter = createRateLimiter({
|
|
51
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
52
|
+
max: parseInt(process.env.RATE_LIMIT_MAX || '1000', 10),
|
|
53
|
+
standardHeaders: true,
|
|
54
|
+
legacyHeaders: false,
|
|
55
|
+
message: { errors: [{ message: 'Too many requests, please try again later.' }] },
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* Rate limiter for authentication endpoints
|
|
59
|
+
* Allows 50 requests per 15 minutes (stricter for auth)
|
|
60
|
+
*/
|
|
61
|
+
export const authRateLimiter = createRateLimiter({
|
|
62
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
63
|
+
max: 50,
|
|
64
|
+
standardHeaders: true,
|
|
65
|
+
legacyHeaders: false,
|
|
66
|
+
message: { errors: [{ message: 'Too many authentication attempts.' }] },
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for validation middleware
|
|
3
|
+
*/
|
|
4
|
+
import { GraphQLError } from 'graphql';
|
|
5
|
+
import { ValidationErrorDetail } from './input-validator';
|
|
6
|
+
/**
|
|
7
|
+
* Configuration options for the validation plugin
|
|
8
|
+
*/
|
|
9
|
+
export interface ValidationPluginOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Whether to skip validation for specific operations
|
|
12
|
+
*/
|
|
13
|
+
skipOperations?: string[];
|
|
14
|
+
/**
|
|
15
|
+
* Custom validation rules to add to the default set
|
|
16
|
+
*/
|
|
17
|
+
customRules?: FieldValidationRule[];
|
|
18
|
+
/**
|
|
19
|
+
* Whether to log validation errors for debugging
|
|
20
|
+
*/
|
|
21
|
+
debug?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Field validation rule definition
|
|
25
|
+
*/
|
|
26
|
+
export interface FieldValidationRule {
|
|
27
|
+
/**
|
|
28
|
+
* Regular expression pattern to match field names
|
|
29
|
+
*/
|
|
30
|
+
pattern: RegExp;
|
|
31
|
+
/**
|
|
32
|
+
* Validation function to apply to matching fields
|
|
33
|
+
* @param value - The field value to validate
|
|
34
|
+
* @param fieldName - The name of the field being validated
|
|
35
|
+
*/
|
|
36
|
+
validator: (value: unknown, fieldName: string) => void;
|
|
37
|
+
/**
|
|
38
|
+
* Human-readable description of what this rule validates
|
|
39
|
+
*/
|
|
40
|
+
description: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* GraphQL operation context with validation information
|
|
44
|
+
*/
|
|
45
|
+
export interface ValidationContext {
|
|
46
|
+
/**
|
|
47
|
+
* The GraphQL operation being executed
|
|
48
|
+
*/
|
|
49
|
+
operation: {
|
|
50
|
+
operation: 'query' | 'mutation' | 'subscription';
|
|
51
|
+
name?: {
|
|
52
|
+
value: string;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* The request variables
|
|
57
|
+
*/
|
|
58
|
+
request: {
|
|
59
|
+
variables?: Record<string, unknown>;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Result of a validation check
|
|
64
|
+
*/
|
|
65
|
+
export interface ValidationResult {
|
|
66
|
+
/**
|
|
67
|
+
* Whether validation passed
|
|
68
|
+
*/
|
|
69
|
+
valid: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Array of validation errors (empty if valid)
|
|
72
|
+
*/
|
|
73
|
+
errors: ValidationErrorDetail[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Type guard to check if an error is a ValidationError
|
|
77
|
+
*/
|
|
78
|
+
export declare function isValidationError(error: unknown): error is GraphQLError;
|
|
79
|
+
/**
|
|
80
|
+
* Type-safe validator function signature
|
|
81
|
+
*/
|
|
82
|
+
export type Validator<T> = (value: T, fieldName: string) => void;
|
|
83
|
+
/**
|
|
84
|
+
* Validation constraint types
|
|
85
|
+
*/
|
|
86
|
+
export type ValidationConstraint = 'isNumber' | 'isString' | 'range' | 'positive' | 'negative' | 'notEmpty' | 'email' | 'url' | 'minimum' | 'maximum' | 'sum' | 'comparison' | 'userLimit' | 'maxLength' | 'custom';
|
|
87
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/middleware/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAE1B;;OAEG;IACH,WAAW,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAEpC;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;;OAIG;IACH,SAAS,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAEvD;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,SAAS,EAAE;QACT,SAAS,EAAE,OAAO,GAAG,UAAU,GAAG,cAAc,CAAC;QACjD,IAAI,CAAC,EAAE;YACL,KAAK,EAAE,MAAM,CAAC;SACf,CAAC;KACH,CAAC;IAEF;;OAEG;IACH,OAAO,EAAE;QACP,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACrC,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,KAAK,EAAE,OAAO,CAAC;IAEf;;OAEG;IACH,MAAM,EAAE,qBAAqB,EAAE,CAAC;CACjC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAMvE;AAED;;GAEG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAC5B,UAAU,GACV,UAAU,GACV,OAAO,GACP,UAAU,GACV,UAAU,GACV,UAAU,GACV,OAAO,GACP,KAAK,GACL,SAAS,GACT,SAAS,GACT,KAAK,GACL,YAAY,GACZ,WAAW,GACX,WAAW,GACX,QAAQ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/middleware/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAkFvC;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,OAAO,CACL,KAAK,YAAY,YAAY;QAC7B,KAAK,CAAC,UAAU,EAAE,IAAI,KAAK,gBAAgB;QAC3C,kBAAkB,IAAI,CAAC,KAAK,CAAC,UAAU,IAAI,EAAE,CAAC,CAC/C,CAAC;AACJ,CAAC"}
|