@takaro/http 0.0.0-next.0da151e

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.
Files changed (59) hide show
  1. package/README.md +7 -0
  2. package/dist/app.d.ts +21 -0
  3. package/dist/app.d.ts.map +1 -0
  4. package/dist/app.js +86 -0
  5. package/dist/app.js.map +1 -0
  6. package/dist/controllers/meta.d.ts +18 -0
  7. package/dist/controllers/meta.d.ts.map +1 -0
  8. package/dist/controllers/meta.js +284 -0
  9. package/dist/controllers/meta.js.map +1 -0
  10. package/dist/main.d.ts +8 -0
  11. package/dist/main.d.ts.map +1 -0
  12. package/dist/main.js +8 -0
  13. package/dist/main.js.map +1 -0
  14. package/dist/middleware/basicAuth.d.ts +3 -0
  15. package/dist/middleware/basicAuth.d.ts.map +1 -0
  16. package/dist/middleware/basicAuth.js +27 -0
  17. package/dist/middleware/basicAuth.js.map +1 -0
  18. package/dist/middleware/errorHandler.d.ts +3 -0
  19. package/dist/middleware/errorHandler.d.ts.map +1 -0
  20. package/dist/middleware/errorHandler.js +73 -0
  21. package/dist/middleware/errorHandler.js.map +1 -0
  22. package/dist/middleware/logger.d.ts +9 -0
  23. package/dist/middleware/logger.d.ts.map +1 -0
  24. package/dist/middleware/logger.js +51 -0
  25. package/dist/middleware/logger.js.map +1 -0
  26. package/dist/middleware/metrics.d.ts +3 -0
  27. package/dist/middleware/metrics.d.ts.map +1 -0
  28. package/dist/middleware/metrics.js +35 -0
  29. package/dist/middleware/metrics.js.map +1 -0
  30. package/dist/middleware/paginationMiddleware.d.ts +3 -0
  31. package/dist/middleware/paginationMiddleware.d.ts.map +1 -0
  32. package/dist/middleware/paginationMiddleware.js +26 -0
  33. package/dist/middleware/paginationMiddleware.js.map +1 -0
  34. package/dist/middleware/rateLimit.d.ts +9 -0
  35. package/dist/middleware/rateLimit.d.ts.map +1 -0
  36. package/dist/middleware/rateLimit.js +60 -0
  37. package/dist/middleware/rateLimit.js.map +1 -0
  38. package/dist/util/apiResponse.d.ts +37 -0
  39. package/dist/util/apiResponse.d.ts.map +1 -0
  40. package/dist/util/apiResponse.js +99 -0
  41. package/dist/util/apiResponse.js.map +1 -0
  42. package/package.json +15 -0
  43. package/src/app.ts +105 -0
  44. package/src/controllers/__tests__/meta.integration.test.ts +23 -0
  45. package/src/controllers/defaultMetadatastorage.d.ts +5 -0
  46. package/src/controllers/meta.ts +268 -0
  47. package/src/main.ts +8 -0
  48. package/src/middleware/__tests__/paginationMiddleware.unit.test.ts +49 -0
  49. package/src/middleware/__tests__/rateLimit.integration.test.ts +130 -0
  50. package/src/middleware/basicAuth.ts +34 -0
  51. package/src/middleware/errorHandler.ts +95 -0
  52. package/src/middleware/logger.ts +62 -0
  53. package/src/middleware/metrics.ts +42 -0
  54. package/src/middleware/paginationMiddleware.ts +29 -0
  55. package/src/middleware/rateLimit.ts +73 -0
  56. package/src/util/apiResponse.ts +112 -0
  57. package/tsconfig.build.json +9 -0
  58. package/tsconfig.json +8 -0
  59. package/typedoc.json +3 -0
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # @takaro/http
2
+
3
+ ## TODO
4
+
5
+ - [ ] Middlewares
6
+ - [ ] Validation middleware (celebrate?)
7
+ - [ ]
package/dist/app.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ import 'reflect-metadata';
2
+ import express from 'express';
3
+ import { Server } from 'node:http';
4
+ import { RoutingControllersOptions } from 'routing-controllers';
5
+ interface IHTTPOptions {
6
+ port?: number;
7
+ allowedOrigins?: string[];
8
+ }
9
+ export declare class HTTP {
10
+ private httpOptions;
11
+ private app;
12
+ private httpServer;
13
+ private logger;
14
+ constructor(options?: RoutingControllersOptions, httpOptions?: IHTTPOptions);
15
+ get expressInstance(): express.Application;
16
+ get server(): Server;
17
+ start(): Promise<void>;
18
+ stop(): Promise<void>;
19
+ }
20
+ export {};
21
+ //# sourceMappingURL=app.d.ts.map
@@ -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 ADDED
@@ -0,0 +1,86 @@
1
+ import 'reflect-metadata';
2
+ import express from 'express';
3
+ import { logger, errors, Sentry } from '@takaro/util';
4
+ import { createServer } from 'node:http';
5
+ import { useExpressServer } from 'routing-controllers';
6
+ import { Meta } from './controllers/meta.js';
7
+ import { LoggingMiddleware } from './middleware/logger.js';
8
+ import { ErrorHandler } from './middleware/errorHandler.js';
9
+ import bodyParser from 'body-parser';
10
+ import cors from 'cors';
11
+ import cookieParser from 'cookie-parser';
12
+ import { metricsMiddleware } from './main.js';
13
+ import { paginationMiddleware } from './middleware/paginationMiddleware.js';
14
+ export class HTTP {
15
+ constructor(options = {}, httpOptions = {}) {
16
+ this.httpOptions = httpOptions;
17
+ this.logger = logger('http');
18
+ this.app = express();
19
+ this.httpServer = createServer(this.app);
20
+ this.app.use(Sentry.Handlers.requestHandler());
21
+ this.app.use(bodyParser.json({
22
+ limit: '1mb',
23
+ verify: (req, res, buf) => {
24
+ req.rawBody = buf.toString();
25
+ },
26
+ }));
27
+ this.app.use(LoggingMiddleware);
28
+ this.app.use(metricsMiddleware);
29
+ this.app.use(paginationMiddleware);
30
+ this.app.set('x-powered-by', false);
31
+ this.app.use(cors({
32
+ credentials: true,
33
+ exposedHeaders: ['X-Trace-Id'],
34
+ origin: (origin, callback) => {
35
+ if (!origin)
36
+ return callback(null, true);
37
+ const allowedOrigins = this.httpOptions.allowedOrigins ?? [];
38
+ if (!origin || allowedOrigins.includes(origin)) {
39
+ callback(null, true);
40
+ }
41
+ else {
42
+ this.logger.warn(`Origin ${origin} not allowed by CORS`);
43
+ callback(new errors.BadRequestError('Not allowed by CORS'));
44
+ }
45
+ },
46
+ }));
47
+ this.app.use(cookieParser());
48
+ if (options.controllers) {
49
+ useExpressServer(this.app, {
50
+ ...options,
51
+ defaultErrorHandler: false,
52
+ validation: { whitelist: true, forbidNonWhitelisted: true },
53
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
54
+ controllers: [Meta, ...options.controllers],
55
+ });
56
+ }
57
+ else {
58
+ useExpressServer(this.app, {
59
+ ...options,
60
+ defaultErrorHandler: false,
61
+ controllers: [Meta],
62
+ validation: { whitelist: true, forbidNonWhitelisted: true },
63
+ });
64
+ }
65
+ this.app.use(Sentry.Handlers.errorHandler());
66
+ this.app.use(ErrorHandler);
67
+ }
68
+ get expressInstance() {
69
+ return this.app;
70
+ }
71
+ get server() {
72
+ return this.httpServer;
73
+ }
74
+ async start() {
75
+ this.httpServer = this.httpServer.listen(this.httpOptions.port, () => {
76
+ this.logger.info(`HTTP server listening on port ${this.httpOptions.port}`);
77
+ });
78
+ }
79
+ async stop() {
80
+ if (this.httpServer) {
81
+ this.httpServer.close();
82
+ this.logger.info('HTTP server stopped');
83
+ }
84
+ }
85
+ }
86
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { Response } from 'express';
2
+ import { OpenAPIObject } from 'openapi3-ts';
3
+ export declare class HealthOutputDTO {
4
+ healthy: boolean;
5
+ }
6
+ export declare class Meta {
7
+ getHealth(): Promise<{
8
+ healthy: boolean;
9
+ }>;
10
+ getReadiness(): Promise<{
11
+ healthy: boolean;
12
+ }>;
13
+ getOpenApi(): Promise<OpenAPIObject>;
14
+ getRoot(res: Response): void;
15
+ getOpenApiHtml(): string;
16
+ getMetrics(): Promise<string>;
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;AAoED,qBACa,IAAI;IAGT,SAAS;;;IAMT,YAAY;;;IAMZ,UAAU;IAsHhB,OAAO,CAAQ,GAAG,EAAE,QAAQ;IAK5B,cAAc;IAwCd,UAAU;CAGX"}
@@ -0,0 +1,284 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
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';
14
+ import { validationMetadatasToSchemas } from 'class-validator-jsonschema';
15
+ import { routingControllersToSpec, ResponseSchema } from 'routing-controllers-openapi';
16
+ import { IsBoolean } from 'class-validator';
17
+ import { getMetrics, health } from '@takaro/util';
18
+ import { PERMISSIONS } from '@takaro/auth';
19
+ import { EventMapping } from '@takaro/modules';
20
+ import { randomUUID } from 'crypto';
21
+ import dedent from 'dedent';
22
+ let spec;
23
+ export class HealthOutputDTO {
24
+ }
25
+ __decorate([
26
+ IsBoolean(),
27
+ __metadata("design:type", Boolean)
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, ranges, and extend options.
46
+ Filters are exact matches, search terms are partial matches, ranges are greater than or less than comparisons,
47
+ and extend allows you to include related entities in the response.
48
+
49
+ Ranges allow you to make queries like "all records created in the last 7 days" or "all records with an age greater than 18".
50
+
51
+ In search and filter sections, you pass an array of values for each property
52
+ These values are OR'ed together. So we'll get 2 records back in this case, if the IDs exist.
53
+
54
+ Eg: \`{"filters": {"id": ["${randomUUID()}", "${randomUUID()}"]}}\`
55
+
56
+ Different filters will be AND'ed together.
57
+ This will return all records where the name is John and the age is 19.
58
+
59
+ Eg: \`{"filters": {"name": "John", "age": 19}}\`
60
+
61
+ The extend parameter allows including related data:
62
+ Eg: \`{"extend": ["roles", "gameServers"]}\`
63
+ `,
64
+ value: {
65
+ filters: {
66
+ id: ['ea85ddf4-2885-482f-adc6-548fbe3fd8af'],
67
+ },
68
+ greaterThan: { createdAt: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString() },
69
+ },
70
+ },
71
+ };
72
+ if (!operation.requestBody) {
73
+ operation.requestBody = {
74
+ content: {
75
+ 'application/json': {
76
+ examples: standardExamples,
77
+ },
78
+ },
79
+ };
80
+ }
81
+ operation.requestBody.content['application/json'].examples = {
82
+ ...standardExamples,
83
+ ...operation.requestBody.content['application/json'].examples,
84
+ };
85
+ }
86
+ });
87
+ });
88
+ return spec;
89
+ }
90
+ let Meta = class Meta {
91
+ async getHealth() {
92
+ return { healthy: true };
93
+ }
94
+ async getReadiness() {
95
+ const healthy = await health.check();
96
+ return { healthy };
97
+ }
98
+ async getOpenApi() {
99
+ if (spec)
100
+ return spec;
101
+ const { getMetadataStorage } = await import('class-validator');
102
+ const classTransformerStorage = await import(
103
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
104
+ // @ts-ignore were doing an import of internal code and ts doesnt like that
105
+ // But this does work, trust me bro...
106
+ 'class-transformer/cjs/storage.js');
107
+ const metadataArgsStorage = getMetadataArgsStorage();
108
+ const metadataStorage = getMetadataStorage();
109
+ const schemas = validationMetadatasToSchemas({
110
+ refPointerPrefix: '#/components/schemas/',
111
+ classTransformerMetadataStorage: classTransformerStorage.defaultMetadataStorage,
112
+ classValidatorMetadataStorage: metadataStorage,
113
+ forbidNonWhitelisted: true,
114
+ });
115
+ spec = routingControllersToSpec(metadataArgsStorage, {}, {
116
+ info: {
117
+ title: `Takaro ${process.env.PACKAGE || 'API'}`,
118
+ version: `${process.env.TAKARO_VERSION} - ${process.env.TAKARO_COMMIT} `,
119
+ contact: {
120
+ name: 'Takaro Team',
121
+ email: 'support@takaro.io',
122
+ url: 'https://takaro.io',
123
+ },
124
+ },
125
+ components: {
126
+ schemas,
127
+ securitySchemes: {
128
+ adminAuth: {
129
+ description: 'Used for system administration, like creating or deleting domains',
130
+ type: 'apiKey',
131
+ in: 'header',
132
+ name: 'x-takaro-admin-token',
133
+ },
134
+ domainAuth: {
135
+ description: 'Used for anything inside a domain. Players, GameServers, etc.',
136
+ type: 'apiKey',
137
+ in: 'cookie',
138
+ name: 'takaro-token',
139
+ },
140
+ },
141
+ },
142
+ });
143
+ // Add required permissions to operation descriptions
144
+ const requiredPermsRegex = /authMiddleware\((.+)\)/;
145
+ metadataArgsStorage.uses.forEach((use) => {
146
+ const requiredPerms = use.middleware.name
147
+ .match(requiredPermsRegex)?.[1]
148
+ .split(',')
149
+ .map((p) => `\`${p}\``)
150
+ .join(', ') || [];
151
+ const operationId = `${use.target.name}.${use.method}`;
152
+ if (!requiredPerms.length)
153
+ return;
154
+ // Find the corresponding path and method in spec
155
+ Object.keys(spec?.paths ?? []).forEach((pathKey) => {
156
+ const pathItem = spec?.paths[pathKey];
157
+ Object.keys(pathItem).forEach((method) => {
158
+ const operation = pathItem[method];
159
+ if (operation.operationId === operationId) {
160
+ // Update the description with required permissions
161
+ operation.description = (operation.description || '') + `\n\n Required permissions: ${requiredPerms}`;
162
+ }
163
+ });
164
+ });
165
+ });
166
+ // Add the operationId to the description, this helps users find the corresponding function call in the API client.
167
+ Object.keys(spec.paths).forEach((pathKey) => {
168
+ const pathItem = spec?.paths[pathKey];
169
+ Object.keys(pathItem).forEach((method) => {
170
+ const operation = pathItem[method];
171
+ // Api client exposes it as roleControllerSearch
172
+ // Current value is RoleController.search so lets adjust
173
+ // Capitalize the part after . and remove the .
174
+ const split = operation.operationId.split('.');
175
+ const cleanOperationId = split[0] + split[1].charAt(0).toUpperCase() + split[1].slice(1);
176
+ operation.description = (operation.description || '') + `<br> OperationId: \`${cleanOperationId}\``;
177
+ });
178
+ });
179
+ if (spec.components?.schemas) {
180
+ spec.components.schemas.PERMISSIONS = {
181
+ enum: Object.values(PERMISSIONS),
182
+ };
183
+ }
184
+ // Force event meta to be the correct types
185
+ // TODO: figure out how to do this 'properly' with class-validator
186
+ const allEvents = Object.values(EventMapping).map((e) => e.name);
187
+ const eventOutputMetaSchema = spec.components?.schemas?.EventOutputDTO;
188
+ if (eventOutputMetaSchema && 'properties' in eventOutputMetaSchema && eventOutputMetaSchema.properties) {
189
+ eventOutputMetaSchema.properties.meta = {
190
+ oneOf: [...allEvents.map((e) => ({ $ref: `#/components/schemas/${e}` }))],
191
+ };
192
+ }
193
+ spec = addSearchExamples(spec);
194
+ return spec;
195
+ }
196
+ getRoot(res) {
197
+ return res.redirect('/api.html');
198
+ }
199
+ getOpenApiHtml() {
200
+ return `<!DOCTYPE html>
201
+ <html>
202
+ <head>
203
+ <meta charset="utf-8" />
204
+ <script
205
+ type="module"
206
+ src="https://cdn.jsdelivr.net/npm/rapidoc@9.3.8"
207
+ ></script>
208
+ </head>
209
+ <body>
210
+ <rapi-doc
211
+ spec-url="/openapi.json"
212
+ render-style="read"
213
+ fill-request-fields-with-example="false"
214
+ persist-auth="true"
215
+
216
+ sort-tags="true"
217
+ sort-endpoints-by="method"
218
+
219
+ show-method-in-nav-bar="as-colored-block"
220
+ show-header="false"
221
+ allow-authentication="true"
222
+ allow-server-selection="false"
223
+
224
+ schema-style="table"
225
+ schema-expand-level="1"
226
+ default-schema-tab="schema"
227
+
228
+ primary-color="#664de5"
229
+ bg-color="#151515"
230
+ text-color="#c2c2c2"
231
+ header-color="#353535"
232
+ />
233
+ </body>
234
+ </html>
235
+ `;
236
+ }
237
+ getMetrics() {
238
+ return getMetrics();
239
+ }
240
+ };
241
+ __decorate([
242
+ Get('/healthz'),
243
+ ResponseSchema(HealthOutputDTO),
244
+ __metadata("design:type", Function),
245
+ __metadata("design:paramtypes", []),
246
+ __metadata("design:returntype", Promise)
247
+ ], Meta.prototype, "getHealth", null);
248
+ __decorate([
249
+ Get('/readyz'),
250
+ ResponseSchema(HealthOutputDTO),
251
+ __metadata("design:type", Function),
252
+ __metadata("design:paramtypes", []),
253
+ __metadata("design:returntype", Promise)
254
+ ], Meta.prototype, "getReadiness", null);
255
+ __decorate([
256
+ Get('/openapi.json'),
257
+ __metadata("design:type", Function),
258
+ __metadata("design:paramtypes", []),
259
+ __metadata("design:returntype", Promise)
260
+ ], Meta.prototype, "getOpenApi", null);
261
+ __decorate([
262
+ Get('/'),
263
+ __param(0, Res()),
264
+ __metadata("design:type", Function),
265
+ __metadata("design:paramtypes", [Object]),
266
+ __metadata("design:returntype", void 0)
267
+ ], Meta.prototype, "getRoot", null);
268
+ __decorate([
269
+ Get('/api.html'),
270
+ __metadata("design:type", Function),
271
+ __metadata("design:paramtypes", []),
272
+ __metadata("design:returntype", void 0)
273
+ ], Meta.prototype, "getOpenApiHtml", null);
274
+ __decorate([
275
+ Get('/metrics'),
276
+ __metadata("design:type", Function),
277
+ __metadata("design:paramtypes", []),
278
+ __metadata("design:returntype", void 0)
279
+ ], Meta.prototype, "getMetrics", null);
280
+ Meta = __decorate([
281
+ Controller()
282
+ ], Meta);
283
+ export { Meta };
284
+ //# sourceMappingURL=meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta.js","sourceRoot":"","sources":["../../src/controllers/meta.ts"],"names":[],"mappings":"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"}
package/dist/main.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ export { HTTP } from './app.js';
2
+ export * from './middleware/metrics.js';
3
+ export { createRateLimitMiddleware } from './middleware/rateLimit.js';
4
+ export { getAdminBasicAuth } from './middleware/basicAuth.js';
5
+ export { paginationMiddleware } from './middleware/paginationMiddleware.js';
6
+ export { apiResponse, APIOutput } from './util/apiResponse.js';
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 ADDED
@@ -0,0 +1,8 @@
1
+ export { HTTP } from './app.js';
2
+ export * from './middleware/metrics.js';
3
+ export { createRateLimitMiddleware } from './middleware/rateLimit.js';
4
+ export { getAdminBasicAuth } from './middleware/basicAuth.js';
5
+ export { paginationMiddleware } from './middleware/paginationMiddleware.js';
6
+ export { apiResponse, APIOutput } from './util/apiResponse.js';
7
+ export { ErrorHandler } from './middleware/errorHandler.js';
8
+ //# sourceMappingURL=main.js.map
@@ -0,0 +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,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,3 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ export declare function getAdminBasicAuth(secret: string): (req: Request, res: Response, next: NextFunction) => void;
3
+ //# sourceMappingURL=basicAuth.d.ts.map
@@ -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,3 @@
1
+ import { Request, Response, NextFunction } from 'express';
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,iBAgFpB"}
@@ -0,0 +1,73 @@
1
+ import { HttpError } from 'routing-controllers';
2
+ import { logger, errors } from '@takaro/util';
3
+ import { apiResponse } from '../util/apiResponse.js';
4
+ import { MulterError } from 'multer';
5
+ const log = logger('errorHandler');
6
+ export async function ErrorHandler(originalError, req, res, _next) {
7
+ let status = 500;
8
+ let parsedError = new errors.InternalServerError();
9
+ if (originalError.name === 'BadRequestError') {
10
+ if (Object.prototype.hasOwnProperty.call(originalError, 'errors')) {
11
+ // @ts-expect-error Error typing is weird in ts... but we validate during runtime so should be OK
12
+ const validationErrors = originalError['errors'];
13
+ parsedError = new errors.ValidationError('Validation error', validationErrors);
14
+ log.warn('⚠️ Validation errror', {
15
+ details: validationErrors.map((e) => JSON.stringify(e.constraints, null, 2)),
16
+ });
17
+ }
18
+ }
19
+ if (originalError instanceof MulterError) {
20
+ status = 400;
21
+ parsedError = new errors.BadRequestError('Invalid file');
22
+ if (originalError.code === 'LIMIT_FIELD_VALUE') {
23
+ status = 400;
24
+ parsedError = new errors.BadRequestError('File too large');
25
+ }
26
+ }
27
+ if (originalError instanceof HttpError) {
28
+ status = originalError.httpCode;
29
+ }
30
+ if (originalError.name === 'UniqueViolationError') {
31
+ status = 409;
32
+ parsedError = new errors.ConflictError('Unique constraint violation');
33
+ }
34
+ if (originalError.name === 'NotNullViolationError') {
35
+ status = 400;
36
+ parsedError = new errors.BadRequestError('Missing required field');
37
+ }
38
+ if (originalError.name === 'CheckViolationError') {
39
+ status = 400;
40
+ parsedError = new errors.BadRequestError('Invalid data provided');
41
+ }
42
+ if (originalError.name === 'DataError' && originalError.message.includes('invalid input syntax for type uuid')) {
43
+ status = 400;
44
+ parsedError = new errors.BadRequestError('Invalid UUID. Passed a string instead of a UUID');
45
+ }
46
+ if ('constraint' in originalError && originalError['constraint'] === 'currency_positive') {
47
+ status = 400;
48
+ parsedError = new errors.BadRequestError('Not enough currency');
49
+ }
50
+ if (originalError instanceof errors.TakaroError) {
51
+ status = originalError.http;
52
+ parsedError = originalError;
53
+ }
54
+ // If error is a JSON.parse error
55
+ if (originalError instanceof SyntaxError) {
56
+ if (originalError.message.includes('Unexpected token') ||
57
+ originalError.message.includes('Unexpected end of JSON input') ||
58
+ originalError.message.includes('Expected property name or')) {
59
+ status = 400;
60
+ parsedError = new errors.BadRequestError('Invalid JSON');
61
+ }
62
+ }
63
+ log.error(originalError);
64
+ if (status >= 500) {
65
+ log.error(`🔴 FAIL ${req.method} ${req.originalUrl}`, parsedError);
66
+ }
67
+ else {
68
+ log.warn(`⚠️ FAIL ${req.method} ${req.originalUrl}`, parsedError);
69
+ }
70
+ res.status(status).json(apiResponse({}, { error: parsedError, req, res }));
71
+ res.end();
72
+ }
73
+ //# sourceMappingURL=errorHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../src/middleware/errorHandler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { NextFunction, Request, Response } from 'express';
2
+ /**
3
+ * This middleware is called very early in the request lifecycle, so it's
4
+ * we leverage this fact to inject the context tracking at this stage
5
+ */
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"}