@clairejs/server 3.15.2 → 3.16.1

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 (86) hide show
  1. package/.mocharc.json +3 -0
  2. package/README.md +5 -0
  3. package/dist/common/AbstractController.js +3 -0
  4. package/dist/common/ControllerMetadata.js +1 -0
  5. package/dist/common/FileOperation.js +6 -0
  6. package/dist/common/ServerModelMetadata.js +1 -0
  7. package/dist/common/Transactionable.js +17 -0
  8. package/dist/common/auth/AbstractPrincipalResolver.js +2 -0
  9. package/dist/common/auth/IPrincipal.js +1 -0
  10. package/dist/common/constants.js +7 -0
  11. package/dist/common/decorator.d.ts +2 -2
  12. package/dist/common/decorator.js +6 -0
  13. package/dist/common/request/EndpointMetadata.js +1 -0
  14. package/dist/common/request/HttpData.js +1 -0
  15. package/dist/common/request/HttpEndpoint.js +1 -0
  16. package/dist/common/request/JobData.js +1 -0
  17. package/dist/common/request/MountedEndpointInfo.js +1 -0
  18. package/dist/common/request/RequestOptions.js +1 -0
  19. package/dist/common/request/SocketData.js +1 -0
  20. package/dist/common/request/types.d.ts +1 -1
  21. package/dist/common/request/types.js +1 -0
  22. package/dist/controllers/FileManageController.js +90 -0
  23. package/dist/controllers/FileUploadController.js +64 -0
  24. package/dist/controllers/dto/system.js +14 -0
  25. package/dist/controllers/dto/upload.js +205 -0
  26. package/dist/http/auth/AbstractHttpAuthorizer.js +2 -0
  27. package/dist/http/common/HttpRequest.js +72 -0
  28. package/dist/http/common/HttpResponse.js +62 -0
  29. package/dist/http/controller/AbstractHttpController.js +21 -0
  30. package/dist/http/controller/AbstractHttpMiddleware.js +2 -0
  31. package/dist/http/controller/AbstractHttpRequestHandler.js +69 -0
  32. package/dist/http/controller/CrudHttpController.js +303 -0
  33. package/dist/http/controller/DefaultHttpRequestHandler.js +143 -0
  34. package/dist/http/decorators.d.ts +1 -1
  35. package/dist/http/decorators.js +86 -0
  36. package/dist/http/file-upload/AbstractFileUploadHandler.js +2 -0
  37. package/dist/http/file-upload/FileUploadHandler.js +41 -0
  38. package/dist/http/file-upload/types.d.ts +1 -1
  39. package/dist/http/file-upload/types.js +1 -0
  40. package/dist/http/repository/AbstractRepository.js +26 -0
  41. package/dist/http/repository/DtoRepository.d.ts +3 -3
  42. package/dist/http/repository/DtoRepository.js +204 -0
  43. package/dist/http/repository/ICrudRepository.js +1 -0
  44. package/dist/http/repository/ModelRepository.js +696 -0
  45. package/dist/http/security/AbstractAccessCondition.js +2 -0
  46. package/dist/http/security/access-conditions/FilterModelFieldAccessCondition.js +30 -0
  47. package/dist/http/security/access-conditions/MaximumQueryLimit.js +31 -0
  48. package/dist/http/security/cors.js +1 -0
  49. package/dist/http/utils.js +32 -0
  50. package/dist/index.js +75 -1
  51. package/dist/job/AbstractJobController.js +9 -0
  52. package/dist/job/AbstractJobRepository.js +2 -0
  53. package/dist/job/AbstractJobScheduler.js +48 -0
  54. package/dist/job/AwsJobScheduler.js +405 -0
  55. package/dist/job/LocalJobScheduler.js +273 -0
  56. package/dist/job/decorators.js +57 -0
  57. package/dist/job/interfaces.js +10 -0
  58. package/dist/logging/FileLogMedium.js +44 -0
  59. package/dist/services/AbstractFileService.js +28 -0
  60. package/dist/services/AbstractMailService.js +2 -0
  61. package/dist/services/AbstractService.js +3 -0
  62. package/dist/services/AbstractSmsService.js +2 -0
  63. package/dist/services/implementations/LocalFileService.js +42 -0
  64. package/dist/services/implementations/LocalMailService.js +27 -0
  65. package/dist/services/implementations/LocalSmsService.js +17 -0
  66. package/dist/services/implementations/S3FileService.js +107 -0
  67. package/dist/services/implementations/SesMailService.js +64 -0
  68. package/dist/socket/AbstractServerSocket.js +44 -0
  69. package/dist/socket/AbstractServerSocketManager.d.ts +1 -1
  70. package/dist/socket/AbstractServerSocketManager.js +348 -0
  71. package/dist/socket/AbstractSocketConnectionHandler.js +2 -0
  72. package/dist/socket/AbstractSocketController.d.ts +3 -3
  73. package/dist/socket/AbstractSocketController.js +12 -0
  74. package/dist/socket/AwsSocketManager.d.ts +2 -2
  75. package/dist/socket/AwsSocketManager.js +160 -0
  76. package/dist/socket/IServerSocket.js +1 -0
  77. package/dist/socket/LocalSocketManager.js +292 -0
  78. package/dist/system/ClaireServer.js +78 -0
  79. package/dist/system/ExpressWrapper.js +122 -0
  80. package/dist/system/LambdaWrapper.js +151 -0
  81. package/dist/system/ServerGlobalStore.js +1 -0
  82. package/dist/system/lamba-request-mapper.js +49 -0
  83. package/dist/system/locale/LocaleEntry.js +13 -0
  84. package/dist/system/locale/LocaleTranslation.js +47 -0
  85. package/dist/system/locale/decorators.js +14 -0
  86. package/package.json +13 -20
