codeweaver 2.0.0 → 2.2.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 +84 -144
- package/package.json +11 -3
- package/src/config.ts +11 -50
- package/src/constants.ts +1 -0
- package/src/db.ts +183 -0
- package/src/entities/order.entity.ts +68 -0
- package/src/entities/product.entity.ts +75 -0
- package/src/entities/user.entity.ts +38 -0
- package/src/main.ts +19 -7
- package/src/routers/orders/dto/order.dto.ts +54 -30
- package/src/routers/orders/index.router.ts +12 -7
- package/src/routers/orders/order.controller.ts +87 -79
- package/src/routers/products/dto/product.dto.ts +86 -31
- package/src/routers/products/index.router.ts +13 -6
- package/src/routers/products/product.controller.ts +109 -120
- package/src/routers/users/dto/user.dto.ts +13 -19
- package/src/routers/users/index.router.ts +7 -4
- package/src/routers/users/user.controller.ts +72 -98
- package/src/swagger-options.ts +7 -22
- package/src/utilities/assign.ts +44 -59
- package/src/utilities/cache/memory-cache.ts +74 -0
- package/src/utilities/cache/redis-cache.ts +111 -0
- package/src/utilities/container.ts +159 -0
- package/src/utilities/conversion.ts +158 -0
- package/src/utilities/error-handling.ts +98 -26
- package/src/utilities/logger/base-logger.interface.ts +11 -0
- package/src/utilities/logger/logger.config.ts +95 -0
- package/src/utilities/logger/logger.service.ts +122 -0
- package/src/utilities/logger/winston-logger.service.ts +16 -0
- package/src/utilities/types.ts +0 -23
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
**Codeweaver** is
|
|
5
|
+
**Codeweaver** is an unopinionated microframework built with `Express`, `TypeScript`, and `Zod` (v4), seamlessly integrated with `Swagger` for comprehensive API documentation. Its modular architecture for routers promotes scalability and organized development, making it easy to expand and maintain. Routers are automatically discovered and wired up through a conventional folder structure, simplifying project organization and reducing boilerplate. Routers can be nested, allowing you to compose complex route trees by placing sub-routers inside parent router folders. It also uses `utils-decorators`, a collection of middleware utilities (throttling, caching, and error handling) designed to strengthen application resilience.
|
|
6
6
|
|
|
7
7
|
## Features and Technologies Used
|
|
8
8
|
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
- **Swagger Integration**: Automatically generates interactive API documentation, facilitating easier understanding of available endpoints for developers and consumers.
|
|
14
14
|
- **Async Handlers**: Utilizes async/await syntax for cleaner, more maintainable asynchronous code without callback nesting.
|
|
15
15
|
- **Zod**: Implements schema validation for input data.
|
|
16
|
-
- **
|
|
17
|
-
- **utils-decorators**: Provides middleware utilities such as throttling and error handling for a more robust application.
|
|
16
|
+
- **utils-decorators**: A collection of middleware utilities (throttling, caching, and error handling) designed to strengthen application resilience.
|
|
18
17
|
|
|
19
18
|
## Installation
|
|
20
19
|
|
|
@@ -134,8 +133,9 @@ const userController = new UserController();
|
|
|
134
133
|
router.post(
|
|
135
134
|
"/",
|
|
136
135
|
asyncHandler(async (req: Request, res: Response) => {
|
|
137
|
-
const user = await userController.
|
|
138
|
-
|
|
136
|
+
const user = await userController.validateUserCreationDto(req.body);
|
|
137
|
+
await userController.create(user);
|
|
138
|
+
res.status(201).send();
|
|
139
139
|
})
|
|
140
140
|
);
|
|
141
141
|
|
|
@@ -160,7 +160,8 @@ router.post(
|
|
|
160
160
|
router.get(
|
|
161
161
|
"/:id",
|
|
162
162
|
asyncHandler(async (req: Request, res: Response) => {
|
|
163
|
-
const
|
|
163
|
+
const id = await userController.validateId(req.params.id);
|
|
164
|
+
const user = await userController.get(id);
|
|
164
165
|
res.json(user);
|
|
165
166
|
})
|
|
166
167
|
);
|
|
@@ -190,136 +191,44 @@ export = router;
|
|
|
190
191
|
**Controllers** in this Express TypeScript framework act as the intermediary between the incoming HTTP requests and the application logic. Each controller is responsible for handling specific routes and defining the behavior associated with those routes. This organization promotes a clean architecture by separating business logic, validation, and routing concerns.
|
|
191
192
|
|
|
192
193
|
Controllers can be organized within the router folders, allowing them to stay closely related to their respective routes. However, they are not limited to this structure and can be placed anywhere within the `src` folder as needed, providing flexibility in organizing the codebase.
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
For example, in the provided `UserController`, the `createUser` method demonstrates how to apply input validation and error handling through decorators. It employs `@rateLimit` to restrict the number of allowed requests within a specified timeframe, effectively guarding against too many rapid submissions. When an error arises, the `@onError` decorator provides a systematic way to handle exceptions, allowing for logging or other error management processes to be performed centrally.
|
|
194
|
+
Controllers leverage decorators from the `utils-decorators` package to implement throttling, caching, and error handling gracefully.
|
|
195
|
+
For example, in the provided `UserController`, the `createUser` method demonstrates how to apply error handling through decorators. It also employs `@rateLimit` to restrict the number of allowed requests within a specified timeframe, effectively guarding against too many rapid submissions. When an error arises, the `@onError` decorator provides a systematic way to handle exceptions, allowing for logging or other error management processes to be performed centrally.
|
|
197
196
|
|
|
198
197
|
Here’s a brief breakdown of key components used in the `UserController`:
|
|
199
198
|
|
|
200
|
-
- **Validation**: The `CreateUserDto` is validated against incoming data using the `@ZodInput` decorator, ensuring that only well-formed data is passed to the business logic, which is crucial for maintaining application stability.
|
|
201
|
-
|
|
202
|
-
```typescript
|
|
203
|
-
import { z } from "zod";
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Zod schema for User entity
|
|
207
|
-
* @typedef {Object} ZodUser
|
|
208
|
-
* @property {number} id - Unique identifier (min 1)
|
|
209
|
-
* @property {string} username - Username (min 3 chars)
|
|
210
|
-
* @property {string} email - Valid email format
|
|
211
|
-
* @property {string} password - Password (min 6 chars)
|
|
212
|
-
*/
|
|
213
|
-
export const ZodUser = z.object({
|
|
214
|
-
id: z.number().min(1).int(),
|
|
215
|
-
username: z.string().min(3),
|
|
216
|
-
email: z.email(),
|
|
217
|
-
password: z.string().min(6),
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
export const ZodUserCreationDto = ZodUser.omit({ id: true });
|
|
221
|
-
export const ZodUserDto = ZodUser.omit({ password: true });
|
|
222
|
-
|
|
223
|
-
export type User = z.infer<typeof ZodUser>;
|
|
224
|
-
export type UserCreationDto = z.infer<typeof ZodUserCreationDto>;
|
|
225
|
-
export type UserDto = z.infer<typeof ZodUserDto>;
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
- **Throttling and Rate Limiting**: The `@rateLimit` decorator is applied to safeguard the application's endpoints from abuse by limiting how frequently a particular method can be invoked.
|
|
229
|
-
|
|
230
|
-
- **Error Handling**: The `@onError` decorator captures any exceptions that occur during the execution of the createUser method, allowing for centralized error management, which can greatly simplify debugging and improve maintainability.
|
|
231
|
-
|
|
232
|
-
By using a well-organized controller structure, this project makes it easier to add, modify, and manage endpoints as the application grows. Developers can focus on implementing business logic while the controllers handle the intricacies of request parsing, validation, and response formatting. Additionally, this separation of concerns improves unit testing, as controllers can be tested independently from the rest of the application logic, ensuring robust and reliable API behavior.
|
|
233
|
-
|
|
234
|
-
Here is a quick reference to the UserController in practice:
|
|
235
|
-
|
|
236
199
|
```typescript
|
|
237
200
|
import {
|
|
238
|
-
User,
|
|
239
201
|
ZodUserCreationDto,
|
|
240
202
|
UserCreationDto,
|
|
241
203
|
UserDto,
|
|
204
|
+
ZodUserDto,
|
|
242
205
|
} from "./dto/user.dto";
|
|
243
206
|
import { memoizeAsync, onError, rateLimit, timeout } from "utils-decorators";
|
|
244
|
-
import {
|
|
245
|
-
import {
|
|
246
|
-
import { parseId } from "@/utilities/error-handling";
|
|
207
|
+
import { ResponseError } from "@/utilities/error-handling";
|
|
208
|
+
import { convert, stringToInteger } from "@/utilities/conversion";
|
|
247
209
|
import config from "@/config";
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
{
|
|
252
|
-
id: 1,
|
|
253
|
-
username: "johndoe",
|
|
254
|
-
email: "johndoe@gmail.com",
|
|
255
|
-
password: "S3cur3P@ssw0rd",
|
|
256
|
-
},
|
|
257
|
-
{
|
|
258
|
-
id: 2,
|
|
259
|
-
username: "janesmith",
|
|
260
|
-
email: "janesmith@yahoo.com",
|
|
261
|
-
password: "P@ssw0rd2024",
|
|
262
|
-
},
|
|
263
|
-
{
|
|
264
|
-
id: 3,
|
|
265
|
-
username: "michael89",
|
|
266
|
-
email: "michael89@hotmail.com",
|
|
267
|
-
password: "M1chael!2024",
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
id: 4,
|
|
271
|
-
username: "lisa.wong",
|
|
272
|
-
email: "lisa.wong@example.com",
|
|
273
|
-
password: "L1saW0ng!2024",
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
id: 5,
|
|
277
|
-
username: "alex_k",
|
|
278
|
-
email: "alex.k@gmail.com",
|
|
279
|
-
password: "A1ex#Key2024",
|
|
280
|
-
},
|
|
281
|
-
{
|
|
282
|
-
id: 6,
|
|
283
|
-
username: "emilyj",
|
|
284
|
-
email: "emilyj@hotmail.com",
|
|
285
|
-
password: "Em!ly0101",
|
|
286
|
-
},
|
|
287
|
-
{
|
|
288
|
-
id: 7,
|
|
289
|
-
username: "davidparker",
|
|
290
|
-
email: "david.parker@yahoo.com",
|
|
291
|
-
password: "D@v!d2024",
|
|
292
|
-
},
|
|
293
|
-
{
|
|
294
|
-
id: 8,
|
|
295
|
-
username: "sophia_m",
|
|
296
|
-
email: "sophia.m@gmail.com",
|
|
297
|
-
password: "Sophi@2024",
|
|
298
|
-
},
|
|
299
|
-
{
|
|
300
|
-
id: 9,
|
|
301
|
-
username: "chrisw",
|
|
302
|
-
email: "chrisw@outlook.com",
|
|
303
|
-
password: "Chri$Wong21",
|
|
304
|
-
},
|
|
305
|
-
{
|
|
306
|
-
id: 10,
|
|
307
|
-
username: "natalie_b",
|
|
308
|
-
email: "natalie_b@gmail.com",
|
|
309
|
-
password: "N@talie#B2024",
|
|
310
|
-
},
|
|
311
|
-
];
|
|
210
|
+
import { users } from "@/db";
|
|
211
|
+
import { User } from "@/entities/user.entity";
|
|
212
|
+
import { MapAsyncCache } from "@/utilities/cache/memory-cache";
|
|
312
213
|
|
|
313
214
|
function exceedHandler() {
|
|
314
215
|
const message = "Too much call in allowed window";
|
|
315
216
|
throw new ResponseError(message, 429);
|
|
316
217
|
}
|
|
317
218
|
|
|
318
|
-
function
|
|
219
|
+
function userNotFoundHandler(e: ResponseError) {
|
|
319
220
|
const message = "User not found.";
|
|
320
|
-
throw new ResponseError(message, 404, e
|
|
221
|
+
throw new ResponseError(message, 404, e?.message);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function invalidInputHandler(e: ResponseError) {
|
|
225
|
+
const message = "Invalid input";
|
|
226
|
+
throw new ResponseError(message, 400, e?.message);
|
|
321
227
|
}
|
|
322
228
|
|
|
229
|
+
const usersCache = new MapAsyncCache<UserDto[]>(config.cacheSize);
|
|
230
|
+
const userCache = new MapAsyncCache<UserDto>(config.cacheSize);
|
|
231
|
+
|
|
323
232
|
/**
|
|
324
233
|
* Controller for handling user-related operations
|
|
325
234
|
* @class UserController
|
|
@@ -328,63 +237,94 @@ function getUserErrorHandler(e: Error) {
|
|
|
328
237
|
export default class UserController {
|
|
329
238
|
// constructor(private readonly userService: UserService) { }
|
|
330
239
|
|
|
240
|
+
@onError({
|
|
241
|
+
func: invalidInputHandler,
|
|
242
|
+
})
|
|
243
|
+
/**
|
|
244
|
+
* Validates a string ID and converts it to a number.
|
|
245
|
+
*
|
|
246
|
+
* @param {string} id - The ID to validate and convert.
|
|
247
|
+
* @returns {number} The numeric value of the provided ID.
|
|
248
|
+
*/
|
|
249
|
+
public async validateId(id: string): Promise<number> {
|
|
250
|
+
return stringToInteger(id);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
@onError({
|
|
254
|
+
func: invalidInputHandler,
|
|
255
|
+
})
|
|
256
|
+
/**
|
|
257
|
+
* Validates and creates a new User from the given DTO.
|
|
258
|
+
*
|
|
259
|
+
* @param {UserCreationDto} user - The incoming UserCreationDto to validate and transform.
|
|
260
|
+
* @returns {User} A fully formed User object ready for persistence.
|
|
261
|
+
*/
|
|
262
|
+
public async validateUserCreationDto(user: UserCreationDto): Promise<User> {
|
|
263
|
+
const newUser = await ZodUserCreationDto.parseAsync(user);
|
|
264
|
+
return { ...newUser, id: users.length + 1 };
|
|
265
|
+
}
|
|
266
|
+
|
|
331
267
|
@rateLimit({
|
|
332
268
|
timeSpanMs: config.rateLimitTimeSpan,
|
|
333
269
|
allowedCalls: config.rateLimitAllowedCalls,
|
|
334
270
|
exceedHandler,
|
|
335
271
|
})
|
|
336
|
-
@Validate
|
|
337
272
|
/**
|
|
338
273
|
* Create a new user
|
|
339
|
-
* @param {
|
|
274
|
+
* @param {User} user - User creation data validated by Zod schema
|
|
340
275
|
* @returns {Promise<void>}
|
|
341
276
|
* @throws {ResponseError} 500 - When rate limit exceeded
|
|
342
277
|
* @throws {ResponseError} 400 - Invalid input data
|
|
343
278
|
*/
|
|
344
|
-
public async create(
|
|
345
|
-
users.push(
|
|
279
|
+
public async create(user: User): Promise<void> {
|
|
280
|
+
users.push(user);
|
|
281
|
+
await userCache.set(user.id.toString(), user as User);
|
|
282
|
+
await usersCache.delete("key");
|
|
346
283
|
}
|
|
347
284
|
|
|
348
|
-
@memoizeAsync(
|
|
349
|
-
|
|
350
|
-
|
|
285
|
+
@memoizeAsync({
|
|
286
|
+
cache: usersCache,
|
|
287
|
+
keyResolver: () => "key",
|
|
288
|
+
expirationTimeMs: config.memoizeTime,
|
|
351
289
|
})
|
|
290
|
+
@timeout(config.timeout)
|
|
352
291
|
@rateLimit({
|
|
353
292
|
timeSpanMs: config.rateLimitTimeSpan,
|
|
354
293
|
allowedCalls: config.rateLimitAllowedCalls,
|
|
355
294
|
exceedHandler,
|
|
356
295
|
})
|
|
357
296
|
/**
|
|
358
|
-
* Get
|
|
359
|
-
* @
|
|
360
|
-
* @
|
|
361
|
-
* @throws {ResponseError} 404 - User not found
|
|
362
|
-
* @throws {ResponseError} 400 - Invalid ID format
|
|
297
|
+
* Get all users
|
|
298
|
+
* @returns {Promise<UserDto[]>} List of users with hidden password fields
|
|
299
|
+
* @throws {ResponseError} 500 - When rate limit exceeded
|
|
363
300
|
*/
|
|
364
|
-
public async
|
|
365
|
-
|
|
366
|
-
const user = users.find((user) => user.id === response);
|
|
367
|
-
if (user == null) throw new ResponseError("User dose not exist.", 404);
|
|
368
|
-
return user satisfies User;
|
|
301
|
+
public async getAll(): Promise<UserDto[]> {
|
|
302
|
+
return users as UserDto[];
|
|
369
303
|
}
|
|
370
304
|
|
|
371
|
-
@memoizeAsync(
|
|
372
|
-
|
|
305
|
+
@memoizeAsync({
|
|
306
|
+
cache: userCache,
|
|
307
|
+
keyResolver: (id: number) => id.toString(),
|
|
308
|
+
expirationTimeMs: config.memoizeTime,
|
|
309
|
+
})
|
|
373
310
|
@rateLimit({
|
|
374
311
|
timeSpanMs: config.rateLimitTimeSpan,
|
|
375
312
|
allowedCalls: config.rateLimitAllowedCalls,
|
|
376
313
|
exceedHandler,
|
|
377
314
|
})
|
|
378
315
|
/**
|
|
379
|
-
* Get
|
|
380
|
-
* @
|
|
381
|
-
* @
|
|
316
|
+
* Get user by ID
|
|
317
|
+
* @param {number} id - User ID as string
|
|
318
|
+
* @returns {Promise<UserDto>} User details or error object
|
|
319
|
+
* @throws {ResponseError} 404 - User not found
|
|
320
|
+
* @throws {ResponseError} 400 - Invalid ID format
|
|
382
321
|
*/
|
|
383
|
-
public async
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
}
|
|
322
|
+
public async get(id: number): Promise<UserDto> {
|
|
323
|
+
const user = users.find((user) => user.id === id);
|
|
324
|
+
if (user == null) {
|
|
325
|
+
throw new ResponseError("Product not found");
|
|
326
|
+
}
|
|
327
|
+
return convert(user!, ZodUserDto);
|
|
388
328
|
}
|
|
389
329
|
}
|
|
390
330
|
```
|
|
@@ -401,7 +341,7 @@ Once the application is running, visit the Swagger UI at http://localhost:3000/a
|
|
|
401
341
|
|
|
402
342
|
### Decorators
|
|
403
343
|
|
|
404
|
-
To prevent abuse of your API, you can utilize throttling, and
|
|
344
|
+
To prevent abuse of your API, you can utilize throttling, caching, and error handling decorators from the `utils-decorators` packages respectively. This packages provides decorators that can be applied directly to your service and controller classes.
|
|
405
345
|
|
|
406
346
|
### Contributing
|
|
407
347
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeweaver",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"main": "src/main.ts",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-codeweaver-app": "./command.js"
|
|
@@ -26,21 +26,29 @@
|
|
|
26
26
|
"nested routers",
|
|
27
27
|
"decorator",
|
|
28
28
|
"swagger",
|
|
29
|
-
"zod"
|
|
29
|
+
"zod",
|
|
30
|
+
"utils-decorators"
|
|
30
31
|
],
|
|
31
32
|
"author": "js-code-crafter",
|
|
32
33
|
"license": "MIT",
|
|
33
34
|
"description": "A lightweight framework built on top of Express and TypeScript",
|
|
34
35
|
"dependencies": {
|
|
36
|
+
"cors": "^2.8.5",
|
|
35
37
|
"dotenv": "^17.2.3",
|
|
36
38
|
"express": "^5.1.0",
|
|
37
39
|
"express-async-handler": "^1.2.0",
|
|
40
|
+
"express-winston": "^4.2.0",
|
|
41
|
+
"ioredis": "^5.8.2",
|
|
42
|
+
"logform": "^2.7.0",
|
|
43
|
+
"reflect-metadata": "^0.2.2",
|
|
38
44
|
"swagger-jsdoc": "^3.7.0",
|
|
39
|
-
"ts-zod4-decorators": "^1.0.0",
|
|
40
45
|
"utils-decorators": "^2.10.0",
|
|
46
|
+
"winston": "^3.18.3",
|
|
47
|
+
"winston-daily-rotate-file": "^5.0.0",
|
|
41
48
|
"zod": "^4.0.14"
|
|
42
49
|
},
|
|
43
50
|
"devDependencies": {
|
|
51
|
+
"@types/cors": "^2.8.19",
|
|
44
52
|
"@types/express": "^5.0.3",
|
|
45
53
|
"@types/node": "^24.1.0",
|
|
46
54
|
"copyfiles": "^2.4.1",
|
package/src/config.ts
CHANGED
|
@@ -5,53 +5,10 @@ import {
|
|
|
5
5
|
rateLimitAllowedCalls,
|
|
6
6
|
timeout,
|
|
7
7
|
portNumber,
|
|
8
|
+
cacheSize,
|
|
8
9
|
} from "./constants";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* Server configuration interface
|
|
12
|
-
* @interface
|
|
13
|
-
* @property {string} url - Base server URL
|
|
14
|
-
*/
|
|
15
|
-
interface Server {
|
|
16
|
-
url: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* API information structure
|
|
21
|
-
* @interface
|
|
22
|
-
* @property {string} title - API title
|
|
23
|
-
* @property {string} version - API version
|
|
24
|
-
* @property {string} description - API description
|
|
25
|
-
*/
|
|
26
|
-
interface Info {
|
|
27
|
-
title: string;
|
|
28
|
-
version: string;
|
|
29
|
-
description: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Swagger definition structure
|
|
34
|
-
* @interface
|
|
35
|
-
* @property {string} openApi - OpenAPI specification version
|
|
36
|
-
* @property {Info} info - API information
|
|
37
|
-
* @property {Server[]} servers - List of server configurations
|
|
38
|
-
*/
|
|
39
|
-
interface SwaggerDefinition {
|
|
40
|
-
openApi: string;
|
|
41
|
-
info: Info;
|
|
42
|
-
servers: Server[];
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Swagger configuration options
|
|
47
|
-
* @interface
|
|
48
|
-
* @property {SwaggerDefinition} swaggerDefinition - Swagger definition object
|
|
49
|
-
* @property {string[]} apis - Paths to API documentation files
|
|
50
|
-
*/
|
|
51
|
-
interface SwaggerOptions {
|
|
52
|
-
swaggerDefinition: SwaggerDefinition;
|
|
53
|
-
apis: string[];
|
|
54
|
-
}
|
|
10
|
+
import { SwaggerOptions } from "./swagger-options";
|
|
11
|
+
import { stringToBoolean } from "./utilities/conversion";
|
|
55
12
|
|
|
56
13
|
/**
|
|
57
14
|
* Main application configuration
|
|
@@ -63,11 +20,13 @@ interface SwaggerOptions {
|
|
|
63
20
|
interface Config {
|
|
64
21
|
devMode: boolean;
|
|
65
22
|
port: number;
|
|
23
|
+
swagger: boolean;
|
|
66
24
|
swaggerOptions: SwaggerOptions;
|
|
67
25
|
timeout: number;
|
|
68
26
|
rateLimitTimeSpan: number;
|
|
69
27
|
rateLimitAllowedCalls: number;
|
|
70
28
|
memoizeTime: number;
|
|
29
|
+
cacheSize: number;
|
|
71
30
|
}
|
|
72
31
|
|
|
73
32
|
const port = Number(process.env.PORT) || portNumber;
|
|
@@ -75,6 +34,7 @@ const port = Number(process.env.PORT) || portNumber;
|
|
|
75
34
|
let config: Config = {
|
|
76
35
|
devMode: process.env.NODE_ENV !== productionEnvironment,
|
|
77
36
|
port,
|
|
37
|
+
swagger: stringToBoolean(process.env.SWAGGER || "true"),
|
|
78
38
|
swaggerOptions: {
|
|
79
39
|
swaggerDefinition: {
|
|
80
40
|
openApi: "3.0.0",
|
|
@@ -90,10 +50,10 @@ let config: Config = {
|
|
|
90
50
|
],
|
|
91
51
|
},
|
|
92
52
|
apis: [
|
|
93
|
-
"./src/routers/index.ts",
|
|
94
|
-
"./src/routers/**/*.ts",
|
|
95
|
-
"./src/routers/index.js",
|
|
96
|
-
"./src/routers/**/*.js",
|
|
53
|
+
"./src/routers/index.router.ts",
|
|
54
|
+
"./src/routers/**/*.router.ts",
|
|
55
|
+
"./src/routers/index.router.js",
|
|
56
|
+
"./src/routers/**/*.router.js",
|
|
97
57
|
], // Path to the API docs
|
|
98
58
|
},
|
|
99
59
|
timeout: Number(process.env.TIMEOUT) || timeout,
|
|
@@ -101,6 +61,7 @@ let config: Config = {
|
|
|
101
61
|
rateLimitAllowedCalls:
|
|
102
62
|
Number(process.env.RATE_LIMIT) || rateLimitAllowedCalls,
|
|
103
63
|
memoizeTime: Number(process.env.MEMOIZE_TIME) || memoizeTime,
|
|
64
|
+
cacheSize: Number(process.env.CACHE_SIZE) || cacheSize,
|
|
104
65
|
};
|
|
105
66
|
|
|
106
67
|
export default config;
|
package/src/constants.ts
CHANGED
|
@@ -2,5 +2,6 @@ export const timeout = 20000;
|
|
|
2
2
|
export const rateLimitTimeSpan = 60000;
|
|
3
3
|
export const rateLimitAllowedCalls = 300;
|
|
4
4
|
export const memoizeTime = 1000 * 60 * 60;
|
|
5
|
+
export const cacheSize = 1000;
|
|
5
6
|
export const productionEnvironment = "production";
|
|
6
7
|
export const portNumber = 3000;
|
package/src/db.ts
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { Order } from "@/entities/order.entity";
|
|
2
|
+
import { Product } from "@/entities/product.entity";
|
|
3
|
+
import { User } from "@/entities/user.entity";
|
|
4
|
+
|
|
5
|
+
// Array to store users (as a mock database)
|
|
6
|
+
export const users: User[] = [
|
|
7
|
+
{
|
|
8
|
+
id: 1,
|
|
9
|
+
username: "johndoe",
|
|
10
|
+
email: "johndoe@gmail.com",
|
|
11
|
+
password: "S3cur3P@ssw0rd",
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: 2,
|
|
15
|
+
username: "janesmith",
|
|
16
|
+
email: "janesmith@yahoo.com",
|
|
17
|
+
password: "P@ssw0rd2024",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 3,
|
|
21
|
+
username: "michael89",
|
|
22
|
+
email: "michael89@hotmail.com",
|
|
23
|
+
password: "M1chael!2024",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 4,
|
|
27
|
+
username: "lisa.wong",
|
|
28
|
+
email: "lisa.wong@example.com",
|
|
29
|
+
password: "L1saW0ng!2024",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: 5,
|
|
33
|
+
username: "alex_k",
|
|
34
|
+
email: "alex.k@gmail.com",
|
|
35
|
+
password: "A1ex#Key2024",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 6,
|
|
39
|
+
username: "emilyj",
|
|
40
|
+
email: "emilyj@hotmail.com",
|
|
41
|
+
password: "Em!ly0101",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 7,
|
|
45
|
+
username: "davidparker",
|
|
46
|
+
email: "david.parker@yahoo.com",
|
|
47
|
+
password: "D@v!d2024",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 8,
|
|
51
|
+
username: "sophia_m",
|
|
52
|
+
email: "sophia.m@gmail.com",
|
|
53
|
+
password: "Sophi@2024",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 9,
|
|
57
|
+
username: "chrisw",
|
|
58
|
+
email: "chrisw@outlook.com",
|
|
59
|
+
password: "Chri$Wong21",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 10,
|
|
63
|
+
username: "natalie_b",
|
|
64
|
+
email: "natalie_b@gmail.com",
|
|
65
|
+
password: "N@talie#B2024",
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
// Array to store products (as a mock database)
|
|
70
|
+
export const products: Product[] = [
|
|
71
|
+
{
|
|
72
|
+
id: 1,
|
|
73
|
+
name: "ASUS ROG Zephyrus G15",
|
|
74
|
+
price: 45000000,
|
|
75
|
+
description: "Gaming laptop with AMD Ryzen 9 5900HS and RTX 3080 GPU",
|
|
76
|
+
category: "Electronics",
|
|
77
|
+
stock: 15,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: 2,
|
|
81
|
+
name: "Sony WH-1000XM5 Wireless Headphones",
|
|
82
|
+
price: 12000000,
|
|
83
|
+
description:
|
|
84
|
+
"Premium noise-canceling over-ear headphones with 30hr battery",
|
|
85
|
+
category: "Electronics",
|
|
86
|
+
stock: 8,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: 3,
|
|
90
|
+
name: "LG Smart Inverter Microwave",
|
|
91
|
+
price: 25000000,
|
|
92
|
+
description: "1.7 cu.ft countertop microwave with smart sensor cooking",
|
|
93
|
+
category: "Appliances",
|
|
94
|
+
stock: 5,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: 4,
|
|
98
|
+
name: "Trek Marlin 5 Mountain Bike",
|
|
99
|
+
price: 18000000,
|
|
100
|
+
description: "Entry-level mountain bike with aluminum frame and 21 speeds",
|
|
101
|
+
category: "Sports",
|
|
102
|
+
stock: 3,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: 5,
|
|
106
|
+
name: "DeLonghi Espresso Machine",
|
|
107
|
+
price: 6500000,
|
|
108
|
+
description: "Compact espresso maker with manual milk frother",
|
|
109
|
+
category: "Kitchen",
|
|
110
|
+
stock: 12,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: 6,
|
|
114
|
+
name: "Anker Wireless Charger",
|
|
115
|
+
price: 1200000,
|
|
116
|
+
description: "15W fast wireless charger with anti-slip surface",
|
|
117
|
+
category: "Mobile Accessories",
|
|
118
|
+
stock: 30,
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: 7,
|
|
122
|
+
name: "Logitech MX Master 3 Mouse",
|
|
123
|
+
price: 4500000,
|
|
124
|
+
description: "Ergonomic wireless mouse with Darkfield tracking",
|
|
125
|
+
category: "Computer Accessories",
|
|
126
|
+
stock: 18,
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: 8,
|
|
130
|
+
name: "Kindle Paperwhite",
|
|
131
|
+
price: 3800000,
|
|
132
|
+
description: 'Waterproof e-reader with 6.8" 300ppi display',
|
|
133
|
+
category: "Electronics",
|
|
134
|
+
stock: 9,
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: 9,
|
|
138
|
+
name: "Dyson V11 Vacuum Cleaner",
|
|
139
|
+
price: 32000000,
|
|
140
|
+
description: "Cordless stick vacuum with LCD screen and 60min runtime",
|
|
141
|
+
category: "Home Appliances",
|
|
142
|
+
stock: 7,
|
|
143
|
+
},
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
// Array to store orders (as a mock database)
|
|
147
|
+
export const orders: Order[] = [
|
|
148
|
+
{
|
|
149
|
+
id: 1,
|
|
150
|
+
userId: 1,
|
|
151
|
+
products: [
|
|
152
|
+
{ productId: 2, quantity: 1 },
|
|
153
|
+
{ productId: 6, quantity: 2 },
|
|
154
|
+
],
|
|
155
|
+
status: "Delivered",
|
|
156
|
+
total: 14400,
|
|
157
|
+
createdAt: new Date("2024-01-15"),
|
|
158
|
+
deliveredAt: new Date("2024-02-10"),
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: 2,
|
|
162
|
+
userId: 3,
|
|
163
|
+
products: [
|
|
164
|
+
{ productId: 9, quantity: 1 },
|
|
165
|
+
{ productId: 7, quantity: 1 },
|
|
166
|
+
],
|
|
167
|
+
status: "Processing",
|
|
168
|
+
total: 36500,
|
|
169
|
+
createdAt: new Date("2024-03-20"),
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
id: 3,
|
|
173
|
+
userId: 2,
|
|
174
|
+
products: [
|
|
175
|
+
{ productId: 1, quantity: 1 },
|
|
176
|
+
{ productId: 4, quantity: 2 },
|
|
177
|
+
],
|
|
178
|
+
status: "Canceled",
|
|
179
|
+
total: 81000,
|
|
180
|
+
createdAt: new Date("2024-05-01"),
|
|
181
|
+
canceledAt: new Date("2024-05-03"),
|
|
182
|
+
},
|
|
183
|
+
];
|