@takaro/http 0.0.14 → 0.0.18
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/dist/app.d.ts +1 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +1 -2
- package/dist/app.js.map +1 -1
- package/dist/controllers/meta.d.ts +3 -0
- package/dist/controllers/meta.d.ts.map +1 -0
- package/dist/controllers/meta.js +87 -2
- package/dist/controllers/meta.js.map +1 -1
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +1 -0
- package/dist/main.js.map +1 -1
- package/dist/middleware/basicAuth.d.ts +3 -0
- package/dist/middleware/basicAuth.d.ts.map +1 -0
- package/dist/middleware/basicAuth.js +27 -0
- package/dist/middleware/basicAuth.js.map +1 -0
- package/dist/middleware/errorHandler.d.ts +2 -1
- package/dist/middleware/errorHandler.d.ts.map +1 -0
- package/dist/middleware/errorHandler.js +4 -2
- package/dist/middleware/errorHandler.js.map +1 -1
- package/dist/middleware/logger.d.ts +5 -1
- package/dist/middleware/logger.d.ts.map +1 -0
- package/dist/middleware/logger.js.map +1 -1
- package/dist/middleware/metrics.d.ts +1 -0
- package/dist/middleware/metrics.d.ts.map +1 -0
- package/dist/middleware/paginationMiddleware.d.ts +1 -0
- package/dist/middleware/paginationMiddleware.d.ts.map +1 -0
- package/dist/middleware/rateLimit.d.ts +1 -0
- package/dist/middleware/rateLimit.d.ts.map +1 -0
- package/dist/util/apiResponse.d.ts +1 -0
- package/dist/util/apiResponse.d.ts.map +1 -0
- package/package.json +3 -6
- package/src/app.ts +1 -3
- package/src/controllers/__tests__/meta.integration.test.ts +3 -9
- package/src/controllers/meta.ts +87 -2
- package/src/main.ts +1 -0
- package/src/middleware/__tests__/paginationMiddleware.unit.test.ts +1 -0
- package/src/middleware/__tests__/rateLimit.integration.test.ts +14 -12
- package/src/middleware/basicAuth.ts +34 -0
- package/src/middleware/errorHandler.ts +4 -2
- package/src/middleware/logger.ts +1 -1
- package/tsconfig.json +1 -1
package/dist/app.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,OAAwB,MAAM,SAAS,CAAC;AAE/C,OAAO,EAAE,MAAM,EAAgB,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,yBAAyB,EAAoB,MAAM,qBAAqB,CAAC;AAUlF,UAAU,YAAY;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,qBAAa,IAAI;IAOb,OAAO,CAAC,WAAW;IANrB,OAAO,CAAC,GAAG,CAAc;IACzB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAC;gBAGb,OAAO,GAAE,yBAA8B,EAC/B,WAAW,GAAE,YAAiB;IA0DxC,IAAI,eAAe,wBAElB;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;IAEK,KAAK;IAML,IAAI;CAMX"}
|
package/dist/app.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
2
|
import express from 'express';
|
|
3
3
|
import { logger, errors, Sentry } from '@takaro/util';
|
|
4
|
-
import { getBullBoard } from '@takaro/queues';
|
|
5
4
|
import { createServer } from 'node:http';
|
|
6
5
|
import { useExpressServer } from 'routing-controllers';
|
|
7
6
|
import { Meta } from './controllers/meta.js';
|
|
@@ -20,6 +19,7 @@ export class HTTP {
|
|
|
20
19
|
this.httpServer = createServer(this.app);
|
|
21
20
|
this.app.use(Sentry.Handlers.requestHandler());
|
|
22
21
|
this.app.use(bodyParser.json({
|
|
22
|
+
limit: '1mb',
|
|
23
23
|
verify: (req, res, buf) => {
|
|
24
24
|
req.rawBody = buf.toString();
|
|
25
25
|
},
|
|
@@ -62,7 +62,6 @@ export class HTTP {
|
|
|
62
62
|
validation: { whitelist: true, forbidNonWhitelisted: true },
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
|
-
this.app.use('/queues', getBullBoard());
|
|
66
65
|
this.app.use(Sentry.Handlers.errorHandler());
|
|
67
66
|
this.app.use(ErrorHandler);
|
|
68
67
|
}
|
package/dist/app.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,OAAwB,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Response } from 'express';
|
|
1
2
|
import { OpenAPIObject } from 'openapi3-ts';
|
|
2
3
|
export declare class HealthOutputDTO {
|
|
3
4
|
healthy: boolean;
|
|
@@ -10,6 +11,8 @@ export declare class Meta {
|
|
|
10
11
|
healthy: boolean;
|
|
11
12
|
}>;
|
|
12
13
|
getOpenApi(): Promise<OpenAPIObject>;
|
|
14
|
+
getRoot(res: Response): void;
|
|
13
15
|
getOpenApiHtml(): string;
|
|
14
16
|
getMetrics(): Promise<string>;
|
|
15
17
|
}
|
|
18
|
+
//# sourceMappingURL=meta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta.d.ts","sourceRoot":"","sources":["../../src/controllers/meta.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAKnC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAQ5C,qBAAa,eAAe;IAE1B,OAAO,EAAG,OAAO,CAAC;CACnB;AA+DD,qBACa,IAAI;IAGT,SAAS;;;IAMT,YAAY;;;IAMZ,UAAU;IAsHhB,OAAO,CAAQ,GAAG,EAAE,QAAQ;IAK5B,cAAc;IAwCd,UAAU;CAGX"}
|
package/dist/controllers/meta.js
CHANGED
|
@@ -7,13 +7,18 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
|
-
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { Controller, Get, getMetadataArgsStorage, Res } from 'routing-controllers';
|
|
11
14
|
import { validationMetadatasToSchemas } from 'class-validator-jsonschema';
|
|
12
15
|
import { routingControllersToSpec, ResponseSchema } from 'routing-controllers-openapi';
|
|
13
16
|
import { IsBoolean } from 'class-validator';
|
|
14
17
|
import { getMetrics, health } from '@takaro/util';
|
|
15
18
|
import { PERMISSIONS } from '@takaro/auth';
|
|
16
19
|
import { EventMapping } from '@takaro/modules';
|
|
20
|
+
import { randomUUID } from 'crypto';
|
|
21
|
+
import dedent from 'dedent';
|
|
17
22
|
let spec;
|
|
18
23
|
export class HealthOutputDTO {
|
|
19
24
|
}
|
|
@@ -21,6 +26,62 @@ __decorate([
|
|
|
21
26
|
IsBoolean(),
|
|
22
27
|
__metadata("design:type", Boolean)
|
|
23
28
|
], HealthOutputDTO.prototype, "healthy", void 0);
|
|
29
|
+
function addSearchExamples(original) {
|
|
30
|
+
// Copy the spec so we don't mutate the original
|
|
31
|
+
const spec = JSON.parse(JSON.stringify(original));
|
|
32
|
+
// Add some examples and info to all the POST /search endpoints
|
|
33
|
+
Object.keys(spec.paths).forEach((pathKey) => {
|
|
34
|
+
const pathItem = spec?.paths[pathKey];
|
|
35
|
+
Object.keys(pathItem).forEach((method) => {
|
|
36
|
+
const operation = pathItem[method];
|
|
37
|
+
if (method === 'post' && pathKey.endsWith('/search')) {
|
|
38
|
+
const standardExamples = {
|
|
39
|
+
list: {
|
|
40
|
+
summary: 'List all',
|
|
41
|
+
value: {},
|
|
42
|
+
},
|
|
43
|
+
advanced: {
|
|
44
|
+
summary: 'Advanced search',
|
|
45
|
+
description: dedent `All /search endpoints allow you to combine different filters, search terms and ranges.
|
|
46
|
+
Filters are exact matches, search terms are partial matches and ranges are greater than or less than comparisons.
|
|
47
|
+
Ranges allow you to make queries like "all records created in the last 7 days" or "all records with an age greater than 18".
|
|
48
|
+
|
|
49
|
+
In search and filter sections, you pass an array of values for each property
|
|
50
|
+
These values are OR'ed together. So we'll get 2 records back in this case, if the IDs exist.
|
|
51
|
+
|
|
52
|
+
Eg: \`{"filters": {"id": ["${randomUUID()}", "${randomUUID()}"]}}\`
|
|
53
|
+
|
|
54
|
+
Different filters will be AND'ed together.
|
|
55
|
+
This will return all records where the name is John and the age is 19.
|
|
56
|
+
|
|
57
|
+
Eg: \`{"filters": {"name": "John", "age": 19}}\`
|
|
58
|
+
`,
|
|
59
|
+
value: {
|
|
60
|
+
filters: {
|
|
61
|
+
id: ['ea85ddf4-2885-482f-adc6-548fbe3fd8af'],
|
|
62
|
+
},
|
|
63
|
+
greaterThan: { createdAt: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString() },
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
if (!operation.requestBody) {
|
|
68
|
+
operation.requestBody = {
|
|
69
|
+
content: {
|
|
70
|
+
'application/json': {
|
|
71
|
+
examples: standardExamples,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
operation.requestBody.content['application/json'].examples = {
|
|
77
|
+
...standardExamples,
|
|
78
|
+
...operation.requestBody.content['application/json'].examples,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
return spec;
|
|
84
|
+
}
|
|
24
85
|
let Meta = class Meta {
|
|
25
86
|
async getHealth() {
|
|
26
87
|
return { healthy: true };
|
|
@@ -92,11 +153,24 @@ let Meta = class Meta {
|
|
|
92
153
|
const operation = pathItem[method];
|
|
93
154
|
if (operation.operationId === operationId) {
|
|
94
155
|
// Update the description with required permissions
|
|
95
|
-
operation.description = (operation.description || '') +
|
|
156
|
+
operation.description = (operation.description || '') + `\n\n Required permissions: ${requiredPerms}`;
|
|
96
157
|
}
|
|
97
158
|
});
|
|
98
159
|
});
|
|
99
160
|
});
|
|
161
|
+
// Add the operationId to the description, this helps users find the corresponding function call in the API client.
|
|
162
|
+
Object.keys(spec.paths).forEach((pathKey) => {
|
|
163
|
+
const pathItem = spec?.paths[pathKey];
|
|
164
|
+
Object.keys(pathItem).forEach((method) => {
|
|
165
|
+
const operation = pathItem[method];
|
|
166
|
+
// Api client exposes it as roleControllerSearch
|
|
167
|
+
// Current value is RoleController.search so lets adjust
|
|
168
|
+
// Capitalize the part after . and remove the .
|
|
169
|
+
const split = operation.operationId.split('.');
|
|
170
|
+
const cleanOperationId = split[0] + split[1].charAt(0).toUpperCase() + split[1].slice(1);
|
|
171
|
+
operation.description = (operation.description || '') + `<br> OperationId: \`${cleanOperationId}\``;
|
|
172
|
+
});
|
|
173
|
+
});
|
|
100
174
|
if (spec.components?.schemas) {
|
|
101
175
|
spec.components.schemas.PERMISSIONS = {
|
|
102
176
|
enum: Object.values(PERMISSIONS),
|
|
@@ -111,8 +185,12 @@ let Meta = class Meta {
|
|
|
111
185
|
oneOf: [...allEvents.map((e) => ({ $ref: `#/components/schemas/${e}` }))],
|
|
112
186
|
};
|
|
113
187
|
}
|
|
188
|
+
spec = addSearchExamples(spec);
|
|
114
189
|
return spec;
|
|
115
190
|
}
|
|
191
|
+
getRoot(res) {
|
|
192
|
+
return res.redirect('/api.html');
|
|
193
|
+
}
|
|
116
194
|
getOpenApiHtml() {
|
|
117
195
|
return `<!DOCTYPE html>
|
|
118
196
|
<html>
|
|
@@ -175,6 +253,13 @@ __decorate([
|
|
|
175
253
|
__metadata("design:paramtypes", []),
|
|
176
254
|
__metadata("design:returntype", Promise)
|
|
177
255
|
], Meta.prototype, "getOpenApi", null);
|
|
256
|
+
__decorate([
|
|
257
|
+
Get('/'),
|
|
258
|
+
__param(0, Res()),
|
|
259
|
+
__metadata("design:type", Function),
|
|
260
|
+
__metadata("design:paramtypes", [Object]),
|
|
261
|
+
__metadata("design:returntype", void 0)
|
|
262
|
+
], Meta.prototype, "getRoot", null);
|
|
178
263
|
__decorate([
|
|
179
264
|
Get('/api.html'),
|
|
180
265
|
__metadata("design:type", Function),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meta.js","sourceRoot":"","sources":["../../src/controllers/meta.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"meta.js","sourceRoot":"","sources":["../../src/controllers/meta.ts"],"names":[],"mappings":"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"}
|
package/dist/main.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { HTTP } from './app.js';
|
|
2
2
|
export * from './middleware/metrics.js';
|
|
3
3
|
export { createRateLimitMiddleware } from './middleware/rateLimit.js';
|
|
4
|
+
export { getAdminBasicAuth } from './middleware/basicAuth.js';
|
|
4
5
|
export { paginationMiddleware } from './middleware/paginationMiddleware.js';
|
|
5
6
|
export { apiResponse, APIOutput } from './util/apiResponse.js';
|
|
6
7
|
export { ErrorHandler } from './middleware/errorHandler.js';
|
|
8
|
+
//# sourceMappingURL=main.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC"}
|
package/dist/main.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { HTTP } from './app.js';
|
|
2
2
|
export * from './middleware/metrics.js';
|
|
3
3
|
export { createRateLimitMiddleware } from './middleware/rateLimit.js';
|
|
4
|
+
export { getAdminBasicAuth } from './middleware/basicAuth.js';
|
|
4
5
|
export { paginationMiddleware } from './middleware/paginationMiddleware.js';
|
|
5
6
|
export { apiResponse, APIOutput } from './util/apiResponse.js';
|
|
6
7
|
export { ErrorHandler } from './middleware/errorHandler.js';
|
package/dist/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC"}
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"basicAuth.d.ts","sourceRoot":"","sources":["../../src/middleware/basicAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE1D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,SACV,OAAO,OAAO,QAAQ,QAAQ,YAAY,UA8B/E"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function getAdminBasicAuth(secret) {
|
|
2
|
+
return function adminBasicAuth(req, res, next) {
|
|
3
|
+
const auth = req.headers.authorization;
|
|
4
|
+
// If no auth header is present, prompt for credentials
|
|
5
|
+
if (!auth) {
|
|
6
|
+
res.set('WWW-Authenticate', 'Basic realm="Admin Access"');
|
|
7
|
+
res.status(401).send('Authentication required');
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const [type, credentials] = auth.split(' ');
|
|
11
|
+
// Verify auth type is Basic
|
|
12
|
+
if (type !== 'Basic') {
|
|
13
|
+
res.set('WWW-Authenticate', 'Basic realm="Admin Access"');
|
|
14
|
+
res.status(401).send('Invalid authentication type');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
// Decode and verify credentials
|
|
18
|
+
const [username, password] = Buffer.from(credentials, 'base64').toString().split(':');
|
|
19
|
+
if (username !== 'admin' || password !== secret) {
|
|
20
|
+
res.set('WWW-Authenticate', 'Basic realm="Admin Access"');
|
|
21
|
+
res.status(401).send('Invalid credentials');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
next();
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=basicAuth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"basicAuth.js","sourceRoot":"","sources":["../../src/middleware/basicAuth.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,OAAO,SAAS,cAAc,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;QAC5E,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;QAEvC,uDAAuD;QACvD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,GAAG,CAAC,GAAG,CAAC,kBAAkB,EAAE,4BAA4B,CAAC,CAAC;YAC1D,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;YAChD,OAAO;QACT,CAAC;QAED,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAE5C,4BAA4B;QAC5B,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YACrB,GAAG,CAAC,GAAG,CAAC,kBAAkB,EAAE,4BAA4B,CAAC,CAAC;YAC1D,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;YACpD,OAAO;QACT,CAAC;QAED,gCAAgC;QAChC,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAEtF,IAAI,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;YAChD,GAAG,CAAC,GAAG,CAAC,kBAAkB,EAAE,4BAA4B,CAAC,CAAC;YAC1D,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;YAC5C,OAAO;QACT,CAAC;QAED,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from 'express';
|
|
2
|
-
export declare function ErrorHandler(originalError: Error, req: Request, res: Response, _next: NextFunction): Promise<
|
|
2
|
+
export declare function ErrorHandler(originalError: Error, req: Request, res: Response, _next: NextFunction): Promise<void>;
|
|
3
|
+
//# sourceMappingURL=errorHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../src/middleware/errorHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAS1D,wBAAsB,YAAY,CAChC,aAAa,EAAE,KAAK,EACpB,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EAEb,KAAK,EAAE,YAAY,iBA0EpB"}
|
|
@@ -11,7 +11,9 @@ export async function ErrorHandler(originalError, req, res, _next) {
|
|
|
11
11
|
// @ts-expect-error Error typing is weird in ts... but we validate during runtime so should be OK
|
|
12
12
|
const validationErrors = originalError['errors'];
|
|
13
13
|
parsedError = new errors.ValidationError('Validation error', validationErrors);
|
|
14
|
-
log.warn('⚠️ Validation errror', {
|
|
14
|
+
log.warn('⚠️ Validation errror', {
|
|
15
|
+
details: validationErrors.map((e) => JSON.stringify(e.constraints, null, 2)),
|
|
16
|
+
});
|
|
15
17
|
}
|
|
16
18
|
}
|
|
17
19
|
if (originalError instanceof MulterError) {
|
|
@@ -61,6 +63,6 @@ export async function ErrorHandler(originalError, req, res, _next) {
|
|
|
61
63
|
log.warn(`⚠️ FAIL ${req.method} ${req.originalUrl}`, parsedError);
|
|
62
64
|
}
|
|
63
65
|
res.status(status).json(apiResponse({}, { error: parsedError, req, res }));
|
|
64
|
-
|
|
66
|
+
res.end();
|
|
65
67
|
}
|
|
66
68
|
//# sourceMappingURL=errorHandler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../src/middleware/errorHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAErD,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAErC,MAAM,GAAG,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;AAEnC,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,aAAoB,EACpB,GAAY,EACZ,GAAa,EAEb,KAAmB;IAEnB,IAAI,MAAM,GAAG,GAAG,CAAC;IACjB,IAAI,WAAW,GAAG,IAAI,MAAM,CAAC,mBAAmB,EAAE,CAAC;IAEnD,IAAI,aAAa,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;QAC7C,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,EAAE,CAAC;YAClE,iGAAiG;YACjG,MAAM,gBAAgB,GAAG,aAAa,CAAC,QAAQ,CAAsB,CAAC;YACtE,WAAW,GAAG,IAAI,MAAM,CAAC,eAAe,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,CAAC;YAC/E,GAAG,CAAC,IAAI,CAAC,sBAAsB,EAAE,
|
|
1
|
+
{"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../src/middleware/errorHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { NextFunction, Request, Response } from 'express';
|
|
1
2
|
/**
|
|
2
3
|
* This middleware is called very early in the request lifecycle, so it's
|
|
3
4
|
* we leverage this fact to inject the context tracking at this stage
|
|
4
5
|
*/
|
|
5
|
-
export declare const LoggingMiddleware:
|
|
6
|
+
export declare const LoggingMiddleware: typeof loggingMiddleware;
|
|
7
|
+
declare function loggingMiddleware(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/middleware/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQ1D;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAA0C,OAAO,iBAAiB,CAAC;AAEjG,iBAAe,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,iBA+C/E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/middleware/logger.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEpD,MAAM,sBAAsB,GAAG,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAC3D,MAAM,aAAa,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,oBAAoB,CAAC,CAAC;AACrG,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;AAE3B;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/middleware/logger.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEpD,MAAM,sBAAsB,GAAG,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAC3D,MAAM,aAAa,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,oBAAoB,CAAC,CAAC;AACrG,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;AAE3B;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,iBAAiB,CAA6B,CAAC;AAEjG,KAAK,UAAU,iBAAiB,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IAC9E,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QACrE,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAElC,MAAM,QAAQ,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IAE9G,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,WAAW,EAAE,EAAE;QAC/C,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,IAAI,EAAE,GAAG,CAAC,WAAW;QACrB,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI;KACxD,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAE7C,IAAI,IAAI,EAAE,CAAC;QACT,2DAA2D;QAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC;QAC3C,GAAG,CAAC,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACpC,CAAC;IAED,6CAA6C;IAC7C,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE;QACtB,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,cAAc,CAAC;QAEjD,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,WAAW,EAAE,EAAE;YAC9C,YAAY;YACZ,aAAa,EAAE,GAAG,CAAC,MAAM;YACzB,UAAU,EAAE,GAAG,CAAC,WAAW;YAC3B,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC;YAC1C,MAAM,EAAE,GAAG,CAAC,UAAU;YACtB,YAAY,EAAE,GAAG,CAAC,SAAS,CAAC,gBAAgB,CAAC;YAC7C,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC;YAChC,QAAQ,EAAE,GAAG,CAAC,EAAE;YAChB,QAAQ,EAAE,WAAW;YACrB,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC;YAC3B,WAAW,EAAE,KAAK;YAClB,QAAQ,EAAE,KAAK;YACf,8BAA8B,EAAE,KAAK;YACrC,QAAQ,EAAE,GAAG,CAAC,QAAQ;SACvB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAI,EAAE,CAAC;AACT,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/middleware/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAgB1D,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,iBAyBtF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paginationMiddleware.d.ts","sourceRoot":"","sources":["../../src/middleware/paginationMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAW1D,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBzG"}
|
|
@@ -6,3 +6,4 @@ export interface IRateLimitMiddlewareOptions {
|
|
|
6
6
|
useInMemory?: boolean;
|
|
7
7
|
}
|
|
8
8
|
export declare function createRateLimitMiddleware(opts: IRateLimitMiddlewareOptions): Promise<(req: Request, res: Response, next: NextFunction) => Promise<void>>;
|
|
9
|
+
//# sourceMappingURL=rateLimit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimit.d.ts","sourceRoot":"","sources":["../../src/middleware/rateLimit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAK1D,MAAM,WAAW,2BAA2B;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,2BAA2B,iBAyB5D,OAAO,OAAO,QAAQ,QAAQ,YAAY,oBAmC9D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiResponse.d.ts","sourceRoot":"","sources":["../../src/util/apiResponse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9D,OAAO,EAML,eAAe,IAAI,mBAAmB,EACvC,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE5C,cAAM,WAAW;IAEf,IAAI,CAAC,EAAE,MAAM,CAAC;IAGd,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,OAAO,CAAC,EAAE,MAAM,GAAG,mBAAmB,EAAE,CAAC;CAC1C;AAED,cAAM,cAAc;IAGlB,UAAU,EAAG,MAAM,CAAC;IAIpB,KAAK,CAAC,EAAE,WAAW,CAAC;IAEpB;;OAEG;IAGH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IAGH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IAGH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AACD,qBAAa,SAAS,CAAC,CAAC,CAAE,SAAQ,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAGvD,IAAI,EAAG,cAAc,CAAC;IAEtB,IAAI,EAAG,CAAC,CAAC;CACV;AAED,UAAU,mBAAmB;IAC3B,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;IACvC,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,QAAQ,CAAC;CACf;AAED,wBAAgB,WAAW,CAAC,IAAI,GAAE,OAAY,EAAE,IAAI,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC,OAAO,CAAC,CA2C9F"}
|
package/package.json
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@takaro/http",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.18",
|
|
4
4
|
"description": "An opinionated http server",
|
|
5
5
|
"main": "dist/main.js",
|
|
6
6
|
"types": "dist/main.d.ts",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"start:dev": "tsc --watch --preserveWatchOutput -p ./tsconfig.build.json",
|
|
10
|
-
"build": "tsc -p ./tsconfig.build.json"
|
|
11
|
-
"test": "npm run test:unit --if-present && npm run test:integration --if-present",
|
|
12
|
-
"test:unit": "mocha --config ../../.mocharc.js src/**/*.unit.test.ts --exit",
|
|
13
|
-
"test:integration": "mocha --config ../../.mocharc.js src/**/*.integration.test.ts --exit"
|
|
10
|
+
"build": "tsc -p ./tsconfig.build.json"
|
|
14
11
|
},
|
|
15
12
|
"keywords": [],
|
|
16
13
|
"author": "",
|
|
@@ -27,7 +24,7 @@
|
|
|
27
24
|
"devDependencies": {
|
|
28
25
|
"@types/cookie-parser": "1.4.7",
|
|
29
26
|
"@types/cors": "2.8.17",
|
|
30
|
-
"@types/express": "
|
|
27
|
+
"@types/express": "5.0.0",
|
|
31
28
|
"supertest": "6.3.4"
|
|
32
29
|
}
|
|
33
30
|
}
|
package/src/app.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
2
|
import express, { Application } from 'express';
|
|
3
3
|
import { logger, errors, Sentry } from '@takaro/util';
|
|
4
|
-
import { getBullBoard } from '@takaro/queues';
|
|
5
4
|
import { Server, createServer } from 'node:http';
|
|
6
5
|
import { RoutingControllersOptions, useExpressServer } from 'routing-controllers';
|
|
7
6
|
import { Meta } from './controllers/meta.js';
|
|
@@ -33,6 +32,7 @@ export class HTTP {
|
|
|
33
32
|
this.app.use(Sentry.Handlers.requestHandler());
|
|
34
33
|
this.app.use(
|
|
35
34
|
bodyParser.json({
|
|
35
|
+
limit: '1mb',
|
|
36
36
|
verify: (req, res, buf) => {
|
|
37
37
|
(req as any).rawBody = buf.toString();
|
|
38
38
|
},
|
|
@@ -77,8 +77,6 @@ export class HTTP {
|
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
this.app.use('/queues', getBullBoard());
|
|
81
|
-
|
|
82
80
|
this.app.use(Sentry.Handlers.errorHandler());
|
|
83
81
|
|
|
84
82
|
this.app.use(ErrorHandler);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HTTP } from '../../app.js';
|
|
2
2
|
import supertest from 'supertest';
|
|
3
|
-
import {
|
|
3
|
+
import { describe, it, before, after } from 'node:test';
|
|
4
4
|
|
|
5
5
|
describe('app', () => {
|
|
6
6
|
let http: HTTP;
|
|
@@ -14,16 +14,10 @@ describe('app', () => {
|
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
it('Serves a health status', async () => {
|
|
17
|
-
|
|
18
|
-
// @ts-ignore
|
|
19
|
-
const response = await supertest(http.expressInstance).get('/healthz');
|
|
20
|
-
expect(response.status).to.be.equal(200);
|
|
17
|
+
await supertest(http.expressInstance).get('/healthz').expect(200);
|
|
21
18
|
});
|
|
22
19
|
|
|
23
20
|
it('Serves a open api spec', async () => {
|
|
24
|
-
|
|
25
|
-
// @ts-ignore
|
|
26
|
-
const response = await supertest(http.expressInstance).get('/openapi.json');
|
|
27
|
-
expect(response.status).to.be.equal(200);
|
|
21
|
+
await supertest(http.expressInstance).get('/openapi.json').expect(200);
|
|
28
22
|
});
|
|
29
23
|
});
|
package/src/controllers/meta.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Controller, Get, getMetadataArgsStorage } from 'routing-controllers';
|
|
1
|
+
import { Controller, Get, getMetadataArgsStorage, Res } from 'routing-controllers';
|
|
2
|
+
import { Response } from 'express';
|
|
2
3
|
import { validationMetadatasToSchemas } from 'class-validator-jsonschema';
|
|
3
4
|
import { routingControllersToSpec, ResponseSchema } from 'routing-controllers-openapi';
|
|
4
5
|
import { IsBoolean } from 'class-validator';
|
|
@@ -6,6 +7,8 @@ import { getMetrics, health } from '@takaro/util';
|
|
|
6
7
|
import { OpenAPIObject } from 'openapi3-ts';
|
|
7
8
|
import { PERMISSIONS } from '@takaro/auth';
|
|
8
9
|
import { EventMapping } from '@takaro/modules';
|
|
10
|
+
import { randomUUID } from 'crypto';
|
|
11
|
+
import dedent from 'dedent';
|
|
9
12
|
|
|
10
13
|
let spec: OpenAPIObject | undefined;
|
|
11
14
|
|
|
@@ -13,6 +16,68 @@ export class HealthOutputDTO {
|
|
|
13
16
|
@IsBoolean()
|
|
14
17
|
healthy!: boolean;
|
|
15
18
|
}
|
|
19
|
+
|
|
20
|
+
function addSearchExamples(original: OpenAPIObject): OpenAPIObject {
|
|
21
|
+
// Copy the spec so we don't mutate the original
|
|
22
|
+
const spec = JSON.parse(JSON.stringify(original));
|
|
23
|
+
|
|
24
|
+
// Add some examples and info to all the POST /search endpoints
|
|
25
|
+
Object.keys(spec.paths).forEach((pathKey) => {
|
|
26
|
+
const pathItem = spec?.paths[pathKey];
|
|
27
|
+
Object.keys(pathItem).forEach((method) => {
|
|
28
|
+
const operation = pathItem[method];
|
|
29
|
+
if (method === 'post' && pathKey.endsWith('/search')) {
|
|
30
|
+
const standardExamples = {
|
|
31
|
+
list: {
|
|
32
|
+
summary: 'List all',
|
|
33
|
+
value: {},
|
|
34
|
+
},
|
|
35
|
+
advanced: {
|
|
36
|
+
summary: 'Advanced search',
|
|
37
|
+
description: dedent`All /search endpoints allow you to combine different filters, search terms and ranges.
|
|
38
|
+
Filters are exact matches, search terms are partial matches and ranges are greater than or less than comparisons.
|
|
39
|
+
Ranges allow you to make queries like "all records created in the last 7 days" or "all records with an age greater than 18".
|
|
40
|
+
|
|
41
|
+
In search and filter sections, you pass an array of values for each property
|
|
42
|
+
These values are OR'ed together. So we'll get 2 records back in this case, if the IDs exist.
|
|
43
|
+
|
|
44
|
+
Eg: \`{"filters": {"id": ["${randomUUID()}", "${randomUUID()}"]}}\`
|
|
45
|
+
|
|
46
|
+
Different filters will be AND'ed together.
|
|
47
|
+
This will return all records where the name is John and the age is 19.
|
|
48
|
+
|
|
49
|
+
Eg: \`{"filters": {"name": "John", "age": 19}}\`
|
|
50
|
+
`,
|
|
51
|
+
value: {
|
|
52
|
+
filters: {
|
|
53
|
+
id: ['ea85ddf4-2885-482f-adc6-548fbe3fd8af'],
|
|
54
|
+
},
|
|
55
|
+
greaterThan: { createdAt: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString() },
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if (!operation.requestBody) {
|
|
61
|
+
operation.requestBody = {
|
|
62
|
+
content: {
|
|
63
|
+
'application/json': {
|
|
64
|
+
examples: standardExamples,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
operation.requestBody.content['application/json'].examples = {
|
|
71
|
+
...standardExamples,
|
|
72
|
+
...operation.requestBody.content['application/json'].examples,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return spec;
|
|
79
|
+
}
|
|
80
|
+
|
|
16
81
|
@Controller()
|
|
17
82
|
export class Meta {
|
|
18
83
|
@Get('/healthz')
|
|
@@ -105,12 +170,26 @@ export class Meta {
|
|
|
105
170
|
const operation = pathItem[method];
|
|
106
171
|
if (operation.operationId === operationId) {
|
|
107
172
|
// Update the description with required permissions
|
|
108
|
-
operation.description = (operation.description || '') +
|
|
173
|
+
operation.description = (operation.description || '') + `\n\n Required permissions: ${requiredPerms}`;
|
|
109
174
|
}
|
|
110
175
|
});
|
|
111
176
|
});
|
|
112
177
|
});
|
|
113
178
|
|
|
179
|
+
// Add the operationId to the description, this helps users find the corresponding function call in the API client.
|
|
180
|
+
Object.keys(spec.paths).forEach((pathKey) => {
|
|
181
|
+
const pathItem = spec?.paths[pathKey];
|
|
182
|
+
Object.keys(pathItem).forEach((method) => {
|
|
183
|
+
const operation = pathItem[method];
|
|
184
|
+
// Api client exposes it as roleControllerSearch
|
|
185
|
+
// Current value is RoleController.search so lets adjust
|
|
186
|
+
// Capitalize the part after . and remove the .
|
|
187
|
+
const split = operation.operationId.split('.');
|
|
188
|
+
const cleanOperationId = split[0] + split[1].charAt(0).toUpperCase() + split[1].slice(1);
|
|
189
|
+
operation.description = (operation.description || '') + `<br> OperationId: \`${cleanOperationId}\``;
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
114
193
|
if (spec.components?.schemas) {
|
|
115
194
|
spec.components.schemas.PERMISSIONS = {
|
|
116
195
|
enum: Object.values(PERMISSIONS),
|
|
@@ -128,9 +207,15 @@ export class Meta {
|
|
|
128
207
|
};
|
|
129
208
|
}
|
|
130
209
|
|
|
210
|
+
spec = addSearchExamples(spec);
|
|
131
211
|
return spec;
|
|
132
212
|
}
|
|
133
213
|
|
|
214
|
+
@Get('/')
|
|
215
|
+
getRoot(@Res() res: Response) {
|
|
216
|
+
return res.redirect('/api.html');
|
|
217
|
+
}
|
|
218
|
+
|
|
134
219
|
@Get('/api.html')
|
|
135
220
|
getOpenApiHtml() {
|
|
136
221
|
return `<!DOCTYPE html>
|
package/src/main.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { HTTP } from './app.js';
|
|
|
2
2
|
|
|
3
3
|
export * from './middleware/metrics.js';
|
|
4
4
|
export { createRateLimitMiddleware } from './middleware/rateLimit.js';
|
|
5
|
+
export { getAdminBasicAuth } from './middleware/basicAuth.js';
|
|
5
6
|
export { paginationMiddleware } from './middleware/paginationMiddleware.js';
|
|
6
7
|
export { apiResponse, APIOutput } from './util/apiResponse.js';
|
|
7
8
|
export { ErrorHandler } from './middleware/errorHandler.js';
|
|
@@ -2,6 +2,7 @@ import { sandbox, expect } from '@takaro/test';
|
|
|
2
2
|
import { NextFunction, Request, Response } from 'express';
|
|
3
3
|
import { errors } from '@takaro/util';
|
|
4
4
|
import { paginationMiddleware } from '../paginationMiddleware.js';
|
|
5
|
+
import { describe, it } from 'node:test';
|
|
5
6
|
|
|
6
7
|
async function runPagination(page?: number, limit?: number) {
|
|
7
8
|
const req = { query: { page, limit } } as unknown as Request;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Response, NextFunction, Request } from 'express';
|
|
2
2
|
import { Redis } from '@takaro/db';
|
|
3
|
-
import { expect } from '@takaro/test';
|
|
4
3
|
import { Controller, UseBefore, Get } from 'routing-controllers';
|
|
5
4
|
import { HTTP } from '../../main.js';
|
|
6
5
|
import { createRateLimitMiddleware } from '../rateLimit.js';
|
|
7
6
|
import { ctx } from '@takaro/util';
|
|
8
7
|
import supertest from 'supertest';
|
|
8
|
+
import { describe, beforeEach, afterEach, after, it } from 'node:test';
|
|
9
9
|
|
|
10
10
|
describe('rateLimit middleware', () => {
|
|
11
11
|
let http: HTTP;
|
|
@@ -112,17 +112,19 @@ describe('rateLimit middleware', () => {
|
|
|
112
112
|
const agent = supertest(http.expressInstance);
|
|
113
113
|
|
|
114
114
|
for (let i = 1; i < 5; i++) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
115
|
+
await agent
|
|
116
|
+
.get('/low-limit')
|
|
117
|
+
.expect(200)
|
|
118
|
+
.expect('x-ratelimit-remaining', (5 - i).toString())
|
|
119
|
+
.expect('x-ratelimit-limit', '5')
|
|
120
|
+
.expect('x-ratelimit-reset', /\d+/);
|
|
121
121
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
122
|
+
|
|
123
|
+
agent
|
|
124
|
+
.get('/low-limit')
|
|
125
|
+
.expect(429)
|
|
126
|
+
.expect('x-ratelimit-remaining', '0')
|
|
127
|
+
.expect('x-ratelimit-limit', '5')
|
|
128
|
+
.expect('x-ratelimit-reset', /\d+/);
|
|
127
129
|
});
|
|
128
130
|
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
export function getAdminBasicAuth(secret: string) {
|
|
4
|
+
return function adminBasicAuth(req: Request, res: Response, next: NextFunction) {
|
|
5
|
+
const auth = req.headers.authorization;
|
|
6
|
+
|
|
7
|
+
// If no auth header is present, prompt for credentials
|
|
8
|
+
if (!auth) {
|
|
9
|
+
res.set('WWW-Authenticate', 'Basic realm="Admin Access"');
|
|
10
|
+
res.status(401).send('Authentication required');
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const [type, credentials] = auth.split(' ');
|
|
15
|
+
|
|
16
|
+
// Verify auth type is Basic
|
|
17
|
+
if (type !== 'Basic') {
|
|
18
|
+
res.set('WWW-Authenticate', 'Basic realm="Admin Access"');
|
|
19
|
+
res.status(401).send('Invalid authentication type');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Decode and verify credentials
|
|
24
|
+
const [username, password] = Buffer.from(credentials, 'base64').toString().split(':');
|
|
25
|
+
|
|
26
|
+
if (username !== 'admin' || password !== secret) {
|
|
27
|
+
res.set('WWW-Authenticate', 'Basic realm="Admin Access"');
|
|
28
|
+
res.status(401).send('Invalid credentials');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
next();
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -22,7 +22,9 @@ export async function ErrorHandler(
|
|
|
22
22
|
// @ts-expect-error Error typing is weird in ts... but we validate during runtime so should be OK
|
|
23
23
|
const validationErrors = originalError['errors'] as ValidationError[];
|
|
24
24
|
parsedError = new errors.ValidationError('Validation error', validationErrors);
|
|
25
|
-
log.warn('⚠️ Validation errror', {
|
|
25
|
+
log.warn('⚠️ Validation errror', {
|
|
26
|
+
details: validationErrors.map((e) => JSON.stringify(e.constraints, null, 2)),
|
|
27
|
+
});
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
|
|
@@ -83,5 +85,5 @@ export async function ErrorHandler(
|
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
res.status(status).json(apiResponse({}, { error: parsedError, req, res }));
|
|
86
|
-
|
|
88
|
+
res.end();
|
|
87
89
|
}
|
package/src/middleware/logger.ts
CHANGED
|
@@ -10,7 +10,7 @@ const log = logger('http');
|
|
|
10
10
|
* This middleware is called very early in the request lifecycle, so it's
|
|
11
11
|
* we leverage this fact to inject the context tracking at this stage
|
|
12
12
|
*/
|
|
13
|
-
export const LoggingMiddleware = ctx.wrap('HTTP', loggingMiddleware);
|
|
13
|
+
export const LoggingMiddleware = ctx.wrap('HTTP', loggingMiddleware) as typeof loggingMiddleware;
|
|
14
14
|
|
|
15
15
|
async function loggingMiddleware(req: Request, res: Response, next: NextFunction) {
|
|
16
16
|
if (HIDDEN_ROUTES.some((route) => req.originalUrl.startsWith(route))) {
|
package/tsconfig.json
CHANGED