@@ -0,0 +1,69 @@
1
+ import { getObjectMetadata, getServiceProvider, SocketMethod } from "@clairejs/core";
2
+ import { getEndpointId } from "../utils";
3
+ import { AbstractHttpController } from "./AbstractHttpController";
4
+ export class AbstractHttpRequestHandler {
5
+ mountPoint;
6
+ logger;
7
+ mountedEndpointInfo;
8
+ corsConfig;
9
+ constructor(mountPoint, logger) {
10
+ this.mountPoint = mountPoint;
11
+ this.logger = logger;
12
+ }
13
+ resolverMountPoint(controllerMetadata, mappingUrls) {
14
+ if (!controllerMetadata) {
15
+ return "";
16
+ }
17
+ const urlParts = [];
18
+ urlParts.push(...mappingUrls);
19
+ let finalUrl = urlParts.reduce((collector, part) => `${collector}/${part}`, "");
20
+ //-- replace double slashes
21
+ return finalUrl.replace(/(\/)\/+/g, "$1");
22
+ }
23
+ async getMountedEndpointInfo() {
24
+ if (!this.mountedEndpointInfo) {
25
+ const result = [];
26
+ const injector = getServiceProvider().getInjector();
27
+ //-- get all controllers from registry and resolve endpoints
28
+ const controllers = injector.resolveMultiple(AbstractHttpController);
29
+ await injector.initInstances();
30
+ for (const controller of controllers) {
31
+ const controllerMetadata = getObjectMetadata(controller.constructor);
32
+ const allEndpointMetadata = controller.getEndpointMetadata();
33
+ for (const endpointMetadata of allEndpointMetadata) {
34
+ const endpoint = {
35
+ mount: endpointMetadata.httpMethod === SocketMethod.MESSAGE
36
+ ? endpointMetadata.url
37
+ : this.resolverMountPoint(controllerMetadata, [
38
+ this.mountPoint || "/",
39
+ endpointMetadata.url,
40
+ ]),
41
+ httpMethod: endpointMetadata.httpMethod,
42
+ controller: controller,
43
+ handlerFunctionName: endpointMetadata.name,
44
+ };
45
+ result.push({
46
+ endpointMetadata,
47
+ endpoint,
48
+ });
49
+ }
50
+ }
51
+ //-- add to final to-be-mounted endpoints
52
+ const mountedEndpointInfo = [];
53
+ for (const endpointInfo of result) {
54
+ //-- check overriden endpoints
55
+ const overridingEndpoint = mountedEndpointInfo.find((info) => info.endpoint.mount === endpointInfo.endpoint.mount &&
56
+ info.endpoint.httpMethod === endpointInfo.endpoint.httpMethod);
57
+ if (overridingEndpoint) {
58
+ //-- if this endpoint has an other overriden endpoints then do not mount
59
+ this.logger?.warn(`Implicit overriding endpoint: ${getEndpointId(overridingEndpoint.endpoint)} of ${overridingEndpoint.endpoint.controller?.constructor.name}:${overridingEndpoint.endpoint.handlerFunctionName}`, `Ignore ${getEndpointId(endpointInfo.endpoint)} of ${endpointInfo.endpoint.controller?.constructor.name}:${endpointInfo.endpoint.handlerFunctionName}`);
60
+ }
61
+ else {
62
+ mountedEndpointInfo.push(endpointInfo);
63
+ }
64
+ }
65
+ this.mountedEndpointInfo = mountedEndpointInfo;
66
+ }
67
+ return this.mountedEndpointInfo;
68
+ }
69
+ }
@@ -0,0 +1,303 @@
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 { getObjectMetadata, HttpMethod, getServiceProvider, leanData, getCreateManyBodyValidator, getCreateManyResponseValidator, getGetManyQueryValidator, getGetManyResponseValidator, getUpdateManyQueryValidator, getUpdateManyBodyValidator, getUpdateManyResponseValidator, uniqueReducer, omitData, getUpdateRecordsQueryValidator, getUpdateRecordsBodyValidator, } from "@clairejs/core";
14
+ import { Transactional, PropagationMode } from "@clairejs/orm";
15
+ import { HttpRequest } from "../common/HttpRequest";
16
+ import { ResponseBuilder } from "../common/HttpResponse";
17
+ import { AbstractPrincipalResolver } from "../../common/auth/AbstractPrincipalResolver";
18
+ import { AccessCondition, ApiDescription, Raw } from "../decorators";
19
+ import { AbstractHttpController } from "./AbstractHttpController";
20
+ import { MaximumQueryLimit } from "../security/access-conditions/MaximumQueryLimit";
21
+ import { FilterModelFieldAccessCondition } from "../security/access-conditions/FilterModelFieldAccessCondition";
22
+ export class CrudHttpController extends AbstractHttpController {
23
+ model;
24
+ crudRepository;
25
+ databaseAdapter;
26
+ modelMetadata;
27
+ principalResolver;
28
+ constructor(model, crudRepository, databaseAdapter) {
29
+ super(databaseAdapter);
30
+ this.model = model;
31
+ this.crudRepository = crudRepository;
32
+ this.databaseAdapter = databaseAdapter;
33
+ this.modelMetadata = getObjectMetadata(model);
34
+ }
35
+ getMountedUrl() {
36
+ return `/${this.model.name.toLowerCase()}`;
37
+ }
38
+ async getAuthProvider() {
39
+ if (this.principalResolver === undefined) {
40
+ const injector = getServiceProvider().getInjector();
41
+ this.principalResolver = injector.resolveOptional(AbstractPrincipalResolver) || null;
42
+ await injector.initInstances();
43
+ }
44
+ return this.principalResolver;
45
+ }
46
+ /**
47
+ * This provides endpoint metadata for mounting /POST request.
48
+ */
49
+ createManyEndpoinMetadata() {
50
+ const endpointMetadata = {};
51
+ endpointMetadata.httpMethod = HttpMethod.POST;
52
+ endpointMetadata.url = this.getMountedUrl();
53
+ endpointMetadata.name = CrudHttpController.prototype.createMany.name;
54
+ endpointMetadata.displayName = "createMany" + this.model.name;
55
+ //-- body dto ------------------------------------
56
+ endpointMetadata.bodyValidationDto = getCreateManyBodyValidator(this.modelMetadata);
57
+ //-- response dto ------------------------------------
58
+ endpointMetadata.responseValidationDto = getCreateManyResponseValidator(this.modelMetadata);
59
+ endpointMetadata.accessConditions = [];
60
+ endpointMetadata.params = { 0: { source: "raw" } };
61
+ return endpointMetadata;
62
+ }
63
+ /**
64
+ * This provides endpoint metadata for mounting /GET request.
65
+ */
66
+ getManyEndpointMetadata() {
67
+ //-- get all
68
+ const endpointMetadata = {};
69
+ endpointMetadata.httpMethod = HttpMethod.GET;
70
+ endpointMetadata.url = this.getMountedUrl();
71
+ endpointMetadata.name = CrudHttpController.prototype.getMany.name;
72
+ endpointMetadata.displayName = "getAll" + this.model.name;
73
+ //-- query dto
74
+ endpointMetadata.queriesValidationDto = getGetManyQueryValidator(this.modelMetadata);
75
+ //-- response dto ------------------------------------
76
+ endpointMetadata.responseValidationDto = getGetManyResponseValidator(this.modelMetadata);
77
+ //-- access condition
78
+ endpointMetadata.accessConditions = [
79
+ FilterModelFieldAccessCondition(this.model, "model_projection", (request) => request.getQuery().projection),
80
+ MaximumQueryLimit,
81
+ ];
82
+ endpointMetadata.params = { 0: { source: "raw" } };
83
+ return endpointMetadata;
84
+ }
85
+ /**
86
+ * This provides endpoint metadata for mounting /PUT request.
87
+ */
88
+ updateManyEndpointMetadata() {
89
+ const endpointMetadata = {};
90
+ endpointMetadata.httpMethod = HttpMethod.PUT;
91
+ endpointMetadata.url = this.getMountedUrl();
92
+ endpointMetadata.name = CrudHttpController.prototype.updateMany.name;
93
+ endpointMetadata.displayName = "update" + this.model.name;
94
+ //-- queries dto ---------------------------------
95
+ endpointMetadata.queriesValidationDto = getUpdateManyQueryValidator(this.modelMetadata);
96
+ //-- body dto ------------------------------------
97
+ endpointMetadata.bodyValidationDto = getUpdateManyBodyValidator(this.modelMetadata);
98
+ //-- response dto ------------------------------------
99
+ endpointMetadata.responseValidationDto = getUpdateManyResponseValidator(this.modelMetadata);
100
+ //-- access condition
101
+ endpointMetadata.accessConditions = [
102
+ FilterModelFieldAccessCondition(this.model, "restrict_update_fields", (request) => {
103
+ const updateBody = request.getBody().update;
104
+ if (!updateBody) {
105
+ return [];
106
+ }
107
+ return Object.keys(leanData(updateBody));
108
+ }),
109
+ ];
110
+ endpointMetadata.params = { 0: { source: "raw" } };
111
+ return endpointMetadata;
112
+ }
113
+ updateRecordsEndpointMetadata() {
114
+ const endpointMetadata = {};
115
+ endpointMetadata.httpMethod = HttpMethod.PUT;
116
+ endpointMetadata.url = `${this.getMountedUrl()}/records`;
117
+ endpointMetadata.name = CrudHttpController.prototype.updateRecords.name;
118
+ endpointMetadata.displayName = "updateRecords" + this.model.name;
119
+ //-- queries dto ---------------------------------
120
+ endpointMetadata.queriesValidationDto = getUpdateRecordsQueryValidator();
121
+ //-- body dto ------------------------------------
122
+ endpointMetadata.bodyValidationDto = getUpdateRecordsBodyValidator(this.modelMetadata);
123
+ //-- response dto ------------------------------------
124
+ endpointMetadata.responseValidationDto = getUpdateManyResponseValidator(this.modelMetadata);
125
+ //-- access condition
126
+ endpointMetadata.accessConditions = [
127
+ FilterModelFieldAccessCondition(this.model, "restrict_update_fields", (request) => {
128
+ const updates = request.getBody();
129
+ return updates.records
130
+ .flatMap(({ id, ...update }) => Object.keys(leanData(update) || {}))
131
+ .reduce(uniqueReducer, []);
132
+ }),
133
+ ];
134
+ return endpointMetadata;
135
+ }
136
+ /**
137
+ * This provides endpoint metadata for mounting /DELETE request.
138
+ */
139
+ deleteManyEndpoinMetadata() {
140
+ const endpointMetadata = {};
141
+ endpointMetadata.httpMethod = HttpMethod.DEL;
142
+ endpointMetadata.url = this.getMountedUrl();
143
+ endpointMetadata.name = CrudHttpController.prototype.deleteMany.name;
144
+ endpointMetadata.displayName = "delete" + this.model.name;
145
+ //-- queries ---------------------------------
146
+ endpointMetadata.queriesValidationDto = getUpdateManyQueryValidator(this.modelMetadata);
147
+ endpointMetadata.responseValidationDto = getUpdateManyResponseValidator(this.modelMetadata);
148
+ endpointMetadata.params = { 0: { source: "raw" } };
149
+ //-- access condition
150
+ endpointMetadata.accessConditions = [];
151
+ return endpointMetadata;
152
+ }
153
+ /**
154
+ * Handler functions
155
+ */
156
+ getEndpointMetadata() {
157
+ let allEndpoints = super.getEndpointMetadata();
158
+ const crudEnpointMetadata = [];
159
+ if (!this.modelMetadata.ignoreCrud?.includes(HttpMethod.GET)) {
160
+ crudEnpointMetadata.push(this.getManyEndpointMetadata());
161
+ }
162
+ else {
163
+ allEndpoints = allEndpoints.filter((e) => e.name !== CrudHttpController.prototype.getMany.name);
164
+ }
165
+ if (!this.modelMetadata.ignoreCrud?.includes(HttpMethod.POST)) {
166
+ crudEnpointMetadata.push(this.createManyEndpoinMetadata());
167
+ }
168
+ else {
169
+ allEndpoints = allEndpoints.filter((e) => e.name !== CrudHttpController.prototype.createMany.name);
170
+ }
171
+ if (!this.modelMetadata.ignoreCrud?.includes(HttpMethod.PUT)) {
172
+ crudEnpointMetadata.push(this.updateManyEndpointMetadata());
173
+ crudEnpointMetadata.push(this.updateRecordsEndpointMetadata());
174
+ }
175
+ else {
176
+ allEndpoints = allEndpoints.filter((e) => e.name !== CrudHttpController.prototype.updateMany.name &&
177
+ e.name !== CrudHttpController.prototype.updateRecords.name);
178
+ }
179
+ if (!this.modelMetadata.ignoreCrud?.includes(HttpMethod.DEL)) {
180
+ crudEnpointMetadata.push(this.deleteManyEndpoinMetadata());
181
+ }
182
+ else {
183
+ allEndpoints = allEndpoints.filter((e) => e.name !== CrudHttpController.prototype.deleteMany.name);
184
+ }
185
+ //-- merge with super
186
+ for (const endpoint of crudEnpointMetadata) {
187
+ const index = allEndpoints.findIndex((e) => (e.httpMethod === endpoint.httpMethod && e.url === endpoint.url) ||
188
+ (e.name === endpoint.name && (!e.httpMethod || !e.url)));
189
+ if (index >= 0) {
190
+ allEndpoints[index] = {
191
+ ...endpoint,
192
+ ...allEndpoints[index],
193
+ accessConditions: [
194
+ ...(endpoint.accessConditions || []),
195
+ ...(allEndpoints[index].accessConditions || []),
196
+ ],
197
+ };
198
+ }
199
+ else {
200
+ allEndpoints.push(endpoint);
201
+ }
202
+ }
203
+ return allEndpoints;
204
+ }
205
+ async updateRecords(req) {
206
+ const tx = await this.getCurrentTransaction();
207
+ const authProvider = await this.getAuthProvider();
208
+ const principal = authProvider && (await authProvider.resolvePrincipal(req));
209
+ const body = req.getBody();
210
+ const queries = req.getQuery();
211
+ const updatedRecords = await Promise.all(body.records.map((record) => this.crudRepository.updateMany({
212
+ principal,
213
+ queries: { ...queries, fields: { id: [record.id] } },
214
+ body: { update: omitData(record, ["id"]) },
215
+ tx,
216
+ })));
217
+ const response = {
218
+ modified: updatedRecords.map((re) => re.modified[0]),
219
+ };
220
+ return ResponseBuilder.json(response).get();
221
+ }
222
+ async createMany(req) {
223
+ const tx = await this.getCurrentTransaction();
224
+ const authProvider = await this.getAuthProvider();
225
+ const principal = authProvider && (await authProvider.resolvePrincipal(req));
226
+ const result = await this.crudRepository.createMany({
227
+ principal,
228
+ body: req.getBody(),
229
+ tx,
230
+ });
231
+ return ResponseBuilder.json(result).get();
232
+ }
233
+ async getMany(req) {
234
+ const result = await this.crudRepository.getMany({
235
+ queries: req.getQuery(),
236
+ queryProvider: this.databaseAdapter,
237
+ });
238
+ return ResponseBuilder.json(result).get();
239
+ }
240
+ async updateMany(req) {
241
+ const tx = await this.getCurrentTransaction();
242
+ const authProvider = await this.getAuthProvider();
243
+ const principal = authProvider && (await authProvider.resolvePrincipal(req));
244
+ const result = await this.crudRepository.updateMany({
245
+ principal,
246
+ queries: req.getQuery(),
247
+ body: req.getBody(),
248
+ tx,
249
+ });
250
+ return ResponseBuilder.json(result).get();
251
+ }
252
+ async deleteMany(req) {
253
+ const tx = await this.getCurrentTransaction();
254
+ const result = await this.crudRepository.deleteMany({ queries: req.getQuery(), tx });
255
+ return ResponseBuilder.json({
256
+ modified: result.modified.map((instance) => instance.id).map((id) => ({ id })),
257
+ }).get();
258
+ }
259
+ }
260
+ __decorate([
261
+ Transactional(PropagationMode.INHERIT_OR_CREATE),
262
+ ApiDescription("Update records of this table."),
263
+ AccessCondition([]),
264
+ __param(0, Raw()),
265
+ __metadata("design:type", Function),
266
+ __metadata("design:paramtypes", [HttpRequest]),
267
+ __metadata("design:returntype", Promise)
268
+ ], CrudHttpController.prototype, "updateRecords", null);
269
+ __decorate([
270
+ Transactional(PropagationMode.INHERIT_OR_CREATE),
271
+ ApiDescription("Create records of this table."),
272
+ AccessCondition([]),
273
+ __param(0, Raw()),
274
+ __metadata("design:type", Function),
275
+ __metadata("design:paramtypes", [HttpRequest]),
276
+ __metadata("design:returntype", Promise)
277
+ ], CrudHttpController.prototype, "createMany", null);
278
+ __decorate([
279
+ AccessCondition([]),
280
+ ApiDescription("Get records of this table."),
281
+ __param(0, Raw()),
282
+ __metadata("design:type", Function),
283
+ __metadata("design:paramtypes", [HttpRequest]),
284
+ __metadata("design:returntype", Promise)
285
+ ], CrudHttpController.prototype, "getMany", null);
286
+ __decorate([
287
+ Transactional(PropagationMode.INHERIT_OR_CREATE),
288
+ ApiDescription("Find and update records which match search condition."),
289
+ AccessCondition([]),
290
+ __param(0, Raw()),
291
+ __metadata("design:type", Function),
292
+ __metadata("design:paramtypes", [HttpRequest]),
293
+ __metadata("design:returntype", Promise)
294
+ ], CrudHttpController.prototype, "updateMany", null);
295
+ __decorate([
296
+ Transactional(PropagationMode.INHERIT_OR_CREATE),
297
+ ApiDescription("Find and remove records which match search condition."),
298
+ AccessCondition([]),
299
+ __param(0, Raw()),
300
+ __metadata("design:type", Function),
301
+ __metadata("design:paramtypes", [HttpRequest]),
302
+ __metadata("design:returntype", Promise)
303
+ ], CrudHttpController.prototype, "deleteMany", null);
@@ -0,0 +1,143 @@
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
+ import { AbstractLogger, stripData, Injectable, getServiceProvider, Errors, LogContext } from "@clairejs/core";
11
+ import randomstring from "randomstring";
12
+ import parseurl from "parseurl";
13
+ import { match } from "path-to-regexp";
14
+ import queryString from "query-string";
15
+ import { HttpResponse, ResponseBuilder } from "../common/HttpResponse";
16
+ import { AbstractPrincipalResolver } from "../../common/auth/AbstractPrincipalResolver";
17
+ import { HttpRequest } from "../common/HttpRequest";
18
+ import { AbstractRequestAuthorizer } from "../auth/AbstractHttpAuthorizer";
19
+ import { AbstractHttpMiddleware } from "./AbstractHttpMiddleware";
20
+ import { AbstractHttpRequestHandler } from "./AbstractHttpRequestHandler";
21
+ let DefaultHttpRequestHandler = class DefaultHttpRequestHandler extends AbstractHttpRequestHandler {
22
+ logger;
23
+ authorizationProvider;
24
+ principalResolver;
25
+ _middleware = null;
26
+ constructor(logger, authorizationProvider, principalResolver) {
27
+ super("", logger);
28
+ this.logger = logger;
29
+ this.authorizationProvider = authorizationProvider;
30
+ this.principalResolver = principalResolver;
31
+ }
32
+ async handleRequest(endpoint, req) {
33
+ //-- supply correct order of params into handler
34
+ const params = Object.values(endpoint.endpointMetadata.params || {}).map((p) => {
35
+ switch (p.source) {
36
+ case "body":
37
+ return req.getBody();
38
+ case "params":
39
+ return req.getParams();
40
+ case "queries":
41
+ return req.getQuery();
42
+ case "headers":
43
+ return req.headers;
44
+ case "raw":
45
+ return req;
46
+ }
47
+ });
48
+ const response = await endpoint.endpoint.controller[endpoint.endpointMetadata.name](...params);
49
+ //-- validate response value against response dto
50
+ if (endpoint.endpointMetadata.responseValidationDto) {
51
+ response.value = stripData(response.value, endpoint.endpointMetadata.responseValidationDto);
52
+ }
53
+ return response;
54
+ }
55
+ exit() { }
56
+ async getResponse(httpData) {
57
+ let response = new HttpResponse();
58
+ //-- match options with handler
59
+ const method = httpData.method;
60
+ const result = parseurl({ url: httpData.fullPath });
61
+ let params = {};
62
+ const mountedEndpointInfo = await this.getMountedEndpointInfo();
63
+ const info = mountedEndpointInfo.find((info) => {
64
+ const parsedParams = info.endpointMetadata.httpMethod === method &&
65
+ match(info.endpointMetadata.url, { decode: decodeURIComponent })(result.pathname || "/");
66
+ if (!parsedParams) {
67
+ return false;
68
+ }
69
+ params = parsedParams;
70
+ return true;
71
+ });
72
+ if (!info) {
73
+ this.logger.debug(httpData);
74
+ throw Errors.NOT_FOUND(`Handler not found`);
75
+ }
76
+ const request = new HttpRequest({
77
+ clientIP: httpData.clientIP,
78
+ method,
79
+ headers: httpData.headers,
80
+ pathName: result.pathname || "",
81
+ hash: result.hash || "",
82
+ query: queryString.parse(decodeURIComponent(result.search || "")),
83
+ params: params.params,
84
+ body: httpData.body,
85
+ cookies: httpData.cookies,
86
+ }, info.endpointMetadata);
87
+ const authInfo = await this.principalResolver.resolvePrincipal(request);
88
+ request.setAuthInfo(authInfo);
89
+ if (!info.endpointMetadata.openAccess) {
90
+ await this.authorizationProvider.authorize(request, info);
91
+ }
92
+ const mws = await this.getMiddleware();
93
+ for (const mw of mws) {
94
+ const next = await mw.intercept(request, response, info.endpointMetadata);
95
+ if (!next) {
96
+ return response;
97
+ }
98
+ }
99
+ const finalResponse = await this.handleRequest(info, request);
100
+ //-- merge response data
101
+ finalResponse.code = finalResponse.code || response.code || 200;
102
+ finalResponse.value = finalResponse.value || response.value;
103
+ finalResponse.headers = { ...response.headers, ...finalResponse.headers };
104
+ finalResponse.cookies = { ...response.cookies, ...finalResponse.cookies };
105
+ return finalResponse;
106
+ }
107
+ async handle(httpData) {
108
+ const requestId = randomstring.generate();
109
+ this.logger.info(`===== HANDLING REQUEST: ${requestId} - ${httpData.method}:${httpData.fullPath}`);
110
+ try {
111
+ const response = await this.getResponse(httpData);
112
+ if (!response || !(response instanceof HttpResponse)) {
113
+ throw Errors.SYSTEM_ERROR(`Invalid response value returned, required instance of ${HttpResponse.name}, found ${response ? response?.constructor.name : response}`);
114
+ }
115
+ this.logger.info(`===== REQUEST SUCCESS: ${requestId} - ${httpData.method}:${httpData.fullPath}`);
116
+ return response;
117
+ }
118
+ catch (err) {
119
+ this.logger.error(err);
120
+ const response = ResponseBuilder.json({ name: err.name, message: err.message })
121
+ .status(err.code || 500)
122
+ .get();
123
+ this.logger.info(`===== REQUEST FAILED: ${requestId} - ${httpData.method}:${httpData.fullPath}, code: ${response.code}`, response.value);
124
+ return response;
125
+ }
126
+ }
127
+ async getMiddleware() {
128
+ if (this._middleware === null) {
129
+ const injector = getServiceProvider().getInjector();
130
+ this._middleware = injector.resolveMultiple(AbstractHttpMiddleware);
131
+ await injector.initInstances();
132
+ }
133
+ return this._middleware;
134
+ }
135
+ };
136
+ DefaultHttpRequestHandler = __decorate([
137
+ Injectable(),
138
+ LogContext(),
139
+ __metadata("design:paramtypes", [AbstractLogger,
140
+ AbstractRequestAuthorizer,
141
+ AbstractPrincipalResolver])
142
+ ], DefaultHttpRequestHandler);
143
+ export { DefaultHttpRequestHandler };
@@ -4,7 +4,7 @@ import { AbstractAccessCondition } from "./security/AbstractAccessCondition";
4
4
  import { HttpResponse } from "./common/HttpResponse";
