codeweaver 2.2.0 → 2.3.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/.env.example +20 -0
- package/README.md +41 -14
- package/package.json +1 -1
- package/src/config.ts +8 -6
- package/src/constants.ts +6 -1
- package/src/main.ts +34 -10
- package/.vscode/settings.json +0 -3
package/.env.example
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Environment
|
|
2
|
+
NODE_ENV=development # or "production" depending on your deployment
|
|
3
|
+
|
|
4
|
+
# Server
|
|
5
|
+
HTTP=http://localhost:3000
|
|
6
|
+
PORT=3000
|
|
7
|
+
|
|
8
|
+
# Feature flags
|
|
9
|
+
SWAGGER=true
|
|
10
|
+
|
|
11
|
+
# Timeouts and limits
|
|
12
|
+
TIMEOUT=30
|
|
13
|
+
|
|
14
|
+
# Rate limiting (separate vars)
|
|
15
|
+
RATE_LIMIT_TIME_SPAN=60
|
|
16
|
+
RATE_LIMIT_ALLOWED_CALLS=100
|
|
17
|
+
|
|
18
|
+
# Memoization and cache
|
|
19
|
+
MEMOIZE_TIME=300
|
|
20
|
+
CACHE_SIZE=1000
|
package/README.md
CHANGED
|
@@ -10,10 +10,14 @@
|
|
|
10
10
|
- **Express**: A lightweight web framework for building server-side applications in Node.js.
|
|
11
11
|
- **TypeScript**: Adds strong typing for enhanced development experience and reduced runtime errors.
|
|
12
12
|
- **Modular Router Structure**: Automates importing and mounting routers, ensuring a clean separation of endpoints and logic for easier scalability.
|
|
13
|
+
- **Dependency resolver**: A simple dependency resolver that uses a lightweight container to manage and inject dependencies at runtime.
|
|
13
14
|
- **Swagger Integration**: Automatically generates interactive API documentation, facilitating easier understanding of available endpoints for developers and consumers.
|
|
14
15
|
- **Async Handlers**: Utilizes async/await syntax for cleaner, more maintainable asynchronous code without callback nesting.
|
|
15
16
|
- **Zod**: Implements schema validation for input data.
|
|
16
|
-
- **
|
|
17
|
+
- **Utils-decorators**: A collection of middleware utilities utils-decorators (throttling, caching, and error handling) designed to strengthen application resilience.
|
|
18
|
+
- **Logger**: A Winston-based logger (with LogForm) that provides scalable, leveled logging, structured JSON output, and pluggable transports (console and file)
|
|
19
|
+
|
|
20
|
+
Here's a revised Installation section that explicitly supports pnpm in addition to npm. I kept the formatting and steps intact, adding clear pnpm equivalents.
|
|
17
21
|
|
|
18
22
|
## Installation
|
|
19
23
|
|
|
@@ -29,9 +33,19 @@ To get started with the project, follow these steps:
|
|
|
29
33
|
|
|
30
34
|
2. **Install dependencies**:
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
Choose your package manager and install:
|
|
37
|
+
|
|
38
|
+
- Using npm:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- Using pnpm:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pnpm install
|
|
48
|
+
```
|
|
35
49
|
|
|
36
50
|
3. **Run the application**:
|
|
37
51
|
|
|
@@ -39,14 +53,29 @@ To get started with the project, follow these steps:
|
|
|
39
53
|
npm start
|
|
40
54
|
```
|
|
41
55
|
|
|
56
|
+
Or, if you used pnpm:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pnpm start
|
|
60
|
+
```
|
|
61
|
+
|
|
42
62
|
4. **Visit the Swagger UI**: Open your browser and go to `http://localhost:3000/api-docs` to view the automatically generated API documentation.
|
|
43
63
|
|
|
44
64
|
5. **Build**: Compile the TypeScript files for the production environment:
|
|
45
65
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
66
|
+
- Using npm:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm run build
|
|
70
|
+
npm run serve
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
- Using pnpm:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pnpm run build
|
|
77
|
+
pnpm run serve
|
|
78
|
+
```
|
|
50
79
|
|
|
51
80
|
## Sample Project Structure
|
|
52
81
|
|
|
@@ -89,9 +118,10 @@ Example of a basic router:
|
|
|
89
118
|
import { Router, Request, Response } from "express";
|
|
90
119
|
import asyncHandler from "express-async-handler";
|
|
91
120
|
import UserController from "./user.controller";
|
|
121
|
+
import { resolve } from "@/utilities/container";
|
|
92
122
|
|
|
93
123
|
const router = Router();
|
|
94
|
-
const userController =
|
|
124
|
+
const userController = resolve(UserController);
|
|
95
125
|
|
|
96
126
|
/**
|
|
97
127
|
* @swagger
|
|
@@ -210,17 +240,13 @@ import config from "@/config";
|
|
|
210
240
|
import { users } from "@/db";
|
|
211
241
|
import { User } from "@/entities/user.entity";
|
|
212
242
|
import { MapAsyncCache } from "@/utilities/cache/memory-cache";
|
|
243
|
+
import { Injectable } from "@/utilities/container";
|
|
213
244
|
|
|
214
245
|
function exceedHandler() {
|
|
215
246
|
const message = "Too much call in allowed window";
|
|
216
247
|
throw new ResponseError(message, 429);
|
|
217
248
|
}
|
|
218
249
|
|
|
219
|
-
function userNotFoundHandler(e: ResponseError) {
|
|
220
|
-
const message = "User not found.";
|
|
221
|
-
throw new ResponseError(message, 404, e?.message);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
250
|
function invalidInputHandler(e: ResponseError) {
|
|
225
251
|
const message = "Invalid input";
|
|
226
252
|
throw new ResponseError(message, 400, e?.message);
|
|
@@ -229,6 +255,7 @@ function invalidInputHandler(e: ResponseError) {
|
|
|
229
255
|
const usersCache = new MapAsyncCache<UserDto[]>(config.cacheSize);
|
|
230
256
|
const userCache = new MapAsyncCache<UserDto>(config.cacheSize);
|
|
231
257
|
|
|
258
|
+
@Injectable()
|
|
232
259
|
/**
|
|
233
260
|
* Controller for handling user-related operations
|
|
234
261
|
* @class UserController
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -4,8 +4,9 @@ import {
|
|
|
4
4
|
rateLimitTimeSpan,
|
|
5
5
|
rateLimitAllowedCalls,
|
|
6
6
|
timeout,
|
|
7
|
-
|
|
7
|
+
port,
|
|
8
8
|
cacheSize,
|
|
9
|
+
http,
|
|
9
10
|
} from "./constants";
|
|
10
11
|
import { SwaggerOptions } from "./swagger-options";
|
|
11
12
|
import { stringToBoolean } from "./utilities/conversion";
|
|
@@ -19,6 +20,7 @@ import { stringToBoolean } from "./utilities/conversion";
|
|
|
19
20
|
*/
|
|
20
21
|
interface Config {
|
|
21
22
|
devMode: boolean;
|
|
23
|
+
http: string;
|
|
22
24
|
port: number;
|
|
23
25
|
swagger: boolean;
|
|
24
26
|
swaggerOptions: SwaggerOptions;
|
|
@@ -29,11 +31,10 @@ interface Config {
|
|
|
29
31
|
cacheSize: number;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
const port = Number(process.env.PORT) || portNumber;
|
|
33
|
-
|
|
34
34
|
let config: Config = {
|
|
35
35
|
devMode: process.env.NODE_ENV !== productionEnvironment,
|
|
36
|
-
|
|
36
|
+
http: process.env.HTTP || http,
|
|
37
|
+
port: Number(process.env.PORT) || port,
|
|
37
38
|
swagger: stringToBoolean(process.env.SWAGGER || "true"),
|
|
38
39
|
swaggerOptions: {
|
|
39
40
|
swaggerDefinition: {
|
|
@@ -57,9 +58,10 @@ let config: Config = {
|
|
|
57
58
|
], // Path to the API docs
|
|
58
59
|
},
|
|
59
60
|
timeout: Number(process.env.TIMEOUT) || timeout,
|
|
60
|
-
rateLimitTimeSpan:
|
|
61
|
+
rateLimitTimeSpan:
|
|
62
|
+
Number(process.env.RATE_LIMIT_TIME_SPAN) || rateLimitTimeSpan,
|
|
61
63
|
rateLimitAllowedCalls:
|
|
62
|
-
Number(process.env.
|
|
64
|
+
Number(process.env.RATE_LIMIT_ALLOWED_CALLS) || rateLimitAllowedCalls,
|
|
63
65
|
memoizeTime: Number(process.env.MEMOIZE_TIME) || memoizeTime,
|
|
64
66
|
cacheSize: Number(process.env.CACHE_SIZE) || cacheSize,
|
|
65
67
|
};
|
package/src/constants.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
export const http = "http://localhost";
|
|
2
|
+
export const port = 3000;
|
|
1
3
|
export const timeout = 20000;
|
|
2
4
|
export const rateLimitTimeSpan = 60000;
|
|
3
5
|
export const rateLimitAllowedCalls = 300;
|
|
4
6
|
export const memoizeTime = 1000 * 60 * 60;
|
|
5
7
|
export const cacheSize = 1000;
|
|
6
8
|
export const productionEnvironment = "production";
|
|
7
|
-
export const
|
|
9
|
+
export const swaggerPath = "/api-docs";
|
|
10
|
+
export const routerDir = "/routers";
|
|
11
|
+
export const routerExtension = ".router";
|
|
12
|
+
export const defaultRouter = "index";
|
package/src/main.ts
CHANGED
|
@@ -5,6 +5,14 @@ import config from "./config";
|
|
|
5
5
|
import { ResponseError } from "./utilities/error-handling";
|
|
6
6
|
import { resolve } from "./utilities/container";
|
|
7
7
|
import { WinstonLoggerService } from "./utilities/logger/winston-logger.service";
|
|
8
|
+
import "dotenv/config";
|
|
9
|
+
import {
|
|
10
|
+
defaultRouter,
|
|
11
|
+
routerDir,
|
|
12
|
+
routerExtension,
|
|
13
|
+
swaggerPath,
|
|
14
|
+
} from "./constants";
|
|
15
|
+
//import cors from "cors";
|
|
8
16
|
|
|
9
17
|
/**
|
|
10
18
|
* Recursively loads Express routers from directory
|
|
@@ -30,17 +38,20 @@ function loadRouters(routerPath: string, basePath: string = "") {
|
|
|
30
38
|
|
|
31
39
|
// Only handle router files
|
|
32
40
|
if (
|
|
33
|
-
!entry.name.endsWith(
|
|
34
|
-
!entry.name.endsWith(
|
|
41
|
+
!entry.name.endsWith(`${routerExtension}.ts`) &&
|
|
42
|
+
!entry.name.endsWith(`${routerExtension}.js`)
|
|
35
43
|
) {
|
|
36
44
|
continue;
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
// Build route path safely
|
|
40
48
|
const routePath = path
|
|
41
|
-
.join(
|
|
49
|
+
.join(
|
|
50
|
+
basePath,
|
|
51
|
+
entry.name.replace(new RegExp(`\\${routerExtension}\\.([tj]s)$`), "")
|
|
52
|
+
)
|
|
42
53
|
.replace(/\\/g, "/")
|
|
43
|
-
.replace(
|
|
54
|
+
.replace(new RegExp(`\\/?${defaultRouter}$`), "");
|
|
44
55
|
|
|
45
56
|
// Optional: skip if the target path would be empty (maps to /)
|
|
46
57
|
const mountPath = "/" + (routePath || "");
|
|
@@ -53,11 +64,12 @@ function loadRouters(routerPath: string, basePath: string = "") {
|
|
|
53
64
|
}
|
|
54
65
|
|
|
55
66
|
const app = express();
|
|
67
|
+
//app.use(cors());
|
|
56
68
|
app.use(express.json());
|
|
57
69
|
app.use(express.urlencoded({ extended: true }));
|
|
58
70
|
|
|
59
71
|
// Automatically import all routers from the /src/routers directory
|
|
60
|
-
const routersPath = path.join(__dirname,
|
|
72
|
+
const routersPath = path.join(__dirname, routerDir);
|
|
61
73
|
loadRouters(routersPath);
|
|
62
74
|
|
|
63
75
|
// Swagger setup
|
|
@@ -65,7 +77,7 @@ if (config.swagger) {
|
|
|
65
77
|
const swaggerJsDoc = require("swagger-jsdoc");
|
|
66
78
|
const swaggerUi = require("swagger-ui-express");
|
|
67
79
|
const swaggerDocs = swaggerJsDoc(config.swaggerOptions);
|
|
68
|
-
app.use(
|
|
80
|
+
app.use(swaggerPath, swaggerUi.serve, swaggerUi.setup(swaggerDocs));
|
|
69
81
|
}
|
|
70
82
|
|
|
71
83
|
const logger = resolve(WinstonLoggerService);
|
|
@@ -74,21 +86,33 @@ const logger = resolve(WinstonLoggerService);
|
|
|
74
86
|
app.use(
|
|
75
87
|
(error: ResponseError, req: Request, res: Response, next: NextFunction) => {
|
|
76
88
|
const status = error.status ?? 500;
|
|
89
|
+
const errorObject = {
|
|
90
|
+
status,
|
|
91
|
+
code: error.code,
|
|
92
|
+
input: error.input,
|
|
93
|
+
stack: error.stack,
|
|
94
|
+
cause: error.cause,
|
|
95
|
+
};
|
|
96
|
+
|
|
77
97
|
res.status(status).json(error);
|
|
78
98
|
if (status < 500) {
|
|
79
|
-
logger.warn(error.message, "Invalid request", error.details);
|
|
99
|
+
logger.warn(error.message, "Invalid request", error.details, errorObject);
|
|
80
100
|
} else {
|
|
81
|
-
|
|
101
|
+
if (status == 401 || status == 403) {
|
|
102
|
+
logger.fatal(error.message, "Forbidden", error.details, errorObject);
|
|
103
|
+
} else {
|
|
104
|
+
logger.error(error.message, "Server error", error.details, errorObject);
|
|
105
|
+
}
|
|
82
106
|
}
|
|
83
107
|
}
|
|
84
108
|
);
|
|
85
109
|
|
|
86
110
|
// Start the server
|
|
87
111
|
app.listen(config.port, () => {
|
|
88
|
-
console.log(`Server is running on http
|
|
112
|
+
console.log(`Server is running on ${config.http}:${config.port}`);
|
|
89
113
|
if (config.devMode) {
|
|
90
114
|
console.log(
|
|
91
|
-
`Swagger UI is available at http
|
|
115
|
+
`Swagger UI is available at ${config.http}:${config.port}${swaggerPath}`
|
|
92
116
|
);
|
|
93
117
|
}
|
|
94
118
|
});
|
package/.vscode/settings.json
DELETED