create-charcole 2.2.0 → 2.2.2
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/.github/workflows/release.yml +26 -26
- package/CHANGELOG.md +301 -301
- package/LICENSE +21 -21
- package/README.md +357 -354
- package/bin/index.js +494 -444
- package/bin/lib/pkgManager.js +49 -49
- package/bin/lib/templateHandler.js +33 -33
- package/package.json +42 -27
- package/packages/swagger/BACKWARD_COMPATIBILITY.md +1 -1
- package/packages/swagger/CHANGELOG.md +1 -1
- package/packages/swagger/README.md +3 -3
- package/packages/swagger/package.json +57 -44
- package/packages/swagger/src/index.d.ts +126 -126
- package/packages/swagger/src/index.js +12 -12
- package/packages/swagger/src/setup.js +100 -100
- package/template/js/.env.example +16 -15
- package/template/js/README.md +982 -978
- package/template/js/basePackage.json +26 -26
- package/template/js/src/app.js +81 -81
- package/template/js/src/config/constants.js +20 -20
- package/template/js/src/config/env.js +26 -26
- package/template/js/src/config/swagger.config.js +15 -15
- package/template/js/src/lib/swagger/SWAGGER_GUIDE.md +3 -3
- package/template/js/src/middlewares/errorHandler.js +180 -180
- package/template/js/src/middlewares/requestLogger.js +33 -33
- package/template/js/src/middlewares/validateRequest.js +42 -42
- package/template/js/src/modules/auth/auth.constants.js +3 -3
- package/template/js/src/modules/auth/auth.controller.js +29 -29
- package/template/js/src/modules/auth/auth.middlewares.js +19 -19
- package/template/js/src/modules/auth/auth.routes.js +131 -131
- package/template/js/src/modules/auth/auth.schemas.js +60 -60
- package/template/js/src/modules/auth/auth.service.js +67 -67
- package/template/js/src/modules/auth/package.json +6 -6
- package/template/js/src/modules/health/controller.js +151 -151
- package/template/js/src/modules/swagger/package.json +5 -5
- package/template/js/src/repositories/user.repo.js +19 -19
- package/template/js/src/routes/index.js +25 -25
- package/template/js/src/routes/protected.js +57 -57
- package/template/js/src/server.js +38 -38
- package/template/js/src/utils/AppError.js +182 -182
- package/template/js/src/utils/logger.js +73 -73
- package/template/js/src/utils/response.js +51 -51
- package/template/ts/.env.example +16 -15
- package/template/ts/README.md +982 -978
- package/template/ts/basePackage.json +36 -36
- package/template/ts/build.js +46 -46
- package/template/ts/src/app.ts +71 -71
- package/template/ts/src/config/constants.ts +27 -27
- package/template/ts/src/config/env.ts +40 -40
- package/template/ts/src/config/swagger.config.ts +30 -30
- package/template/ts/src/lib/swagger/SWAGGER_GUIDE.md +2 -2
- package/template/ts/src/middlewares/errorHandler.ts +201 -201
- package/template/ts/src/middlewares/requestLogger.ts +38 -38
- package/template/ts/src/middlewares/validateRequest.ts +46 -46
- package/template/ts/src/modules/auth/auth.constants.ts +6 -6
- package/template/ts/src/modules/auth/auth.controller.ts +32 -32
- package/template/ts/src/modules/auth/auth.middlewares.ts +46 -46
- package/template/ts/src/modules/auth/auth.routes.ts +52 -52
- package/template/ts/src/modules/auth/auth.schemas.ts +73 -73
- package/template/ts/src/modules/auth/auth.service.ts +106 -106
- package/template/ts/src/modules/auth/package.json +10 -10
- package/template/ts/src/modules/health/controller.ts +80 -80
- package/template/ts/src/modules/swagger/package.json +5 -5
- package/template/ts/src/repositories/user.repo.ts +33 -33
- package/template/ts/src/routes/index.ts +24 -24
- package/template/ts/src/routes/protected.ts +46 -46
- package/template/ts/src/server.ts +41 -41
- package/template/ts/src/types/express.d.ts +9 -9
- package/template/ts/src/utils/AppError.ts +220 -220
- package/template/ts/src/utils/logger.ts +55 -55
- package/template/ts/src/utils/response.ts +100 -100
- package/template/ts/tsconfig.json +26 -26
- package/packages/swagger/package-lock.json +0 -1715
- package/tmpclaude-1049-cwd +0 -1
- package/tmpclaude-3e37-cwd +0 -1
- package/tmpclaude-4d73-cwd +0 -1
- package/tmpclaude-8a8e-cwd +0 -1
|
@@ -1,201 +1,201 @@
|
|
|
1
|
-
import { ZodError } from "zod";
|
|
2
|
-
import { HTTP_STATUS, ERROR_MESSAGES } from "../config/constants.ts";
|
|
3
|
-
import { logger } from "../utils/logger.ts";
|
|
4
|
-
import {
|
|
5
|
-
AppError,
|
|
6
|
-
ValidationError,
|
|
7
|
-
InternalServerError,
|
|
8
|
-
AuthenticationError,
|
|
9
|
-
AuthorizationError,
|
|
10
|
-
NotFoundError,
|
|
11
|
-
ConflictError,
|
|
12
|
-
BadRequestError,
|
|
13
|
-
} from "../utils/AppError.ts";
|
|
14
|
-
import { env } from "../config/env.ts";
|
|
15
|
-
import { Request, Response, NextFunction } from "express";
|
|
16
|
-
|
|
17
|
-
// Custom type for async route handlers
|
|
18
|
-
type AsyncRequestHandler = (
|
|
19
|
-
req: Request,
|
|
20
|
-
res: Response,
|
|
21
|
-
next: NextFunction,
|
|
22
|
-
) => Promise<any> | any;
|
|
23
|
-
|
|
24
|
-
const normalizeError = (err: unknown): AppError => {
|
|
25
|
-
if (err instanceof AppError) {
|
|
26
|
-
return err;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (err instanceof ZodError) {
|
|
30
|
-
const errors = err.errors.map((e) => ({
|
|
31
|
-
field: e.path.join("."),
|
|
32
|
-
message: e.message,
|
|
33
|
-
code: e.code,
|
|
34
|
-
}));
|
|
35
|
-
return new ValidationError(ERROR_MESSAGES.VALIDATION_ERROR, errors);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (err instanceof Error) {
|
|
39
|
-
if (err instanceof SyntaxError) {
|
|
40
|
-
return new InternalServerError("Syntax error in application code", err, {
|
|
41
|
-
type: "SyntaxError",
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (err instanceof TypeError) {
|
|
46
|
-
return new InternalServerError("Type error in application", err, {
|
|
47
|
-
type: "TypeError",
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (err instanceof ReferenceError) {
|
|
52
|
-
return new InternalServerError("Reference error in application", err, {
|
|
53
|
-
type: "ReferenceError",
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (err instanceof RangeError) {
|
|
58
|
-
return new InternalServerError("Range error in application", err, {
|
|
59
|
-
type: "RangeError",
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return new InternalServerError(
|
|
64
|
-
err.message || ERROR_MESSAGES.SERVER_ERROR,
|
|
65
|
-
err,
|
|
66
|
-
{ type: "UnknownError" },
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const errorMessage =
|
|
71
|
-
typeof err === "string"
|
|
72
|
-
? err
|
|
73
|
-
: err &&
|
|
74
|
-
typeof err === "object" &&
|
|
75
|
-
"message" in err &&
|
|
76
|
-
typeof err.message === "string"
|
|
77
|
-
? err.message
|
|
78
|
-
: ERROR_MESSAGES.SERVER_ERROR;
|
|
79
|
-
|
|
80
|
-
return new InternalServerError(
|
|
81
|
-
errorMessage,
|
|
82
|
-
err instanceof Error ? err : new Error(String(err)),
|
|
83
|
-
{ type: "UnknownError" },
|
|
84
|
-
);
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const logError = (appError: AppError, req: Request): void => {
|
|
88
|
-
const errorDetails = {
|
|
89
|
-
type: appError.isOperational ? "OPERATIONAL" : "PROGRAMMER",
|
|
90
|
-
code: appError.code,
|
|
91
|
-
message: appError.message,
|
|
92
|
-
statusCode: appError.statusCode,
|
|
93
|
-
method: req.method,
|
|
94
|
-
path: req.path,
|
|
95
|
-
query: req.query,
|
|
96
|
-
ip: req.ip,
|
|
97
|
-
userAgent: req.get("user-agent"),
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
if (appError.isOperational) {
|
|
101
|
-
logger.warn(`Operational Error: ${appError.code}`, errorDetails);
|
|
102
|
-
} else {
|
|
103
|
-
logger.error(
|
|
104
|
-
`Programmer Error: ${appError.code}`,
|
|
105
|
-
errorDetails,
|
|
106
|
-
appError.stack,
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
appError instanceof ValidationError &&
|
|
112
|
-
(appError as ValidationError).errors
|
|
113
|
-
) {
|
|
114
|
-
const validationError = appError as ValidationError;
|
|
115
|
-
logger.debug("Validation errors", { errors: validationError.errors });
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (appError.cause) {
|
|
119
|
-
const causeMessage =
|
|
120
|
-
appError.cause instanceof Error
|
|
121
|
-
? appError.cause.message
|
|
122
|
-
: String(appError.cause);
|
|
123
|
-
logger.debug("Error cause", { cause: causeMessage });
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const sendErrorResponse = (res: Response, appError: AppError): void => {
|
|
128
|
-
const statusCode = appError.statusCode || HTTP_STATUS.INTERNAL_SERVER_ERROR;
|
|
129
|
-
|
|
130
|
-
if (!appError.isOperational && env.isProduction) {
|
|
131
|
-
res.status(statusCode).json({
|
|
132
|
-
success: false,
|
|
133
|
-
message: ERROR_MESSAGES.SERVER_ERROR,
|
|
134
|
-
code: "INTERNAL_SERVER_ERROR",
|
|
135
|
-
timestamp: new Date().toISOString(),
|
|
136
|
-
});
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const response =
|
|
141
|
-
(appError as any).toJSON && typeof (appError as any).toJSON === "function"
|
|
142
|
-
? (appError as any).toJSON()
|
|
143
|
-
: {
|
|
144
|
-
success: false,
|
|
145
|
-
message: appError.message,
|
|
146
|
-
code: appError.code,
|
|
147
|
-
statusCode,
|
|
148
|
-
timestamp: new Date().toISOString(),
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
res.status(statusCode).json(response);
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Global Error Handler Middleware
|
|
156
|
-
*
|
|
157
|
-
* This middleware catches all errors and provides a unified way to handle them.
|
|
158
|
-
* MUST be the last middleware in the app.
|
|
159
|
-
*
|
|
160
|
-
* Features:
|
|
161
|
-
* - Distinguishes between operational and programmer errors
|
|
162
|
-
* - Logs errors with full context
|
|
163
|
-
* - Hides sensitive info in production
|
|
164
|
-
* - Formats JSON responses consistently
|
|
165
|
-
*/
|
|
166
|
-
export const errorHandler = (
|
|
167
|
-
err: unknown,
|
|
168
|
-
req: Request,
|
|
169
|
-
res: Response,
|
|
170
|
-
next: NextFunction,
|
|
171
|
-
): void => {
|
|
172
|
-
const appError = normalizeError(err);
|
|
173
|
-
|
|
174
|
-
logError(appError, req);
|
|
175
|
-
|
|
176
|
-
sendErrorResponse(res, appError);
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Async error wrapper
|
|
181
|
-
* Wrap async route handlers to catch errors and pass to middleware
|
|
182
|
-
*
|
|
183
|
-
* Example:
|
|
184
|
-
* router.get('/users/:id', asyncHandler(getUserHandler))
|
|
185
|
-
*/
|
|
186
|
-
export const asyncHandler = (fn: AsyncRequestHandler) => {
|
|
187
|
-
return (req: Request, res: Response, next: NextFunction): void => {
|
|
188
|
-
Promise.resolve(fn(req, res, next)).catch(next);
|
|
189
|
-
};
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
export {
|
|
193
|
-
AppError,
|
|
194
|
-
ValidationError,
|
|
195
|
-
InternalServerError,
|
|
196
|
-
AuthenticationError,
|
|
197
|
-
AuthorizationError,
|
|
198
|
-
NotFoundError,
|
|
199
|
-
ConflictError,
|
|
200
|
-
BadRequestError,
|
|
201
|
-
};
|
|
1
|
+
import { ZodError } from "zod";
|
|
2
|
+
import { HTTP_STATUS, ERROR_MESSAGES } from "../config/constants.ts";
|
|
3
|
+
import { logger } from "../utils/logger.ts";
|
|
4
|
+
import {
|
|
5
|
+
AppError,
|
|
6
|
+
ValidationError,
|
|
7
|
+
InternalServerError,
|
|
8
|
+
AuthenticationError,
|
|
9
|
+
AuthorizationError,
|
|
10
|
+
NotFoundError,
|
|
11
|
+
ConflictError,
|
|
12
|
+
BadRequestError,
|
|
13
|
+
} from "../utils/AppError.ts";
|
|
14
|
+
import { env } from "../config/env.ts";
|
|
15
|
+
import { Request, Response, NextFunction } from "express";
|
|
16
|
+
|
|
17
|
+
// Custom type for async route handlers
|
|
18
|
+
type AsyncRequestHandler = (
|
|
19
|
+
req: Request,
|
|
20
|
+
res: Response,
|
|
21
|
+
next: NextFunction,
|
|
22
|
+
) => Promise<any> | any;
|
|
23
|
+
|
|
24
|
+
const normalizeError = (err: unknown): AppError => {
|
|
25
|
+
if (err instanceof AppError) {
|
|
26
|
+
return err;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (err instanceof ZodError) {
|
|
30
|
+
const errors = err.errors.map((e) => ({
|
|
31
|
+
field: e.path.join("."),
|
|
32
|
+
message: e.message,
|
|
33
|
+
code: e.code,
|
|
34
|
+
}));
|
|
35
|
+
return new ValidationError(ERROR_MESSAGES.VALIDATION_ERROR, errors);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (err instanceof Error) {
|
|
39
|
+
if (err instanceof SyntaxError) {
|
|
40
|
+
return new InternalServerError("Syntax error in application code", err, {
|
|
41
|
+
type: "SyntaxError",
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (err instanceof TypeError) {
|
|
46
|
+
return new InternalServerError("Type error in application", err, {
|
|
47
|
+
type: "TypeError",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (err instanceof ReferenceError) {
|
|
52
|
+
return new InternalServerError("Reference error in application", err, {
|
|
53
|
+
type: "ReferenceError",
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (err instanceof RangeError) {
|
|
58
|
+
return new InternalServerError("Range error in application", err, {
|
|
59
|
+
type: "RangeError",
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return new InternalServerError(
|
|
64
|
+
err.message || ERROR_MESSAGES.SERVER_ERROR,
|
|
65
|
+
err,
|
|
66
|
+
{ type: "UnknownError" },
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const errorMessage =
|
|
71
|
+
typeof err === "string"
|
|
72
|
+
? err
|
|
73
|
+
: err &&
|
|
74
|
+
typeof err === "object" &&
|
|
75
|
+
"message" in err &&
|
|
76
|
+
typeof err.message === "string"
|
|
77
|
+
? err.message
|
|
78
|
+
: ERROR_MESSAGES.SERVER_ERROR;
|
|
79
|
+
|
|
80
|
+
return new InternalServerError(
|
|
81
|
+
errorMessage,
|
|
82
|
+
err instanceof Error ? err : new Error(String(err)),
|
|
83
|
+
{ type: "UnknownError" },
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const logError = (appError: AppError, req: Request): void => {
|
|
88
|
+
const errorDetails = {
|
|
89
|
+
type: appError.isOperational ? "OPERATIONAL" : "PROGRAMMER",
|
|
90
|
+
code: appError.code,
|
|
91
|
+
message: appError.message,
|
|
92
|
+
statusCode: appError.statusCode,
|
|
93
|
+
method: req.method,
|
|
94
|
+
path: req.path,
|
|
95
|
+
query: req.query,
|
|
96
|
+
ip: req.ip,
|
|
97
|
+
userAgent: req.get("user-agent"),
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
if (appError.isOperational) {
|
|
101
|
+
logger.warn(`Operational Error: ${appError.code}`, errorDetails);
|
|
102
|
+
} else {
|
|
103
|
+
logger.error(
|
|
104
|
+
`Programmer Error: ${appError.code}`,
|
|
105
|
+
errorDetails,
|
|
106
|
+
appError.stack,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (
|
|
111
|
+
appError instanceof ValidationError &&
|
|
112
|
+
(appError as ValidationError).errors
|
|
113
|
+
) {
|
|
114
|
+
const validationError = appError as ValidationError;
|
|
115
|
+
logger.debug("Validation errors", { errors: validationError.errors });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (appError.cause) {
|
|
119
|
+
const causeMessage =
|
|
120
|
+
appError.cause instanceof Error
|
|
121
|
+
? appError.cause.message
|
|
122
|
+
: String(appError.cause);
|
|
123
|
+
logger.debug("Error cause", { cause: causeMessage });
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const sendErrorResponse = (res: Response, appError: AppError): void => {
|
|
128
|
+
const statusCode = appError.statusCode || HTTP_STATUS.INTERNAL_SERVER_ERROR;
|
|
129
|
+
|
|
130
|
+
if (!appError.isOperational && env.isProduction) {
|
|
131
|
+
res.status(statusCode).json({
|
|
132
|
+
success: false,
|
|
133
|
+
message: ERROR_MESSAGES.SERVER_ERROR,
|
|
134
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
135
|
+
timestamp: new Date().toISOString(),
|
|
136
|
+
});
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const response =
|
|
141
|
+
(appError as any).toJSON && typeof (appError as any).toJSON === "function"
|
|
142
|
+
? (appError as any).toJSON()
|
|
143
|
+
: {
|
|
144
|
+
success: false,
|
|
145
|
+
message: appError.message,
|
|
146
|
+
code: appError.code,
|
|
147
|
+
statusCode,
|
|
148
|
+
timestamp: new Date().toISOString(),
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
res.status(statusCode).json(response);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Global Error Handler Middleware
|
|
156
|
+
*
|
|
157
|
+
* This middleware catches all errors and provides a unified way to handle them.
|
|
158
|
+
* MUST be the last middleware in the app.
|
|
159
|
+
*
|
|
160
|
+
* Features:
|
|
161
|
+
* - Distinguishes between operational and programmer errors
|
|
162
|
+
* - Logs errors with full context
|
|
163
|
+
* - Hides sensitive info in production
|
|
164
|
+
* - Formats JSON responses consistently
|
|
165
|
+
*/
|
|
166
|
+
export const errorHandler = (
|
|
167
|
+
err: unknown,
|
|
168
|
+
req: Request,
|
|
169
|
+
res: Response,
|
|
170
|
+
next: NextFunction,
|
|
171
|
+
): void => {
|
|
172
|
+
const appError = normalizeError(err);
|
|
173
|
+
|
|
174
|
+
logError(appError, req);
|
|
175
|
+
|
|
176
|
+
sendErrorResponse(res, appError);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Async error wrapper
|
|
181
|
+
* Wrap async route handlers to catch errors and pass to middleware
|
|
182
|
+
*
|
|
183
|
+
* Example:
|
|
184
|
+
* router.get('/users/:id', asyncHandler(getUserHandler))
|
|
185
|
+
*/
|
|
186
|
+
export const asyncHandler = (fn: AsyncRequestHandler) => {
|
|
187
|
+
return (req: Request, res: Response, next: NextFunction): void => {
|
|
188
|
+
Promise.resolve(fn(req, res, next)).catch(next);
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
export {
|
|
193
|
+
AppError,
|
|
194
|
+
ValidationError,
|
|
195
|
+
InternalServerError,
|
|
196
|
+
AuthenticationError,
|
|
197
|
+
AuthorizationError,
|
|
198
|
+
NotFoundError,
|
|
199
|
+
ConflictError,
|
|
200
|
+
BadRequestError,
|
|
201
|
+
};
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
import { Request, Response, NextFunction } from "express";
|
|
2
|
-
import { logger } from "../utils/logger.ts";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Request logging middleware
|
|
6
|
-
* Logs all HTTP requests with method, path, status, duration, and IP
|
|
7
|
-
*/
|
|
8
|
-
export const requestLogger = (
|
|
9
|
-
req: Request,
|
|
10
|
-
res: Response,
|
|
11
|
-
next: NextFunction,
|
|
12
|
-
): void => {
|
|
13
|
-
const start = Date.now();
|
|
14
|
-
|
|
15
|
-
res.on("finish", () => {
|
|
16
|
-
const duration = Date.now() - start;
|
|
17
|
-
const statusCode = res.statusCode;
|
|
18
|
-
const isError = statusCode >= 400;
|
|
19
|
-
|
|
20
|
-
const logData = {
|
|
21
|
-
method: req.method,
|
|
22
|
-
path: req.path,
|
|
23
|
-
statusCode,
|
|
24
|
-
durationMs: duration,
|
|
25
|
-
ip: req.ip,
|
|
26
|
-
userAgent: req.get("user-agent") || undefined,
|
|
27
|
-
...(isError && { error: true }),
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
if (isError) {
|
|
31
|
-
logger.warn(`${req.method} ${req.path}`, logData);
|
|
32
|
-
} else {
|
|
33
|
-
logger.debug(`${req.method} ${req.path}`, logData);
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
next();
|
|
38
|
-
};
|
|
1
|
+
import { Request, Response, NextFunction } from "express";
|
|
2
|
+
import { logger } from "../utils/logger.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Request logging middleware
|
|
6
|
+
* Logs all HTTP requests with method, path, status, duration, and IP
|
|
7
|
+
*/
|
|
8
|
+
export const requestLogger = (
|
|
9
|
+
req: Request,
|
|
10
|
+
res: Response,
|
|
11
|
+
next: NextFunction,
|
|
12
|
+
): void => {
|
|
13
|
+
const start = Date.now();
|
|
14
|
+
|
|
15
|
+
res.on("finish", () => {
|
|
16
|
+
const duration = Date.now() - start;
|
|
17
|
+
const statusCode = res.statusCode;
|
|
18
|
+
const isError = statusCode >= 400;
|
|
19
|
+
|
|
20
|
+
const logData = {
|
|
21
|
+
method: req.method,
|
|
22
|
+
path: req.path,
|
|
23
|
+
statusCode,
|
|
24
|
+
durationMs: duration,
|
|
25
|
+
ip: req.ip,
|
|
26
|
+
userAgent: req.get("user-agent") || undefined,
|
|
27
|
+
...(isError && { error: true }),
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
if (isError) {
|
|
31
|
+
logger.warn(`${req.method} ${req.path}`, logData);
|
|
32
|
+
} else {
|
|
33
|
+
logger.debug(`${req.method} ${req.path}`, logData);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
next();
|
|
38
|
+
};
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
import { Request, Response, NextFunction } from "express";
|
|
2
|
-
import { z, ZodError } from "zod";
|
|
3
|
-
import { ValidationError } from "../utils/AppError.ts";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Request validation middleware
|
|
7
|
-
*
|
|
8
|
-
* Validates request body, query, and params against a Zod schema
|
|
9
|
-
* Throws ValidationError if validation fails
|
|
10
|
-
*
|
|
11
|
-
* Example:
|
|
12
|
-
* const schema = z.object({
|
|
13
|
-
* body: z.object({ name: z.string() }),
|
|
14
|
-
* query: z.object({ page: z.coerce.number().optional() }),
|
|
15
|
-
* });
|
|
16
|
-
*
|
|
17
|
-
* router.post('/items', validateRequest(schema), handler)
|
|
18
|
-
*/
|
|
19
|
-
export const validateRequest = (schema: z.AnyZodObject) => {
|
|
20
|
-
return async (
|
|
21
|
-
req: Request,
|
|
22
|
-
res: Response,
|
|
23
|
-
next: NextFunction,
|
|
24
|
-
): Promise<void> => {
|
|
25
|
-
try {
|
|
26
|
-
const validated = await schema.parseAsync({
|
|
27
|
-
body: req.body,
|
|
28
|
-
query: req.query,
|
|
29
|
-
params: req.params,
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
req.validatedData = validated;
|
|
33
|
-
next();
|
|
34
|
-
} catch (error) {
|
|
35
|
-
if (error instanceof ZodError) {
|
|
36
|
-
const errors = error.errors.map((e) => ({
|
|
37
|
-
field: e.path.join("."),
|
|
38
|
-
message: e.message,
|
|
39
|
-
code: e.code,
|
|
40
|
-
}));
|
|
41
|
-
throw new ValidationError("Request validation failed", errors);
|
|
42
|
-
}
|
|
43
|
-
next(error);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
};
|
|
1
|
+
import { Request, Response, NextFunction } from "express";
|
|
2
|
+
import { z, ZodError } from "zod";
|
|
3
|
+
import { ValidationError } from "../utils/AppError.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Request validation middleware
|
|
7
|
+
*
|
|
8
|
+
* Validates request body, query, and params against a Zod schema
|
|
9
|
+
* Throws ValidationError if validation fails
|
|
10
|
+
*
|
|
11
|
+
* Example:
|
|
12
|
+
* const schema = z.object({
|
|
13
|
+
* body: z.object({ name: z.string() }),
|
|
14
|
+
* query: z.object({ page: z.coerce.number().optional() }),
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* router.post('/items', validateRequest(schema), handler)
|
|
18
|
+
*/
|
|
19
|
+
export const validateRequest = (schema: z.AnyZodObject) => {
|
|
20
|
+
return async (
|
|
21
|
+
req: Request,
|
|
22
|
+
res: Response,
|
|
23
|
+
next: NextFunction,
|
|
24
|
+
): Promise<void> => {
|
|
25
|
+
try {
|
|
26
|
+
const validated = await schema.parseAsync({
|
|
27
|
+
body: req.body,
|
|
28
|
+
query: req.query,
|
|
29
|
+
params: req.params,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
req.validatedData = validated;
|
|
33
|
+
next();
|
|
34
|
+
} catch (error) {
|
|
35
|
+
if (error instanceof ZodError) {
|
|
36
|
+
const errors = error.errors.map((e) => ({
|
|
37
|
+
field: e.path.join("."),
|
|
38
|
+
message: e.message,
|
|
39
|
+
code: e.code,
|
|
40
|
+
}));
|
|
41
|
+
throw new ValidationError("Request validation failed", errors);
|
|
42
|
+
}
|
|
43
|
+
next(error);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export const USER_ROLES = ["user", "admin"] as const;
|
|
2
|
-
|
|
3
|
-
export const AUTH_PROVIDERS = ["credentials"] as const;
|
|
4
|
-
|
|
5
|
-
export type UserRole = (typeof USER_ROLES)[number];
|
|
6
|
-
export type AuthProvider = (typeof AUTH_PROVIDERS)[number];
|
|
1
|
+
export const USER_ROLES = ["user", "admin"] as const;
|
|
2
|
+
|
|
3
|
+
export const AUTH_PROVIDERS = ["credentials"] as const;
|
|
4
|
+
|
|
5
|
+
export type UserRole = (typeof USER_ROLES)[number];
|
|
6
|
+
export type AuthProvider = (typeof AUTH_PROVIDERS)[number];
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { AuthService } from "./auth.service.ts";
|
|
2
|
-
import { Request, Response } from "express";
|
|
3
|
-
|
|
4
|
-
export const AuthController = {
|
|
5
|
-
async register(req: Request, res: Response): Promise<void> {
|
|
6
|
-
try {
|
|
7
|
-
const user = await AuthService.register(
|
|
8
|
-
req.body,
|
|
9
|
-
req.app.locals.userRepo,
|
|
10
|
-
);
|
|
11
|
-
|
|
12
|
-
res.status(201).json({
|
|
13
|
-
message: "User registered successfully",
|
|
14
|
-
user,
|
|
15
|
-
});
|
|
16
|
-
} catch (err) {
|
|
17
|
-
const message = err instanceof Error ? err.message : "An error occurred";
|
|
18
|
-
res.status(400).json({ message });
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
async login(req: Request, res: Response): Promise<void> {
|
|
23
|
-
try {
|
|
24
|
-
const result = await AuthService.login(req.body, req.app.locals.userRepo);
|
|
25
|
-
|
|
26
|
-
res.json(result);
|
|
27
|
-
} catch (err) {
|
|
28
|
-
const message = err instanceof Error ? err.message : "An error occurred";
|
|
29
|
-
res.status(401).json({ message });
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
};
|
|
1
|
+
import { AuthService } from "./auth.service.ts";
|
|
2
|
+
import { Request, Response } from "express";
|
|
3
|
+
|
|
4
|
+
export const AuthController = {
|
|
5
|
+
async register(req: Request, res: Response): Promise<void> {
|
|
6
|
+
try {
|
|
7
|
+
const user = await AuthService.register(
|
|
8
|
+
req.body,
|
|
9
|
+
req.app.locals.userRepo,
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
res.status(201).json({
|
|
13
|
+
message: "User registered successfully",
|
|
14
|
+
user,
|
|
15
|
+
});
|
|
16
|
+
} catch (err) {
|
|
17
|
+
const message = err instanceof Error ? err.message : "An error occurred";
|
|
18
|
+
res.status(400).json({ message });
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
async login(req: Request, res: Response): Promise<void> {
|
|
23
|
+
try {
|
|
24
|
+
const result = await AuthService.login(req.body, req.app.locals.userRepo);
|
|
25
|
+
|
|
26
|
+
res.json(result);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
const message = err instanceof Error ? err.message : "An error occurred";
|
|
29
|
+
res.status(401).json({ message });
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
};
|