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.
Files changed (77) hide show
  1. package/.github/workflows/release.yml +26 -26
  2. package/CHANGELOG.md +301 -301
  3. package/LICENSE +21 -21
  4. package/README.md +357 -354
  5. package/bin/index.js +494 -444
  6. package/bin/lib/pkgManager.js +49 -49
  7. package/bin/lib/templateHandler.js +33 -33
  8. package/package.json +42 -27
  9. package/packages/swagger/BACKWARD_COMPATIBILITY.md +1 -1
  10. package/packages/swagger/CHANGELOG.md +1 -1
  11. package/packages/swagger/README.md +3 -3
  12. package/packages/swagger/package.json +57 -44
  13. package/packages/swagger/src/index.d.ts +126 -126
  14. package/packages/swagger/src/index.js +12 -12
  15. package/packages/swagger/src/setup.js +100 -100
  16. package/template/js/.env.example +16 -15
  17. package/template/js/README.md +982 -978
  18. package/template/js/basePackage.json +26 -26
  19. package/template/js/src/app.js +81 -81
  20. package/template/js/src/config/constants.js +20 -20
  21. package/template/js/src/config/env.js +26 -26
  22. package/template/js/src/config/swagger.config.js +15 -15
  23. package/template/js/src/lib/swagger/SWAGGER_GUIDE.md +3 -3
  24. package/template/js/src/middlewares/errorHandler.js +180 -180
  25. package/template/js/src/middlewares/requestLogger.js +33 -33
  26. package/template/js/src/middlewares/validateRequest.js +42 -42
  27. package/template/js/src/modules/auth/auth.constants.js +3 -3
  28. package/template/js/src/modules/auth/auth.controller.js +29 -29
  29. package/template/js/src/modules/auth/auth.middlewares.js +19 -19
  30. package/template/js/src/modules/auth/auth.routes.js +131 -131
  31. package/template/js/src/modules/auth/auth.schemas.js +60 -60
  32. package/template/js/src/modules/auth/auth.service.js +67 -67
  33. package/template/js/src/modules/auth/package.json +6 -6
  34. package/template/js/src/modules/health/controller.js +151 -151
  35. package/template/js/src/modules/swagger/package.json +5 -5
  36. package/template/js/src/repositories/user.repo.js +19 -19
  37. package/template/js/src/routes/index.js +25 -25
  38. package/template/js/src/routes/protected.js +57 -57
  39. package/template/js/src/server.js +38 -38
  40. package/template/js/src/utils/AppError.js +182 -182
  41. package/template/js/src/utils/logger.js +73 -73
  42. package/template/js/src/utils/response.js +51 -51
  43. package/template/ts/.env.example +16 -15
  44. package/template/ts/README.md +982 -978
  45. package/template/ts/basePackage.json +36 -36
  46. package/template/ts/build.js +46 -46
  47. package/template/ts/src/app.ts +71 -71
  48. package/template/ts/src/config/constants.ts +27 -27
  49. package/template/ts/src/config/env.ts +40 -40
  50. package/template/ts/src/config/swagger.config.ts +30 -30
  51. package/template/ts/src/lib/swagger/SWAGGER_GUIDE.md +2 -2
  52. package/template/ts/src/middlewares/errorHandler.ts +201 -201
  53. package/template/ts/src/middlewares/requestLogger.ts +38 -38
  54. package/template/ts/src/middlewares/validateRequest.ts +46 -46
  55. package/template/ts/src/modules/auth/auth.constants.ts +6 -6
  56. package/template/ts/src/modules/auth/auth.controller.ts +32 -32
  57. package/template/ts/src/modules/auth/auth.middlewares.ts +46 -46
  58. package/template/ts/src/modules/auth/auth.routes.ts +52 -52
  59. package/template/ts/src/modules/auth/auth.schemas.ts +73 -73
  60. package/template/ts/src/modules/auth/auth.service.ts +106 -106
  61. package/template/ts/src/modules/auth/package.json +10 -10
  62. package/template/ts/src/modules/health/controller.ts +80 -80
  63. package/template/ts/src/modules/swagger/package.json +5 -5
  64. package/template/ts/src/repositories/user.repo.ts +33 -33
  65. package/template/ts/src/routes/index.ts +24 -24
  66. package/template/ts/src/routes/protected.ts +46 -46
  67. package/template/ts/src/server.ts +41 -41
  68. package/template/ts/src/types/express.d.ts +9 -9
  69. package/template/ts/src/utils/AppError.ts +220 -220
  70. package/template/ts/src/utils/logger.ts +55 -55
  71. package/template/ts/src/utils/response.ts +100 -100
  72. package/template/ts/tsconfig.json +26 -26
  73. package/packages/swagger/package-lock.json +0 -1715
  74. package/tmpclaude-1049-cwd +0 -1
  75. package/tmpclaude-3e37-cwd +0 -1
  76. package/tmpclaude-4d73-cwd +0 -1
  77. 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
+ };