codeweaver 3.1.3 → 4.0.0
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/README.md +56 -73
- package/package.json +23 -1
- package/src/config.ts +17 -15
- package/src/constants.ts +1 -0
- package/src/core/aws/api-gateway.ts +187 -0
- package/src/core/aws/basic-types.ts +147 -0
- package/src/core/aws/dynamodb.ts +187 -0
- package/src/core/aws/index.ts +9 -0
- package/src/core/aws/lambda.ts +199 -0
- package/src/core/aws/message-broker.ts +167 -0
- package/src/core/aws/message.ts +259 -0
- package/src/core/aws/s3.ts +136 -0
- package/src/core/aws/utilities.ts +44 -0
- package/src/core/cache/basic-types.ts +17 -0
- package/src/core/cache/decorator.ts +72 -0
- package/src/core/cache/index.ts +4 -0
- package/src/core/cache/memory-cache.class.ts +119 -0
- package/src/{utilities/cache/redis-cache.ts → core/cache/redis-cache.class.ts} +58 -10
- package/src/core/container/basic-types.ts +10 -0
- package/src/{utilities → core/container}/container.ts +7 -17
- package/src/core/container/index.ts +2 -0
- package/src/{utilities → core/error}/error-handling.ts +1 -65
- package/src/core/error/index.ts +3 -0
- package/src/core/error/response-error.ts +45 -0
- package/src/core/error/send-http-error.ts +15 -0
- package/src/core/file/file-helpers.ts +166 -0
- package/src/core/file/index.ts +1 -0
- package/src/{utilities → core/helpers}/assignment.ts +2 -2
- package/src/core/helpers/comparison.ts +86 -0
- package/src/{utilities → core/helpers}/conversion.ts +2 -2
- package/src/core/helpers/decorators.ts +316 -0
- package/src/core/helpers/format.ts +9 -0
- package/src/core/helpers/index.ts +7 -0
- package/src/core/helpers/range.ts +67 -0
- package/src/core/helpers/types.ts +3 -0
- package/src/core/logger/index.ts +4 -0
- package/src/{utilities/logger/logger.config.ts → core/logger/winston-logger.config.ts} +1 -1
- package/src/{utilities → core}/logger/winston-logger.service.ts +3 -3
- package/src/core/message-broker/bullmq/basic-types.ts +67 -0
- package/src/core/message-broker/bullmq/broker.ts +141 -0
- package/src/core/message-broker/bullmq/index.ts +3 -0
- package/src/core/message-broker/bullmq/queue.ts +58 -0
- package/src/core/message-broker/bullmq/worker.ts +68 -0
- package/src/core/message-broker/kafka/basic-types.ts +45 -0
- package/src/core/message-broker/kafka/consumer.ts +95 -0
- package/src/core/message-broker/kafka/index.ts +3 -0
- package/src/core/message-broker/kafka/producer.ts +113 -0
- package/src/core/message-broker/rabitmq/basic-types.ts +44 -0
- package/src/core/message-broker/rabitmq/channel.ts +95 -0
- package/src/core/message-broker/rabitmq/consumer.ts +94 -0
- package/src/core/message-broker/rabitmq/index.ts +4 -0
- package/src/core/message-broker/rabitmq/producer.ts +100 -0
- package/src/core/message-broker/utilities.ts +50 -0
- package/src/core/middlewares/basic-types.ts +39 -0
- package/src/core/middlewares/decorators.ts +244 -0
- package/src/core/middlewares/index.ts +3 -0
- package/src/core/middlewares/middlewares.ts +246 -0
- package/src/core/parallel/index.ts +3 -0
- package/src/{utilities → core}/parallel/parallel.ts +11 -1
- package/src/core/rate-limit/basic-types.ts +43 -0
- package/src/core/rate-limit/index.ts +4 -0
- package/src/core/rate-limit/memory-store.ts +65 -0
- package/src/core/rate-limit/rate-limit.ts +134 -0
- package/src/core/rate-limit/redis-store.ts +141 -0
- package/src/core/retry/basic-types.ts +21 -0
- package/src/core/retry/decorator.ts +139 -0
- package/src/core/retry/index.ts +2 -0
- package/src/main.ts +6 -8
- package/src/routers/orders/index.router.ts +5 -1
- package/src/routers/orders/order.controller.ts +46 -59
- package/src/routers/products/index.router.ts +2 -1
- package/src/routers/products/product.controller.ts +25 -63
- package/src/routers/users/index.router.ts +1 -1
- package/src/routers/users/user.controller.ts +22 -50
- package/src/utilities/cache/memory-cache.ts +0 -74
- /package/src/{utilities → core}/logger/base-logger.interface.ts +0 -0
- /package/src/{utilities → core}/logger/logger.service.ts +0 -0
- /package/src/{utilities → core}/parallel/chanel.ts +0 -0
- /package/src/{utilities → core}/parallel/worker-pool.ts +0 -0
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import { memoizeAsync, onError, rateLimit, timeout } from "utils-decorators";
|
|
2
1
|
import { OrderDto, OrderCreationDto, ZodOrderDto } from "./dto/order.dto";
|
|
3
|
-
import { ResponseError } from "@/
|
|
4
|
-
import { convert, stringToInteger } from "@/
|
|
2
|
+
import { ResponseError } from "@/core/error";
|
|
3
|
+
import { convert, stringToInteger } from "@/core/helpers";
|
|
5
4
|
import { config } from "@/config";
|
|
6
5
|
import { orders } from "@/db";
|
|
7
6
|
import { Order, ZodOrder } from "@/entities/order.entity";
|
|
8
|
-
import { MapAsyncCache } from "@/
|
|
9
|
-
import { Injectable } from "@/
|
|
10
|
-
import { parallelMap } from "@/
|
|
7
|
+
import { Invalidate, MapAsyncCache, Memoize } from "@/core/cache";
|
|
8
|
+
import { Injectable } from "@/core/container";
|
|
9
|
+
import { parallelMap } from "@/core/parallel";
|
|
10
|
+
import { RateLimit } from "@/core/rate-limit";
|
|
11
|
+
import { ErrorHandler, Timeout } from "@/core/middlewares";
|
|
11
12
|
|
|
12
|
-
function exceedHandler() {
|
|
13
|
+
async function exceedHandler() {
|
|
13
14
|
const message = "Too much call in allowed window";
|
|
14
15
|
throw new ResponseError(message, 429);
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
function invalidInputHandler(e: ResponseError) {
|
|
18
|
+
async function invalidInputHandler(e: ResponseError) {
|
|
18
19
|
const message = "Invalid input";
|
|
19
20
|
throw new ResponseError(message, 400, e.message);
|
|
20
21
|
}
|
|
@@ -31,9 +32,7 @@ const orderCache = new MapAsyncCache<OrderDto>(config.cacheSize);
|
|
|
31
32
|
export default class OrderController {
|
|
32
33
|
// constructor(private readonly orderService: OrderService) { }
|
|
33
34
|
|
|
34
|
-
@
|
|
35
|
-
func: invalidInputHandler,
|
|
36
|
-
})
|
|
35
|
+
@ErrorHandler(invalidInputHandler)
|
|
37
36
|
/**
|
|
38
37
|
* Validates a string ID and converts it to a number.
|
|
39
38
|
*
|
|
@@ -44,9 +43,7 @@ export default class OrderController {
|
|
|
44
43
|
return stringToInteger(id);
|
|
45
44
|
}
|
|
46
45
|
|
|
47
|
-
@
|
|
48
|
-
func: invalidInputHandler,
|
|
49
|
-
})
|
|
46
|
+
@ErrorHandler(invalidInputHandler)
|
|
50
47
|
/**
|
|
51
48
|
* Validates and creates a new Order from the given DTO.
|
|
52
49
|
*
|
|
@@ -66,11 +63,12 @@ export default class OrderController {
|
|
|
66
63
|
return convert(newOrder, ZodOrder);
|
|
67
64
|
}
|
|
68
65
|
|
|
69
|
-
@
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
66
|
+
@Invalidate(ordersCache)
|
|
67
|
+
@RateLimit(
|
|
68
|
+
config.rateLimitTimeSpan,
|
|
69
|
+
config.rateLimitAllowedCalls,
|
|
70
|
+
exceedHandler
|
|
71
|
+
)
|
|
74
72
|
/**
|
|
75
73
|
* Create a new order
|
|
76
74
|
* @param {Order} order - Order creation data
|
|
@@ -80,20 +78,15 @@ export default class OrderController {
|
|
|
80
78
|
*/
|
|
81
79
|
public async create(order: Order): Promise<void> {
|
|
82
80
|
orders.push(order);
|
|
83
|
-
await ordersCache.delete("key");
|
|
84
81
|
}
|
|
85
82
|
|
|
86
|
-
@
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
timeSpanMs: config.rateLimitTimeSpan,
|
|
94
|
-
allowedCalls: config.rateLimitAllowedCalls,
|
|
95
|
-
exceedHandler,
|
|
96
|
-
})
|
|
83
|
+
@Memoize(orderCache)
|
|
84
|
+
@Timeout(config.timeout)
|
|
85
|
+
@RateLimit(
|
|
86
|
+
config.rateLimitTimeSpan,
|
|
87
|
+
config.rateLimitAllowedCalls,
|
|
88
|
+
exceedHandler
|
|
89
|
+
)
|
|
97
90
|
/**
|
|
98
91
|
* Retrieves all orders
|
|
99
92
|
* @returns List of orders
|
|
@@ -105,16 +98,12 @@ export default class OrderController {
|
|
|
105
98
|
);
|
|
106
99
|
}
|
|
107
100
|
|
|
108
|
-
@
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
timeSpanMs: config.rateLimitTimeSpan,
|
|
115
|
-
allowedCalls: config.rateLimitAllowedCalls,
|
|
116
|
-
exceedHandler,
|
|
117
|
-
})
|
|
101
|
+
@Memoize(orderCache)
|
|
102
|
+
@RateLimit(
|
|
103
|
+
config.rateLimitTimeSpan,
|
|
104
|
+
config.rateLimitAllowedCalls,
|
|
105
|
+
exceedHandler
|
|
106
|
+
)
|
|
118
107
|
/**
|
|
119
108
|
* Finds an order by its ID
|
|
120
109
|
* @param {number} id - Order ID as string
|
|
@@ -128,11 +117,13 @@ export default class OrderController {
|
|
|
128
117
|
return await convert(order, ZodOrder);
|
|
129
118
|
}
|
|
130
119
|
|
|
131
|
-
@
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
120
|
+
@Invalidate(orderCache, false)
|
|
121
|
+
@Invalidate(ordersCache)
|
|
122
|
+
@RateLimit(
|
|
123
|
+
config.rateLimitTimeSpan,
|
|
124
|
+
config.rateLimitAllowedCalls,
|
|
125
|
+
exceedHandler
|
|
126
|
+
)
|
|
136
127
|
/**
|
|
137
128
|
* Cancel an existing order
|
|
138
129
|
* @param {number} id - Order ID to cancel
|
|
@@ -145,21 +136,20 @@ export default class OrderController {
|
|
|
145
136
|
if (order.status != "Processing") {
|
|
146
137
|
throw new ResponseError(
|
|
147
138
|
"Cancellation is not available unless the order is in processing status.",
|
|
148
|
-
|
|
139
|
+
409
|
|
149
140
|
);
|
|
150
141
|
}
|
|
151
142
|
order.status = "Canceled";
|
|
152
143
|
order.deliveredAt = new Date();
|
|
153
|
-
|
|
154
|
-
await orderCache.delete(id.toString());
|
|
155
|
-
await ordersCache.delete("key");
|
|
156
144
|
}
|
|
157
145
|
|
|
158
|
-
@
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
146
|
+
@Invalidate(orderCache, false)
|
|
147
|
+
@Invalidate(ordersCache)
|
|
148
|
+
@RateLimit(
|
|
149
|
+
config.rateLimitTimeSpan,
|
|
150
|
+
config.rateLimitAllowedCalls,
|
|
151
|
+
exceedHandler
|
|
152
|
+
)
|
|
163
153
|
/**
|
|
164
154
|
* Mark an order as delivered
|
|
165
155
|
* @param {number} id - Order ID to mark as delivered
|
|
@@ -172,13 +162,10 @@ export default class OrderController {
|
|
|
172
162
|
if (order.status != "Processing") {
|
|
173
163
|
throw new ResponseError(
|
|
174
164
|
"Delivery is only available when the order is in processing status.",
|
|
175
|
-
|
|
165
|
+
409
|
|
176
166
|
);
|
|
177
167
|
}
|
|
178
168
|
order.status = "Delivered";
|
|
179
169
|
order.deliveredAt = new Date();
|
|
180
|
-
|
|
181
|
-
await orderCache.delete(id.toString());
|
|
182
|
-
await ordersCache.delete("key");
|
|
183
170
|
}
|
|
184
171
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Router, Request, Response } from "express";
|
|
2
2
|
import asyncHandler from "express-async-handler";
|
|
3
3
|
import ProductController from "./product.controller";
|
|
4
|
-
import { resolve } from "@/
|
|
4
|
+
import { resolve } from "@/core/container";
|
|
5
5
|
|
|
6
6
|
const router = Router();
|
|
7
7
|
const productController = resolve(ProductController);
|
|
@@ -201,6 +201,7 @@ router.delete(
|
|
|
201
201
|
asyncHandler(async (req: Request, res: Response) => {
|
|
202
202
|
const id = await productController.validateId(req.params.id);
|
|
203
203
|
await productController.delete(id);
|
|
204
|
+
res.status(204).send();
|
|
204
205
|
})
|
|
205
206
|
);
|
|
206
207
|
|
|
@@ -1,26 +1,22 @@
|
|
|
1
|
-
import { memoizeAsync, onError, rateLimit, timeout } from "utils-decorators";
|
|
2
1
|
import {
|
|
3
2
|
ProductCreationDto,
|
|
4
3
|
ProductDto,
|
|
5
4
|
ProductUpdateDto,
|
|
6
5
|
ZodProductDto,
|
|
7
6
|
} from "./dto/product.dto";
|
|
8
|
-
import { MapAsyncCache } from "@/
|
|
9
|
-
import { convert, stringToInteger } from "@/
|
|
7
|
+
import { Invalidate, MapAsyncCache, Memoize } from "@/core/cache";
|
|
8
|
+
import { convert, stringToInteger } from "@/core/helpers";
|
|
10
9
|
import { config } from "@/config";
|
|
11
|
-
import { ResponseError } from "@/
|
|
10
|
+
import { ResponseError } from "@/core/error";
|
|
12
11
|
import { products } from "@/db";
|
|
13
12
|
import { Product, ZodProduct } from "@/entities/product.entity";
|
|
14
|
-
import { Injectable } from "@/
|
|
15
|
-
import assign from "@/
|
|
16
|
-
import { parallelMap } from "@/
|
|
13
|
+
import { Injectable } from "@/core/container";
|
|
14
|
+
import { assign } from "@/core/helpers";
|
|
15
|
+
import { parallelMap } from "@/core/parallel";
|
|
16
|
+
import { ErrorHandler, Timeout } from "@/core/middlewares";
|
|
17
|
+
import { RateLimit } from "@/core/rate-limit";
|
|
17
18
|
|
|
18
|
-
function
|
|
19
|
-
const message = "Too much call in allowed window";
|
|
20
|
-
throw new ResponseError(message, 429);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function invalidInputHandler(e: ResponseError) {
|
|
19
|
+
async function invalidInputHandler(e: ResponseError) {
|
|
24
20
|
const message = "Invalid input";
|
|
25
21
|
throw new ResponseError(message, 400, e.message);
|
|
26
22
|
}
|
|
@@ -37,9 +33,7 @@ const productCache = new MapAsyncCache<ProductDto>(config.cacheSize);
|
|
|
37
33
|
export default class ProductController {
|
|
38
34
|
// constructor(private readonly productService: ProductService) { }
|
|
39
35
|
|
|
40
|
-
@
|
|
41
|
-
func: invalidInputHandler,
|
|
42
|
-
})
|
|
36
|
+
@ErrorHandler(invalidInputHandler)
|
|
43
37
|
/**
|
|
44
38
|
* Validates a string ID and converts it to a number.
|
|
45
39
|
*
|
|
@@ -50,9 +44,7 @@ export default class ProductController {
|
|
|
50
44
|
return stringToInteger(id);
|
|
51
45
|
}
|
|
52
46
|
|
|
53
|
-
@
|
|
54
|
-
func: invalidInputHandler,
|
|
55
|
-
})
|
|
47
|
+
@ErrorHandler(invalidInputHandler)
|
|
56
48
|
/**
|
|
57
49
|
* Validates and creates a new Product from the given DTO.
|
|
58
50
|
*
|
|
@@ -65,9 +57,7 @@ export default class ProductController {
|
|
|
65
57
|
return await convert({ ...product, id: products.length + 1 }, ZodProduct);
|
|
66
58
|
}
|
|
67
59
|
|
|
68
|
-
@
|
|
69
|
-
func: invalidInputHandler,
|
|
70
|
-
})
|
|
60
|
+
@ErrorHandler(invalidInputHandler)
|
|
71
61
|
/**
|
|
72
62
|
* Validates and creates a new Product from the given DTO.
|
|
73
63
|
*
|
|
@@ -80,11 +70,8 @@ export default class ProductController {
|
|
|
80
70
|
return await convert(product, ZodProduct);
|
|
81
71
|
}
|
|
82
72
|
|
|
83
|
-
@
|
|
84
|
-
|
|
85
|
-
allowedCalls: config.rateLimitAllowedCalls,
|
|
86
|
-
exceedHandler,
|
|
87
|
-
})
|
|
73
|
+
@Invalidate(productsCache)
|
|
74
|
+
@RateLimit(config.rateLimitTimeSpan, config.rateLimitAllowedCalls)
|
|
88
75
|
/**
|
|
89
76
|
* Creates a new product with validated data
|
|
90
77
|
* @param {Product} product - Product creation data validated by Zod schema
|
|
@@ -94,20 +81,11 @@ export default class ProductController {
|
|
|
94
81
|
*/
|
|
95
82
|
public async create(product: Product): Promise<void> {
|
|
96
83
|
products.push(product);
|
|
97
|
-
await productsCache.delete("key");
|
|
98
84
|
}
|
|
99
85
|
|
|
100
|
-
@
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
expirationTimeMs: config.memoizeTime,
|
|
104
|
-
})
|
|
105
|
-
@timeout(config.timeout)
|
|
106
|
-
@rateLimit({
|
|
107
|
-
timeSpanMs: config.rateLimitTimeSpan,
|
|
108
|
-
allowedCalls: config.rateLimitAllowedCalls,
|
|
109
|
-
exceedHandler,
|
|
110
|
-
})
|
|
86
|
+
@Memoize(productsCache)
|
|
87
|
+
@Timeout(config.timeout)
|
|
88
|
+
@RateLimit(config.rateLimitTimeSpan, config.rateLimitAllowedCalls)
|
|
111
89
|
/**
|
|
112
90
|
* Retrieves all products with truncated descriptions
|
|
113
91
|
* @returns List of products with summarized descriptions
|
|
@@ -119,16 +97,8 @@ export default class ProductController {
|
|
|
119
97
|
);
|
|
120
98
|
}
|
|
121
99
|
|
|
122
|
-
@
|
|
123
|
-
|
|
124
|
-
keyResolver: (id: number) => id.toString(),
|
|
125
|
-
expirationTimeMs: config.memoizeTime,
|
|
126
|
-
})
|
|
127
|
-
@rateLimit({
|
|
128
|
-
timeSpanMs: config.rateLimitTimeSpan,
|
|
129
|
-
allowedCalls: config.rateLimitAllowedCalls,
|
|
130
|
-
exceedHandler,
|
|
131
|
-
})
|
|
100
|
+
@Memoize(productCache)
|
|
101
|
+
@RateLimit(config.rateLimitTimeSpan, config.rateLimitAllowedCalls)
|
|
132
102
|
/**
|
|
133
103
|
* Finds a product by its ID
|
|
134
104
|
* @param {number} id - Product ID as string
|
|
@@ -142,11 +112,9 @@ export default class ProductController {
|
|
|
142
112
|
return await convert(product, ZodProduct);
|
|
143
113
|
}
|
|
144
114
|
|
|
145
|
-
@
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
exceedHandler,
|
|
149
|
-
})
|
|
115
|
+
@Invalidate(productCache, false)
|
|
116
|
+
@Invalidate(productsCache)
|
|
117
|
+
@RateLimit(config.rateLimitTimeSpan, config.rateLimitAllowedCalls)
|
|
150
118
|
/**
|
|
151
119
|
* Updates an existing product
|
|
152
120
|
* @param {number} id - Product ID to update
|
|
@@ -162,18 +130,14 @@ export default class ProductController {
|
|
|
162
130
|
const product = await this.get(id);
|
|
163
131
|
if (product != null) {
|
|
164
132
|
await assign(product, updateData, ZodProduct);
|
|
165
|
-
await productCache.delete(updateData.id.toString());
|
|
166
|
-
await productsCache.delete("key");
|
|
167
133
|
} else {
|
|
168
134
|
throw new ResponseError("Product dose not exist.", 404);
|
|
169
135
|
}
|
|
170
136
|
}
|
|
171
137
|
|
|
172
|
-
@
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
exceedHandler,
|
|
176
|
-
})
|
|
138
|
+
@Invalidate(productCache, false)
|
|
139
|
+
@Invalidate(productsCache)
|
|
140
|
+
@RateLimit(config.rateLimitTimeSpan, config.rateLimitAllowedCalls)
|
|
177
141
|
/**
|
|
178
142
|
* Deletes a product by ID
|
|
179
143
|
* @param {number} id - Product ID to delete
|
|
@@ -186,8 +150,6 @@ export default class ProductController {
|
|
|
186
150
|
if (index == -1) {
|
|
187
151
|
throw new ResponseError("Product dose not exist.", 404);
|
|
188
152
|
}
|
|
189
|
-
await productCache.delete(id.toString());
|
|
190
|
-
await productsCache.delete("key");
|
|
191
153
|
products.splice(index, 1)[0];
|
|
192
154
|
}
|
|
193
155
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Router, Request, Response } from "express";
|
|
2
2
|
import asyncHandler from "express-async-handler";
|
|
3
3
|
import UserController from "./user.controller";
|
|
4
|
-
import { resolve } from "@/
|
|
4
|
+
import { resolve } from "@/core/container";
|
|
5
5
|
|
|
6
6
|
const router = Router();
|
|
7
7
|
const userController = resolve(UserController);
|
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
import { UserCreationDto, UserDto, ZodUserDto } from "./dto/user.dto";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { convert, stringToInteger } from "@/utilities/conversion";
|
|
2
|
+
import { ResponseError } from "@/core/error";
|
|
3
|
+
import { convert, stringToInteger } from "@/core/helpers";
|
|
5
4
|
import { config } from "@/config";
|
|
6
5
|
import { users } from "@/db";
|
|
7
6
|
import { User, ZodUser } from "@/entities/user.entity";
|
|
8
|
-
import { MapAsyncCache } from "@/
|
|
9
|
-
import { Injectable } from "@/
|
|
10
|
-
import { parallelMap } from "@/
|
|
7
|
+
import { Invalidate, MapAsyncCache, Memoize } from "@/core/cache";
|
|
8
|
+
import { Injectable } from "@/core/container";
|
|
9
|
+
import { parallelMap } from "@/core/parallel";
|
|
10
|
+
import { ErrorHandler, LogMethod, Timeout } from "@/core/middlewares";
|
|
11
|
+
import { RateLimit } from "@/core/rate-limit";
|
|
11
12
|
|
|
12
|
-
function
|
|
13
|
-
const message = "Too much call in allowed window";
|
|
14
|
-
throw new ResponseError(message, 429);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function invalidInputHandler(e: ResponseError) {
|
|
13
|
+
async function invalidInputHandler(e: ResponseError) {
|
|
18
14
|
const message = "Invalid input";
|
|
19
15
|
throw new ResponseError(message, 400, e?.message);
|
|
20
16
|
}
|
|
21
17
|
|
|
22
18
|
const usersCache = new MapAsyncCache<UserDto[]>(config.cacheSize);
|
|
23
|
-
const userCache = new MapAsyncCache<UserDto>(config.cacheSize);
|
|
24
19
|
|
|
25
20
|
@Injectable()
|
|
26
21
|
/**
|
|
@@ -31,9 +26,7 @@ const userCache = new MapAsyncCache<UserDto>(config.cacheSize);
|
|
|
31
26
|
export default class UserController {
|
|
32
27
|
// constructor(private readonly userService: UserService) { }
|
|
33
28
|
|
|
34
|
-
@
|
|
35
|
-
func: invalidInputHandler,
|
|
36
|
-
})
|
|
29
|
+
@ErrorHandler(invalidInputHandler)
|
|
37
30
|
/**
|
|
38
31
|
* Validates a string ID and converts it to a number.
|
|
39
32
|
*
|
|
@@ -44,9 +37,7 @@ export default class UserController {
|
|
|
44
37
|
return stringToInteger(id);
|
|
45
38
|
}
|
|
46
39
|
|
|
47
|
-
@
|
|
48
|
-
func: invalidInputHandler,
|
|
49
|
-
})
|
|
40
|
+
@ErrorHandler(invalidInputHandler)
|
|
50
41
|
/**
|
|
51
42
|
* Validates and creates a new User from the given DTO.
|
|
52
43
|
*
|
|
@@ -57,11 +48,8 @@ export default class UserController {
|
|
|
57
48
|
return await convert(user, ZodUser);
|
|
58
49
|
}
|
|
59
50
|
|
|
60
|
-
@
|
|
61
|
-
|
|
62
|
-
allowedCalls: config.rateLimitAllowedCalls,
|
|
63
|
-
exceedHandler,
|
|
64
|
-
})
|
|
51
|
+
@Invalidate(usersCache)
|
|
52
|
+
@RateLimit(config.rateLimitTimeSpan, config.rateLimitAllowedCalls)
|
|
65
53
|
/**
|
|
66
54
|
* Create a new user
|
|
67
55
|
* @param {User} user - User creation data validated by Zod schema
|
|
@@ -71,42 +59,26 @@ export default class UserController {
|
|
|
71
59
|
*/
|
|
72
60
|
public async create(user: User): Promise<void> {
|
|
73
61
|
users.push(user);
|
|
74
|
-
await usersCache.delete("key");
|
|
75
62
|
}
|
|
76
63
|
|
|
77
|
-
@
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
expirationTimeMs: config.memoizeTime,
|
|
81
|
-
})
|
|
82
|
-
@timeout(config.timeout)
|
|
83
|
-
@rateLimit({
|
|
84
|
-
timeSpanMs: config.rateLimitTimeSpan,
|
|
85
|
-
allowedCalls: config.rateLimitAllowedCalls,
|
|
86
|
-
exceedHandler,
|
|
87
|
-
})
|
|
64
|
+
@Memoize(usersCache)
|
|
65
|
+
@Timeout(config.timeout)
|
|
66
|
+
@RateLimit(config.rateLimitTimeSpan, config.rateLimitAllowedCalls)
|
|
88
67
|
/**
|
|
89
68
|
* Get all users
|
|
90
69
|
* @returns {Promise<UserDto[]>} List of users with hidden password fields
|
|
91
70
|
* @throws {ResponseError} 500 - When rate limit exceeded
|
|
92
71
|
*/
|
|
93
|
-
public async getAll(): Promise<UserDto[]> {
|
|
94
|
-
return await parallelMap(
|
|
95
|
-
|
|
96
|
-
|
|
72
|
+
public async getAll(signal?: AbortSignal): Promise<(UserDto | null)[]> {
|
|
73
|
+
return await parallelMap(users, async (user) =>
|
|
74
|
+
signal?.aborted == false
|
|
75
|
+
? await convert<User, UserDto>(user!, ZodUserDto)
|
|
76
|
+
: null
|
|
97
77
|
);
|
|
98
78
|
}
|
|
99
79
|
|
|
100
|
-
@
|
|
101
|
-
|
|
102
|
-
keyResolver: (id: number) => id.toString(),
|
|
103
|
-
expirationTimeMs: config.memoizeTime,
|
|
104
|
-
})
|
|
105
|
-
@rateLimit({
|
|
106
|
-
timeSpanMs: config.rateLimitTimeSpan,
|
|
107
|
-
allowedCalls: config.rateLimitAllowedCalls,
|
|
108
|
-
exceedHandler,
|
|
109
|
-
})
|
|
80
|
+
@Memoize(usersCache)
|
|
81
|
+
@RateLimit(config.rateLimitTimeSpan, config.rateLimitAllowedCalls)
|
|
110
82
|
/**
|
|
111
83
|
* Get user by ID
|
|
112
84
|
* @param {number} id - User ID as string
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { AsyncCache } from "utils-decorators";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* A simple in-memory map-based cache implementing AsyncCache<T>.
|
|
5
|
-
* This is a straightforward wrapper around a Map<string, T>.
|
|
6
|
-
*/
|
|
7
|
-
export class MapAsyncCache<T> implements AsyncCache<T> {
|
|
8
|
-
public constructor(
|
|
9
|
-
private capacity?: number,
|
|
10
|
-
private cache?: Map<string, T>
|
|
11
|
-
) {
|
|
12
|
-
this.capacity = this.capacity ?? Number.POSITIVE_INFINITY;
|
|
13
|
-
this.cache = this.cache ?? new Map<string, T>();
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Asynchronously set a value by key.
|
|
18
|
-
* @param key The cache key
|
|
19
|
-
* @param value The value to cache
|
|
20
|
-
*/
|
|
21
|
-
async set(key: string, value: T): Promise<void> {
|
|
22
|
-
if (value != null) {
|
|
23
|
-
if (this.cache?.has(key)) {
|
|
24
|
-
this.cache?.set(key, value);
|
|
25
|
-
} else {
|
|
26
|
-
if (
|
|
27
|
-
(this.capacity ?? Number.POSITIVE_INFINITY) > (this.cache?.size ?? 0)
|
|
28
|
-
) {
|
|
29
|
-
this.cache?.set(key, value);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
} else {
|
|
33
|
-
this.cache?.delete(key);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Asynchronously get a value by key.
|
|
39
|
-
* - If the key exists, returns the value.
|
|
40
|
-
* - If the key does not exist, returns undefined cast to T to satisfy the Promise<T> return type.
|
|
41
|
-
*
|
|
42
|
-
* Note: Returning undefined may be surprising for callers expecting a strict A/B.
|
|
43
|
-
* Consider returning `T | undefined` from AsyncCache if you can adjust the interface,
|
|
44
|
-
* or throw a ResponseError for "not found" depending on your usage.
|
|
45
|
-
*/
|
|
46
|
-
async get(key: string): Promise<T> {
|
|
47
|
-
if (this.cache?.has(key)) {
|
|
48
|
-
return this.cache?.get(key) ?? (null as T);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return null as T;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Asynchronously delete a value by key.
|
|
56
|
-
*/
|
|
57
|
-
async delete(key: string): Promise<void> {
|
|
58
|
-
this.cache?.delete(key);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Asynchronously check if a key exists in the cache.
|
|
63
|
-
*/
|
|
64
|
-
async has(key: string): Promise<boolean> {
|
|
65
|
-
return this.cache?.has(key) ?? false;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Asynchronously clear the cache.
|
|
70
|
-
*/
|
|
71
|
-
async clear(key: string): Promise<void> {
|
|
72
|
-
this.cache?.clear();
|
|
73
|
-
}
|
|
74
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|