@takaro/http 0.0.15 → 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 +1 -0
- package/dist/middleware/errorHandler.d.ts.map +1 -0
- package/dist/middleware/logger.d.ts +1 -0
- package/dist/middleware/logger.d.ts.map +1 -0
- 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 +2 -5
- package/src/app.ts +1 -3
- package/src/controllers/__tests__/meta.integration.test.ts +1 -0
- 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 +1 -0
- package/src/middleware/basicAuth.ts +34 -0
- 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"}
|
|
@@ -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"}
|
|
@@ -6,3 +6,4 @@ import { NextFunction, Request, Response } from 'express';
|
|
|
6
6
|
export declare const LoggingMiddleware: typeof loggingMiddleware;
|
|
7
7
|
declare function loggingMiddleware(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
8
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"}
|
|
@@ -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": "",
|
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);
|
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;
|
|
@@ -5,6 +5,7 @@ import { HTTP } from '../../main.js';
|
|
|
5
5
|
import { createRateLimitMiddleware } from '../rateLimit.js';
|
|
6
6
|
import { ctx } from '@takaro/util';
|
|
7
7
|
import supertest from 'supertest';
|
|
8
|
+
import { describe, beforeEach, afterEach, after, it } from 'node:test';
|
|
8
9
|
|
|
9
10
|
describe('rateLimit middleware', () => {
|
|
10
11
|
let http: HTTP;
|
|
@@ -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
|
+
}
|
package/tsconfig.json
CHANGED