5
5
  import { UriMapperHandler } from "./file-upload/types";
6
6
  import { IPrincipal } from "../common/auth/IPrincipal";
7
- declare type HttpFunctionMethod<T> = (...args: any[]) => Promise<HttpResponse<T>>;
7
+ type HttpFunctionMethod<T> = (...args: any[]) => Promise<HttpResponse<T>>;
8
8
  export declare const ApiDescription: (description: string) => (prototype: AbstractHttpController, propertyKey: string) => void;
9
9
  export declare const Endpoint: (config: {
10
10
  method: HttpMethod;
@@ -0,0 +1,86 @@
1
+ import { initFieldMetadata, getObjectMetadata, HttpMethod, DataType, } from "@clairejs/core";
2
+ export const ApiDescription = (description) => (prototype, propertyKey) => {
3
+ const endpointMetadata = initFieldMetadata(prototype, propertyKey);
4
+ endpointMetadata.description = description;
5
+ };
6
+ export const Endpoint = (config) => (prototype, propertyKey) => {
7
+ const endpointMetadata = initFieldMetadata(prototype, propertyKey);
8
+ endpointMetadata.httpMethod = config.method;
9
+ //-- default to "/" if empty string is provided
10
+ endpointMetadata.url = config.url || "/";
11
+ };
12
+ const HttpMethodDecoratorFactory = (method) => (url) => (prototype, propertyKey, _descriptor) => Endpoint({ method, url })(prototype, propertyKey);
13
+ export const Post = HttpMethodDecoratorFactory(HttpMethod.POST);
14
+ export const Put = HttpMethodDecoratorFactory(HttpMethod.PUT);
15
+ export const Del = HttpMethodDecoratorFactory(HttpMethod.DEL);
16
+ export const Get = HttpMethodDecoratorFactory(HttpMethod.GET);
17
+ export const Head = HttpMethodDecoratorFactory(HttpMethod.HEAD);
18
+ export const ApiResponse = (responseClass) => (prototype, propertyKey, _descriptor) => {
19
+ const endpointMetadata = initFieldMetadata(prototype, propertyKey);
20
+ if (responseClass === String) {
21
+ endpointMetadata.responseValidationDto = { id: "", primitiveType: DataType.STRING, fields: [] };
22
+ }
23
+ else if (responseClass === Number) {
24
+ endpointMetadata.responseValidationDto = { id: "", primitiveType: DataType.NUMBER, fields: [] };
25
+ }
26
+ else if (responseClass === Boolean) {
27
+ endpointMetadata.responseValidationDto = { id: "", primitiveType: DataType.BOOLEAN, fields: [] };
28
+ }
29
+ else {
30
+ endpointMetadata.responseValidationDto = getObjectMetadata(responseClass);
31
+ }
32
+ };
33
+ export const OpenAccess = () => (prototype, propertyKey) => {
34
+ const endpointMetadata = initFieldMetadata(prototype, propertyKey);
35
+ endpointMetadata.openAccess = true;
36
+ };
37
+ export const TfaRequired = () => (prototype, propertyKey) => {
38
+ const endpointMetadata = initFieldMetadata(prototype, propertyKey);
39
+ endpointMetadata.tfaRequired = true;
40
+ };
41
+ export const AccessCondition = (conditions) => (prototype, propertyKey) => {
42
+ const endpointMetadata = initFieldMetadata(prototype, propertyKey);
43
+ endpointMetadata.accessConditions = conditions;
44
+ };
45
+ const RequestDeco = (source) => (prototype, propertyKey, paramIndex) => {
46
+ const endpointMetadata = initFieldMetadata(prototype, propertyKey);
47
+ const paramClasses = Reflect.getMetadata("design:paramtypes", prototype, propertyKey) || [];
48
+ switch (source) {
49
+ case "body":
50
+ endpointMetadata.bodyValidationDto = getObjectMetadata(paramClasses[paramIndex]);
51
+ break;
52
+ case "params":
53
+ endpointMetadata.paramsValidationDto = getObjectMetadata(paramClasses[paramIndex]);
54
+ break;
55
+ case "queries":
56
+ endpointMetadata.queriesValidationDto = getObjectMetadata(paramClasses[paramIndex]);
57
+ break;
58
+ default:
59
+ break;
60
+ }
61
+ if (!endpointMetadata.params) {
62
+ endpointMetadata.params = {};
63
+ }
64
+ endpointMetadata.params[paramIndex] = {
65
+ source,
66
+ diClass: paramClasses[paramIndex],
67
+ };
68
+ };
69
+ export const Body = () => RequestDeco("body");
70
+ export const Params = () => RequestDeco("params");
71
+ export const Queries = () => RequestDeco("queries");
72
+ export const Headers = () => RequestDeco("headers");
73
+ export const Socket = () => RequestDeco("socket");
74
+ export const Raw = () => RequestDeco("raw");
75
+ export const UriMapper = (mapper) => (prototype, propertyKey) => {
76
+ const field = initFieldMetadata(prototype, propertyKey);
77
+ field.uriMapper = mapper;
78
+ };
79
+ /**
80
+ * Current User decorator only has effect when being used with ICrudRepository.
81
+ * */
82
+ export const CurrentUser = (resolver) => (prototype, propertyKey) => {
83
+ const field = initFieldMetadata(prototype, propertyKey);
84
+ field.userResolver = resolver;
85
+ field.serverValue = true;
86
+ };
@@ -0,0 +1,2 @@
1
+ export class AbstractFileUploadHandler {
2
+ }
@@ -0,0 +1,41 @@
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
+ import { Injectable } from "@clairejs/core";
11
+ import { AbstractFileService } from "../../services/AbstractFileService";
12
+ import { AbstractFileUploadHandler } from "./AbstractFileUploadHandler";
13
+ let FileUploadHandler = class FileUploadHandler extends AbstractFileUploadHandler {
14
+ fileService;
15
+ constructor(fileService) {
16
+ super();
17
+ this.fileService = fileService;
18
+ }
19
+ async moveFile(fromURI, toURI) {
20
+ await this.fileService.moveObject([{ fromURI, toURI }]);
21
+ }
22
+ async copyFile(fromURI, toURI) {
23
+ await this.fileService.copyObject([{ fromURI, toURI }]);
24
+ }
25
+ async removeFile(uri) {
26
+ await this.fileService.removeObject([uri]);
27
+ }
28
+ async resolvePublicUrl(uri) {
29
+ const result = await this.fileService.getAccessUrls([uri], true);
30
+ return result[0];
31
+ }
32
+ async resolvePrivateUrl(uri) {
33
+ const result = await this.fileService.getAccessUrls([uri], false);
34
+ return result[0];
35
+ }
36
+ };
37
+ FileUploadHandler = __decorate([
38
+ Injectable(),
39
+ __metadata("design:paramtypes", [AbstractFileService])
40
+ ], FileUploadHandler);
41
+ export { FileUploadHandler };
@@ -1 +1 @@
1
- export declare type UriMapperHandler = (tmpUri: string, locale?: string) => string;
1
+ export type UriMapperHandler = (tmpUri: string, locale?: string) => string;
@@ -0,0 +1 @@
1
+ export {};