create-charcole 2.0.4 → 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/CHANGELOG.md +290 -14
- package/README.md +258 -312
- package/bin/index.js +392 -55
- package/bin/lib/pkgManager.js +8 -25
- package/bin/lib/templateHandler.js +5 -42
- package/create-charcole-2.1.0.tgz +0 -0
- package/package.json +2 -2
- package/packages/swagger/BACKWARD_COMPATIBILITY.md +145 -0
- package/packages/swagger/CHANGELOG.md +404 -0
- package/packages/swagger/README.md +578 -0
- package/packages/swagger/charcole-swagger-1.0.0.tgz +0 -0
- package/packages/swagger/package-lock.json +1715 -0
- package/packages/swagger/package.json +44 -0
- package/packages/swagger/src/helpers.js +427 -0
- package/packages/swagger/src/index.d.ts +126 -0
- package/packages/swagger/src/index.js +12 -0
- package/packages/swagger/src/setup.js +100 -0
- package/template/js/.env.example +8 -0
- package/template/js/README.md +128 -5
- package/template/js/basePackage.json +11 -13
- package/template/js/src/app.js +8 -2
- package/template/js/src/config/swagger.config.js +15 -0
- package/template/js/src/lib/swagger/SWAGGER_GUIDE.md +561 -0
- package/template/js/src/modules/auth/auth.constants.js +3 -0
- package/template/js/src/modules/auth/auth.controller.js +29 -0
- package/template/js/src/modules/auth/auth.middlewares.js +19 -0
- package/template/js/src/modules/auth/auth.routes.js +131 -0
- package/template/js/src/modules/auth/auth.schemas.js +60 -0
- package/template/js/src/modules/auth/auth.service.js +67 -0
- package/template/js/src/modules/auth/package.json +6 -0
- package/template/js/src/modules/health/controller.js +104 -3
- package/template/js/src/modules/swagger/charcole-swagger-1.0.0.tgz +0 -0
- package/template/js/src/modules/swagger/package.json +5 -0
- package/template/js/src/repositories/user.repo.js +19 -0
- package/template/js/src/routes/index.js +25 -0
- package/template/js/src/routes/protected.js +57 -0
- package/template/ts/.env.example +8 -0
- package/template/ts/README.md +128 -5
- package/template/ts/basePackage.json +19 -15
- package/template/ts/build.js +46 -0
- package/template/ts/src/app.ts +12 -7
- package/template/ts/src/config/swagger.config.ts +30 -0
- package/template/ts/src/lib/swagger/SWAGGER_GUIDE.md +561 -0
- package/template/ts/src/middlewares/errorHandler.ts +15 -23
- package/template/ts/src/middlewares/requestLogger.ts +1 -1
- package/template/ts/src/middlewares/validateRequest.ts +1 -1
- package/template/ts/src/modules/auth/auth.constants.ts +6 -0
- package/template/ts/src/modules/auth/auth.controller.ts +32 -0
- package/template/ts/src/modules/auth/auth.middlewares.ts +46 -0
- package/template/ts/src/modules/auth/auth.routes.ts +52 -0
- package/template/ts/src/modules/auth/auth.schemas.ts +73 -0
- package/template/ts/src/modules/auth/auth.service.ts +106 -0
- package/template/ts/src/modules/auth/package.json +10 -0
- package/template/ts/src/modules/health/controller.ts +61 -45
- package/template/ts/src/modules/swagger/charcole-swagger-1.0.0.tgz +0 -0
- package/template/ts/src/modules/swagger/package.json +5 -0
- package/template/ts/src/repositories/user.repo.ts +33 -0
- package/template/ts/src/routes/index.ts +24 -0
- package/template/ts/src/routes/protected.ts +46 -0
- package/template/ts/src/server.ts +3 -4
- package/template/ts/src/utils/logger.ts +1 -1
- package/template/ts/tsconfig.json +14 -7
- package/tmpclaude-1049-cwd +1 -0
- package/tmpclaude-3e37-cwd +1 -0
- package/tmpclaude-4d73-cwd +1 -0
- package/tmpclaude-8a8e-cwd +1 -0
- package/template/js/ARCHITECTURE_DIAGRAMS.md +0 -283
- package/template/js/CHECKLIST.md +0 -279
- package/template/js/COMPLETE.md +0 -405
- package/template/js/ERROR_HANDLING.md +0 -393
- package/template/js/IMPLEMENTATION.md +0 -368
- package/template/js/IMPLEMENTATION_COMPLETE.md +0 -363
- package/template/js/INDEX.md +0 -290
- package/template/js/QUICK_REFERENCE.md +0 -270
- package/template/js/package.json +0 -28
- package/template/js/src/routes.js +0 -17
- package/template/js/test-api.js +0 -100
- package/template/ts/ARCHITECTURE_DIAGRAMS.md +0 -283
- package/template/ts/CHECKLIST.md +0 -279
- package/template/ts/COMPLETE.md +0 -405
- package/template/ts/ERROR_HANDLING.md +0 -393
- package/template/ts/IMPLEMENTATION.md +0 -368
- package/template/ts/IMPLEMENTATION_COMPLETE.md +0 -363
- package/template/ts/INDEX.md +0 -290
- package/template/ts/QUICK_REFERENCE.md +0 -270
- package/template/ts/package.json +0 -32
- package/template/ts/src/app.js +0 -75
- package/template/ts/src/config/constants.js +0 -20
- package/template/ts/src/config/env.js +0 -26
- package/template/ts/src/middlewares/errorHandler.js +0 -180
- package/template/ts/src/middlewares/requestLogger.js +0 -33
- package/template/ts/src/middlewares/validateRequest.js +0 -42
- package/template/ts/src/modules/health/controller.js +0 -50
- package/template/ts/src/routes.js +0 -17
- package/template/ts/src/routes.ts +0 -16
- package/template/ts/src/server.js +0 -38
- package/template/ts/src/utils/AppError.js +0 -182
- package/template/ts/src/utils/logger.js +0 -73
- package/template/ts/src/utils/response.js +0 -51
- package/template/ts/test-api.js +0 -100
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import { AuthController } from "./auth.controller.js";
|
|
3
|
+
|
|
4
|
+
const router = Router();
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @swagger
|
|
8
|
+
* /api/auth/register:
|
|
9
|
+
* post:
|
|
10
|
+
* summary: Register a new user
|
|
11
|
+
* description: Create a new user account with email and password
|
|
12
|
+
* tags:
|
|
13
|
+
* - Authentication
|
|
14
|
+
* requestBody:
|
|
15
|
+
* required: true
|
|
16
|
+
* content:
|
|
17
|
+
* application/json:
|
|
18
|
+
* schema:
|
|
19
|
+
* type: object
|
|
20
|
+
* required:
|
|
21
|
+
* - email
|
|
22
|
+
* - password
|
|
23
|
+
* - name
|
|
24
|
+
* properties:
|
|
25
|
+
* email:
|
|
26
|
+
* type: string
|
|
27
|
+
* format: email
|
|
28
|
+
* example: user@example.com
|
|
29
|
+
* password:
|
|
30
|
+
* type: string
|
|
31
|
+
* format: password
|
|
32
|
+
* minLength: 8
|
|
33
|
+
* example: SecurePassword123
|
|
34
|
+
* name:
|
|
35
|
+
* type: string
|
|
36
|
+
* example: John Doe
|
|
37
|
+
* responses:
|
|
38
|
+
* 201:
|
|
39
|
+
* description: User registered successfully
|
|
40
|
+
* content:
|
|
41
|
+
* application/json:
|
|
42
|
+
* schema:
|
|
43
|
+
* type: object
|
|
44
|
+
* properties:
|
|
45
|
+
* success:
|
|
46
|
+
* type: boolean
|
|
47
|
+
* example: true
|
|
48
|
+
* message:
|
|
49
|
+
* type: string
|
|
50
|
+
* example: User registered successfully
|
|
51
|
+
* data:
|
|
52
|
+
* type: object
|
|
53
|
+
* properties:
|
|
54
|
+
* id:
|
|
55
|
+
* type: string
|
|
56
|
+
* example: user_123abc
|
|
57
|
+
* email:
|
|
58
|
+
* type: string
|
|
59
|
+
* example: user@example.com
|
|
60
|
+
* token:
|
|
61
|
+
* type: string
|
|
62
|
+
* example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
|
63
|
+
* 400:
|
|
64
|
+
* description: Validation error or user already exists
|
|
65
|
+
* 500:
|
|
66
|
+
* description: Internal server error
|
|
67
|
+
*/
|
|
68
|
+
router.post("/register", AuthController.register);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @swagger
|
|
72
|
+
* /api/auth/login:
|
|
73
|
+
* post:
|
|
74
|
+
* summary: Login user
|
|
75
|
+
* description: Authenticate user with email and password
|
|
76
|
+
* tags:
|
|
77
|
+
* - Authentication
|
|
78
|
+
* requestBody:
|
|
79
|
+
* required: true
|
|
80
|
+
* content:
|
|
81
|
+
* application/json:
|
|
82
|
+
* schema:
|
|
83
|
+
* type: object
|
|
84
|
+
* required:
|
|
85
|
+
* - email
|
|
86
|
+
* - password
|
|
87
|
+
* properties:
|
|
88
|
+
* email:
|
|
89
|
+
* type: string
|
|
90
|
+
* format: email
|
|
91
|
+
* example: user@example.com
|
|
92
|
+
* password:
|
|
93
|
+
* type: string
|
|
94
|
+
* format: password
|
|
95
|
+
* example: SecurePassword123
|
|
96
|
+
* responses:
|
|
97
|
+
* 200:
|
|
98
|
+
* description: Login successful
|
|
99
|
+
* content:
|
|
100
|
+
* application/json:
|
|
101
|
+
* schema:
|
|
102
|
+
* type: object
|
|
103
|
+
* properties:
|
|
104
|
+
* success:
|
|
105
|
+
* type: boolean
|
|
106
|
+
* example: true
|
|
107
|
+
* message:
|
|
108
|
+
* type: string
|
|
109
|
+
* example: Login successful
|
|
110
|
+
* data:
|
|
111
|
+
* type: object
|
|
112
|
+
* properties:
|
|
113
|
+
* id:
|
|
114
|
+
* type: string
|
|
115
|
+
* example: user_123abc
|
|
116
|
+
* email:
|
|
117
|
+
* type: string
|
|
118
|
+
* example: user@example.com
|
|
119
|
+
* token:
|
|
120
|
+
* type: string
|
|
121
|
+
* example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
|
122
|
+
* 401:
|
|
123
|
+
* description: Invalid credentials
|
|
124
|
+
* 400:
|
|
125
|
+
* description: Validation error
|
|
126
|
+
* 500:
|
|
127
|
+
* description: Internal server error
|
|
128
|
+
*/
|
|
129
|
+
router.post("/login", AuthController.login);
|
|
130
|
+
|
|
131
|
+
export default router;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { USER_ROLES, AUTH_PROVIDERS } from "./auth.constants.js";
|
|
3
|
+
|
|
4
|
+
export const emailSchema = z
|
|
5
|
+
.string()
|
|
6
|
+
.email("Invalid email address")
|
|
7
|
+
.toLowerCase();
|
|
8
|
+
|
|
9
|
+
export const passwordSchema = z
|
|
10
|
+
.string()
|
|
11
|
+
.min(8, "Password must be at least 8 characters")
|
|
12
|
+
.max(72, "Password too long");
|
|
13
|
+
|
|
14
|
+
export const userSchema = z.object({
|
|
15
|
+
id: z.string().uuid(),
|
|
16
|
+
email: emailSchema,
|
|
17
|
+
name: z.string().min(1).max(100),
|
|
18
|
+
role: z.enum(USER_ROLES).default("user"),
|
|
19
|
+
provider: z.enum(AUTH_PROVIDERS).default("credentials"),
|
|
20
|
+
|
|
21
|
+
passwordHash: z.string().optional(), // credentials only
|
|
22
|
+
isEmailVerified: z.boolean().default(false),
|
|
23
|
+
|
|
24
|
+
createdAt: z.date(),
|
|
25
|
+
updatedAt: z.date(),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export const registerSchema = z.object({
|
|
29
|
+
name: z.string().min(1, "Name is required"),
|
|
30
|
+
email: emailSchema,
|
|
31
|
+
password: passwordSchema,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export const loginSchema = z.object({
|
|
35
|
+
email: emailSchema,
|
|
36
|
+
password: z.string().min(1, "Password is required"),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export const jwtPayloadSchema = z.object({
|
|
40
|
+
sub: z.string().uuid(), // user id
|
|
41
|
+
email: emailSchema,
|
|
42
|
+
role: z.enum(USER_ROLES),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export const forgotPasswordSchema = z.object({
|
|
46
|
+
email: emailSchema,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export const resetPasswordSchema = z.object({
|
|
50
|
+
token: z.string().min(1),
|
|
51
|
+
newPassword: passwordSchema,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export const publicUserSchema = userSchema.omit({
|
|
55
|
+
passwordHash: true,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export const validate = (schema, data) => {
|
|
59
|
+
return schema.parse(data);
|
|
60
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import bcrypt from "bcryptjs";
|
|
2
|
+
import jwt from "jsonwebtoken";
|
|
3
|
+
import { registerSchema, loginSchema } from "./auth.schemas.js";
|
|
4
|
+
|
|
5
|
+
const SALT_ROUNDS = 10;
|
|
6
|
+
const JWT_EXPIRES_IN = "7d";
|
|
7
|
+
|
|
8
|
+
export const AuthService = {
|
|
9
|
+
async hashPassword(password) {
|
|
10
|
+
return bcrypt.hash(password, SALT_ROUNDS);
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
async comparePassword(password, hash) {
|
|
14
|
+
return bcrypt.compare(password, hash);
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
signToken(payload) {
|
|
18
|
+
return jwt.sign(payload, process.env.JWT_SECRET, {
|
|
19
|
+
expiresIn: JWT_EXPIRES_IN,
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
async register(data, userRepo) {
|
|
24
|
+
const input = registerSchema.parse(data);
|
|
25
|
+
|
|
26
|
+
const existingUser = await userRepo.findByEmail(input.email);
|
|
27
|
+
if (existingUser) {
|
|
28
|
+
throw new Error("Email already in use");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const passwordHash = await this.hashPassword(input.password);
|
|
32
|
+
|
|
33
|
+
const user = await userRepo.create({
|
|
34
|
+
email: input.email,
|
|
35
|
+
name: input.name,
|
|
36
|
+
passwordHash,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return user;
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
async login(data, userRepo) {
|
|
43
|
+
const input = loginSchema.parse(data);
|
|
44
|
+
|
|
45
|
+
const user = await userRepo.findByEmail(input.email);
|
|
46
|
+
if (!user || !user.passwordHash) {
|
|
47
|
+
throw new Error("Invalid credentials");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const isValid = await this.comparePassword(
|
|
51
|
+
input.password,
|
|
52
|
+
user.passwordHash,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
if (!isValid) {
|
|
56
|
+
throw new Error("Invalid credentials");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const token = this.signToken({
|
|
60
|
+
sub: user.id,
|
|
61
|
+
email: user.email,
|
|
62
|
+
role: user.role,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return { user, token };
|
|
66
|
+
},
|
|
67
|
+
};
|
|
@@ -3,8 +3,39 @@ import { sendSuccess } from "../../utils/response.js";
|
|
|
3
3
|
import { asyncHandler } from "../../middlewares/errorHandler.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* @swagger
|
|
7
|
+
* /api/health:
|
|
8
|
+
* get:
|
|
9
|
+
* summary: Health check endpoint
|
|
10
|
+
* description: Returns the health status of the API
|
|
11
|
+
* tags:
|
|
12
|
+
* - Health
|
|
13
|
+
* responses:
|
|
14
|
+
* 200:
|
|
15
|
+
* description: Service is healthy
|
|
16
|
+
* content:
|
|
17
|
+
* application/json:
|
|
18
|
+
* schema:
|
|
19
|
+
* type: object
|
|
20
|
+
* properties:
|
|
21
|
+
* success:
|
|
22
|
+
* type: boolean
|
|
23
|
+
* example: true
|
|
24
|
+
* message:
|
|
25
|
+
* type: string
|
|
26
|
+
* example: Service is healthy
|
|
27
|
+
* data:
|
|
28
|
+
* type: object
|
|
29
|
+
* properties:
|
|
30
|
+
* status:
|
|
31
|
+
* type: string
|
|
32
|
+
* example: healthy
|
|
33
|
+
* uptime:
|
|
34
|
+
* type: number
|
|
35
|
+
* example: 123.45
|
|
36
|
+
* timestamp:
|
|
37
|
+
* type: string
|
|
38
|
+
* format: date-time
|
|
8
39
|
*/
|
|
9
40
|
export const getHealth = asyncHandler(async (req, res) => {
|
|
10
41
|
sendSuccess(
|
|
@@ -30,8 +61,78 @@ export const createItemSchema = z.object({
|
|
|
30
61
|
}),
|
|
31
62
|
});
|
|
32
63
|
|
|
64
|
+
/**
|
|
65
|
+
* @swagger
|
|
66
|
+
* /api/items:
|
|
67
|
+
* post:
|
|
68
|
+
* summary: Create a new item
|
|
69
|
+
* description: Example endpoint demonstrating validation with Zod
|
|
70
|
+
* tags:
|
|
71
|
+
* - Items
|
|
72
|
+
* requestBody:
|
|
73
|
+
* required: true
|
|
74
|
+
* content:
|
|
75
|
+
* application/json:
|
|
76
|
+
* schema:
|
|
77
|
+
* type: object
|
|
78
|
+
* required:
|
|
79
|
+
* - name
|
|
80
|
+
* properties:
|
|
81
|
+
* name:
|
|
82
|
+
* type: string
|
|
83
|
+
* minLength: 1
|
|
84
|
+
* maxLength: 100
|
|
85
|
+
* example: My Item
|
|
86
|
+
* description:
|
|
87
|
+
* type: string
|
|
88
|
+
* example: This is an example item
|
|
89
|
+
* responses:
|
|
90
|
+
* 201:
|
|
91
|
+
* description: Item created successfully
|
|
92
|
+
* content:
|
|
93
|
+
* application/json:
|
|
94
|
+
* schema:
|
|
95
|
+
* type: object
|
|
96
|
+
* properties:
|
|
97
|
+
* success:
|
|
98
|
+
* type: boolean
|
|
99
|
+
* example: true
|
|
100
|
+
* message:
|
|
101
|
+
* type: string
|
|
102
|
+
* example: Item created successfully
|
|
103
|
+
* data:
|
|
104
|
+
* type: object
|
|
105
|
+
* properties:
|
|
106
|
+
* id:
|
|
107
|
+
* type: string
|
|
108
|
+
* example: abc123def
|
|
109
|
+
* name:
|
|
110
|
+
* type: string
|
|
111
|
+
* example: My Item
|
|
112
|
+
* description:
|
|
113
|
+
* type: string
|
|
114
|
+
* nullable: true
|
|
115
|
+
* example: This is an example item
|
|
116
|
+
* createdAt:
|
|
117
|
+
* type: string
|
|
118
|
+
* format: date-time
|
|
119
|
+
* 400:
|
|
120
|
+
* description: Validation error
|
|
121
|
+
* content:
|
|
122
|
+
* application/json:
|
|
123
|
+
* schema:
|
|
124
|
+
* type: object
|
|
125
|
+
* properties:
|
|
126
|
+
* success:
|
|
127
|
+
* type: boolean
|
|
128
|
+
* example: false
|
|
129
|
+
* message:
|
|
130
|
+
* type: string
|
|
131
|
+
* example: Validation failed
|
|
132
|
+
*/
|
|
33
133
|
export const createItem = asyncHandler(async (req, res) => {
|
|
34
|
-
const
|
|
134
|
+
const parsedData = createItemSchema.parse({ body: req.body });
|
|
135
|
+
const { name, description } = parsedData.body;
|
|
35
136
|
|
|
36
137
|
// Simulate some async work
|
|
37
138
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
Binary file
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
|
|
3
|
+
const users = [];
|
|
4
|
+
|
|
5
|
+
export const userRepo = {
|
|
6
|
+
async findByEmail(email) {
|
|
7
|
+
return users.find((u) => u.email === email);
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
async create(data) {
|
|
11
|
+
const user = {
|
|
12
|
+
id: randomUUID(),
|
|
13
|
+
role: "user",
|
|
14
|
+
...data,
|
|
15
|
+
};
|
|
16
|
+
users.push(user);
|
|
17
|
+
return user;
|
|
18
|
+
},
|
|
19
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import {
|
|
3
|
+
getHealth,
|
|
4
|
+
createItem,
|
|
5
|
+
createItemSchema,
|
|
6
|
+
} from "../modules/health/controller.js";
|
|
7
|
+
import { validateRequest } from "../middlewares/validateRequest.js";
|
|
8
|
+
import { requireAuth } from "../modules/auth/auth.middlewares.js";
|
|
9
|
+
import protectedRoutes from "./protected.js";
|
|
10
|
+
import authRoutes from "../modules/auth/auth.routes.js";
|
|
11
|
+
const router = Router();
|
|
12
|
+
|
|
13
|
+
// Health check
|
|
14
|
+
router.get("/health", getHealth);
|
|
15
|
+
|
|
16
|
+
// Example: Create item with validation
|
|
17
|
+
router.post("/items", validateRequest(createItemSchema), createItem);
|
|
18
|
+
|
|
19
|
+
// 🔐 Auth routes
|
|
20
|
+
router.use("/auth", authRoutes);
|
|
21
|
+
|
|
22
|
+
// 🔐 Protected routes (REQUIRED BEARER TOKEN FOR THEM)
|
|
23
|
+
router.use("/protected", protectedRoutes);
|
|
24
|
+
|
|
25
|
+
export default router;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import { requireAuth } from "../modules/auth/auth.middlewares.js";
|
|
3
|
+
|
|
4
|
+
const router = Router();
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @swagger
|
|
8
|
+
* /api/protected/me:
|
|
9
|
+
* get:
|
|
10
|
+
* summary: Get current user profile
|
|
11
|
+
* description: Returns the authenticated user's information
|
|
12
|
+
* tags:
|
|
13
|
+
* - Protected
|
|
14
|
+
* security:
|
|
15
|
+
* - bearerAuth: []
|
|
16
|
+
* responses:
|
|
17
|
+
* 200:
|
|
18
|
+
* description: User profile retrieved successfully
|
|
19
|
+
* content:
|
|
20
|
+
* application/json:
|
|
21
|
+
* schema:
|
|
22
|
+
* type: object
|
|
23
|
+
* properties:
|
|
24
|
+
* message:
|
|
25
|
+
* type: string
|
|
26
|
+
* example: You are authenticated
|
|
27
|
+
* user:
|
|
28
|
+
* type: object
|
|
29
|
+
* properties:
|
|
30
|
+
* id:
|
|
31
|
+
* type: string
|
|
32
|
+
* example: user_123abc
|
|
33
|
+
* email:
|
|
34
|
+
* type: string
|
|
35
|
+
* example: user@example.com
|
|
36
|
+
* 401:
|
|
37
|
+
* description: Unauthorized - Invalid or missing token
|
|
38
|
+
* content:
|
|
39
|
+
* application/json:
|
|
40
|
+
* schema:
|
|
41
|
+
* type: object
|
|
42
|
+
* properties:
|
|
43
|
+
* success:
|
|
44
|
+
* type: boolean
|
|
45
|
+
* example: false
|
|
46
|
+
* message:
|
|
47
|
+
* type: string
|
|
48
|
+
* example: Unauthorized
|
|
49
|
+
*/
|
|
50
|
+
router.get("/me", requireAuth, (req, res) => {
|
|
51
|
+
res.json({
|
|
52
|
+
message: "You are authenticated",
|
|
53
|
+
user: req.user,
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
export default router;
|
package/template/ts/.env.example
CHANGED
package/template/ts/README.md
CHANGED
|
@@ -8,11 +8,12 @@ Welcome! This guide will help you set up and start using the Charcole API framew
|
|
|
8
8
|
2. [Project Structure](#project-structure)
|
|
9
9
|
3. [Configuration](#configuration)
|
|
10
10
|
4. [Creating Your First Endpoint](#creating-your-first-endpoint)
|
|
11
|
-
5. [
|
|
12
|
-
6. [
|
|
13
|
-
7. [
|
|
14
|
-
8. [
|
|
15
|
-
9. [
|
|
11
|
+
5. [API Documentation with Swagger](#api-documentation-with-swagger)
|
|
12
|
+
6. [Error Handling](#error-handling)
|
|
13
|
+
7. [Validation](#validation)
|
|
14
|
+
8. [Logging](#logging)
|
|
15
|
+
9. [Running Your API](#running-your-api)
|
|
16
|
+
10. [Troubleshooting](#troubleshooting)
|
|
16
17
|
|
|
17
18
|
---
|
|
18
19
|
|
|
@@ -396,6 +397,128 @@ router.get("/users/:id", async (req, res) => {
|
|
|
396
397
|
|
|
397
398
|
---
|
|
398
399
|
|
|
400
|
+
## 📖 API Documentation with Swagger
|
|
401
|
+
|
|
402
|
+
Your API comes with **automatic interactive documentation** powered by Swagger UI!
|
|
403
|
+
|
|
404
|
+
### Accessing the Documentation
|
|
405
|
+
|
|
406
|
+
1. Start your server: `npm run dev`
|
|
407
|
+
2. Visit: **http://localhost:3000/api-docs**
|
|
408
|
+
|
|
409
|
+
You'll see all your APIs automatically documented and can test them directly from the browser!
|
|
410
|
+
|
|
411
|
+
### How It Works
|
|
412
|
+
|
|
413
|
+
All built-in APIs are already documented. When you create new endpoints, simply add JSDoc comments with `@swagger` annotations:
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
/**
|
|
417
|
+
* @swagger
|
|
418
|
+
* /api/users:
|
|
419
|
+
* get:
|
|
420
|
+
* summary: Get all users
|
|
421
|
+
* tags:
|
|
422
|
+
* - Users
|
|
423
|
+
* responses:
|
|
424
|
+
* 200:
|
|
425
|
+
* description: Success
|
|
426
|
+
*/
|
|
427
|
+
export const getAllUsers = asyncHandler(async (req: Request, res: Response) => {
|
|
428
|
+
// Your code here
|
|
429
|
+
});
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
That's it! Your new endpoint will automatically appear in Swagger UI.
|
|
433
|
+
|
|
434
|
+
### Quick Example: Documenting a POST Endpoint
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
/**
|
|
438
|
+
* @swagger
|
|
439
|
+
* /api/posts:
|
|
440
|
+
* post:
|
|
441
|
+
* summary: Create a new post
|
|
442
|
+
* tags:
|
|
443
|
+
* - Posts
|
|
444
|
+
* requestBody:
|
|
445
|
+
* required: true
|
|
446
|
+
* content:
|
|
447
|
+
* application/json:
|
|
448
|
+
* schema:
|
|
449
|
+
* type: object
|
|
450
|
+
* required:
|
|
451
|
+
* - title
|
|
452
|
+
* properties:
|
|
453
|
+
* title:
|
|
454
|
+
* type: string
|
|
455
|
+
* example: My First Post
|
|
456
|
+
* content:
|
|
457
|
+
* type: string
|
|
458
|
+
* example: This is the post content
|
|
459
|
+
* responses:
|
|
460
|
+
* 201:
|
|
461
|
+
* description: Post created successfully
|
|
462
|
+
* 400:
|
|
463
|
+
* description: Validation error
|
|
464
|
+
*/
|
|
465
|
+
export const createPost = asyncHandler(async (req: Request, res: Response) => {
|
|
466
|
+
// Your implementation
|
|
467
|
+
});
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### Protected Endpoints (with Authentication)
|
|
471
|
+
|
|
472
|
+
For endpoints that require authentication, add the `security` field:
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
/**
|
|
476
|
+
* @swagger
|
|
477
|
+
* /api/profile:
|
|
478
|
+
* get:
|
|
479
|
+
* summary: Get user profile
|
|
480
|
+
* tags:
|
|
481
|
+
* - Profile
|
|
482
|
+
* security:
|
|
483
|
+
* - bearerAuth: []
|
|
484
|
+
* responses:
|
|
485
|
+
* 200:
|
|
486
|
+
* description: Profile retrieved
|
|
487
|
+
* 401:
|
|
488
|
+
* description: Unauthorized
|
|
489
|
+
*/
|
|
490
|
+
router.get("/profile", requireAuth, getProfile);
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### 📘 Complete Guide
|
|
494
|
+
|
|
495
|
+
For comprehensive examples including:
|
|
496
|
+
|
|
497
|
+
- Path and query parameters
|
|
498
|
+
- File uploads
|
|
499
|
+
- Complex schemas
|
|
500
|
+
- Error responses
|
|
501
|
+
- CRUD operations
|
|
502
|
+
|
|
503
|
+
See the **[Complete Swagger Documentation Guide](src/lib/swagger/SWAGGER_GUIDE.md)**
|
|
504
|
+
|
|
505
|
+
### Testing APIs in Swagger UI
|
|
506
|
+
|
|
507
|
+
1. Open http://localhost:3000/api-docs
|
|
508
|
+
2. Click on any endpoint to expand it
|
|
509
|
+
3. Click "Try it out"
|
|
510
|
+
4. Fill in the parameters
|
|
511
|
+
5. Click "Execute"
|
|
512
|
+
6. See the response!
|
|
513
|
+
|
|
514
|
+
For protected endpoints:
|
|
515
|
+
|
|
516
|
+
1. Click the "Authorize" button at the top
|
|
517
|
+
2. Enter your JWT token
|
|
518
|
+
3. Now you can test protected endpoints
|
|
519
|
+
|
|
520
|
+
---
|
|
521
|
+
|
|
399
522
|
## ✔️ Validation
|
|
400
523
|
|
|
401
524
|
### Zod Schema Basics
|