kybernus 2.4.0 → 3.0.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.
Files changed (133) hide show
  1. package/README.md +15 -6
  2. package/add-features/auth/java-spring/AuthController.java +54 -0
  3. package/add-features/auth/java-spring/AuthService.java +85 -0
  4. package/add-features/auth/java-spring/INSTRUCTIONS.md +119 -0
  5. package/add-features/auth/java-spring/dto/LoginRequest.java +22 -0
  6. package/add-features/auth/java-spring/dto/RegisterRequest.java +22 -0
  7. package/add-features/auth/java-spring/security/JwtRequestFilter.java +45 -0
  8. package/add-features/auth/java-spring/security/JwtUtil.java +59 -0
  9. package/add-features/auth/java-spring/security/SecurityConfig.java +39 -0
  10. package/add-features/auth/nestjs/INSTRUCTIONS.md +112 -0
  11. package/add-features/auth/nestjs/auth.controller.ts +27 -0
  12. package/add-features/auth/nestjs/auth.module.ts +20 -0
  13. package/add-features/auth/nestjs/auth.service.ts +81 -0
  14. package/add-features/auth/nestjs/dto/login.dto.ts +4 -0
  15. package/add-features/auth/nestjs/dto/register.dto.ts +4 -0
  16. package/add-features/auth/nestjs/jwt-auth.guard.ts +17 -0
  17. package/add-features/auth/nestjs/jwt.strategy.ts +24 -0
  18. package/add-features/auth/nextjs/INSTRUCTIONS.md +97 -0
  19. package/add-features/auth/nextjs/jwt.ts +21 -0
  20. package/add-features/auth/nextjs/middleware.ts +37 -0
  21. package/add-features/auth/nextjs/routes/login.ts +43 -0
  22. package/add-features/auth/nextjs/routes/register.ts +50 -0
  23. package/add-features/auth/nextjs/session.ts +28 -0
  24. package/add-features/auth/nodejs-express/INSTRUCTIONS.md +109 -0
  25. package/add-features/auth/nodejs-express/auth.controller.ts +59 -0
  26. package/add-features/auth/nodejs-express/auth.middleware.ts +38 -0
  27. package/add-features/auth/nodejs-express/auth.routes.ts +15 -0
  28. package/add-features/auth/nodejs-express/auth.service.ts +73 -0
  29. package/add-features/auth/nodejs-express/jwt.config.ts +17 -0
  30. package/add-features/auth/python-fastapi/INSTRUCTIONS.md +100 -0
  31. package/add-features/auth/python-fastapi/router.py +26 -0
  32. package/add-features/auth/python-fastapi/schemas.py +25 -0
  33. package/add-features/auth/python-fastapi/security.py +37 -0
  34. package/add-features/auth/python-fastapi/service.py +61 -0
  35. package/add-features/deploy/dockerfiles/Dockerfile.java +22 -0
  36. package/add-features/deploy/dockerfiles/Dockerfile.nextjs +32 -0
  37. package/add-features/deploy/dockerfiles/Dockerfile.nodejs +25 -0
  38. package/add-features/deploy/dockerfiles/Dockerfile.python +17 -0
  39. package/add-features/deploy/fly/INSTRUCTIONS.md +39 -0
  40. package/add-features/deploy/fly/java-spring.toml +21 -0
  41. package/add-features/deploy/fly/nextjs.toml +16 -0
  42. package/add-features/deploy/fly/nodejs.toml +21 -0
  43. package/add-features/deploy/fly/python-fastapi.toml +21 -0
  44. package/add-features/deploy/railway/INSTRUCTIONS.md +38 -0
  45. package/add-features/deploy/railway/java-spring.toml +16 -0
  46. package/add-features/deploy/railway/nextjs.toml +14 -0
  47. package/add-features/deploy/railway/nodejs.toml +14 -0
  48. package/add-features/deploy/railway/python-fastapi.toml +13 -0
  49. package/add-features/deploy/render/INSTRUCTIONS.md +35 -0
  50. package/add-features/deploy/render/java-spring.yaml +14 -0
  51. package/add-features/deploy/render/nextjs.yaml +17 -0
  52. package/add-features/deploy/render/nodejs.yaml +15 -0
  53. package/add-features/deploy/render/python-fastapi.yaml +13 -0
  54. package/add-features/deploy/vercel/INSTRUCTIONS.md +40 -0
  55. package/add-features/deploy/vercel/nextjs.json +16 -0
  56. package/add-features/deploy/vercel/nodejs-express.json +21 -0
  57. package/add-features/deploy/vercel/python-fastapi.json +21 -0
  58. package/add-features/husky/INSTRUCTIONS.md +52 -0
  59. package/add-features/husky/commit-msg +4 -0
  60. package/add-features/husky/commitlint.config.js +3 -0
  61. package/add-features/husky/pre-commit +4 -0
  62. package/add-features/redis/.env.snippet +1 -0
  63. package/add-features/redis/INSTRUCTIONS.md +64 -0
  64. package/add-features/redis/docker-compose.snippet.yml +18 -0
  65. package/add-features/redis/java-spring.java +27 -0
  66. package/add-features/redis/nextjs.ts +23 -0
  67. package/add-features/redis/nodejs.ts +14 -0
  68. package/add-features/redis/python.py +22 -0
  69. package/add-features/swagger/INSTRUCTIONS.md +53 -0
  70. package/add-features/swagger/java-spring.java +34 -0
  71. package/add-features/swagger/nestjs.ts +16 -0
  72. package/add-features/swagger/nextjs-route.ts +11 -0
  73. package/add-features/swagger/nextjs-swagger.ts +17 -0
  74. package/add-features/swagger/nodejs-express.ts +30 -0
  75. package/add-features/swagger/python-fastapi.py +21 -0
  76. package/add-features/websocket/INSTRUCTIONS.md +63 -0
  77. package/add-features/websocket/java-spring.java +60 -0
  78. package/add-features/websocket/nestjs.ts +38 -0
  79. package/add-features/websocket/nodejs-express.ts +38 -0
  80. package/add-features/websocket/python-fastapi.py +41 -0
  81. package/dist/cli/commands/add.d.ts +11 -0
  82. package/dist/cli/commands/add.d.ts.map +1 -0
  83. package/dist/cli/commands/add.js +102 -0
  84. package/dist/cli/commands/add.js.map +1 -0
  85. package/dist/cli/commands/auth.d.ts +11 -0
  86. package/dist/cli/commands/auth.d.ts.map +1 -0
  87. package/dist/cli/commands/auth.js +71 -0
  88. package/dist/cli/commands/auth.js.map +1 -0
  89. package/dist/cli/commands/deploy.d.ts +10 -0
  90. package/dist/cli/commands/deploy.d.ts.map +1 -0
  91. package/dist/cli/commands/deploy.js +110 -0
  92. package/dist/cli/commands/deploy.js.map +1 -0
  93. package/dist/cli/commands/doctor.d.ts +3 -0
  94. package/dist/cli/commands/doctor.d.ts.map +1 -0
  95. package/dist/cli/commands/doctor.js +110 -0
  96. package/dist/cli/commands/doctor.js.map +1 -0
  97. package/dist/cli/commands/init.d.ts +1 -0
  98. package/dist/cli/commands/init.d.ts.map +1 -1
  99. package/dist/cli/commands/init.js +1 -0
  100. package/dist/cli/commands/init.js.map +1 -1
  101. package/dist/cli/prompts/wizard.d.ts +1 -0
  102. package/dist/cli/prompts/wizard.d.ts.map +1 -1
  103. package/dist/cli/prompts/wizard.js +23 -15
  104. package/dist/cli/prompts/wizard.js.map +1 -1
  105. package/dist/cli/utils/cli-helpers.d.ts +43 -0
  106. package/dist/cli/utils/cli-helpers.d.ts.map +1 -0
  107. package/dist/cli/utils/cli-helpers.js +107 -0
  108. package/dist/cli/utils/cli-helpers.js.map +1 -0
  109. package/dist/core/deploy/deploy-generator.d.ts +18 -0
  110. package/dist/core/deploy/deploy-generator.d.ts.map +1 -0
  111. package/dist/core/deploy/deploy-generator.js +155 -0
  112. package/dist/core/deploy/deploy-generator.js.map +1 -0
  113. package/dist/core/features/feature-generator.d.ts +26 -0
  114. package/dist/core/features/feature-generator.d.ts.map +1 -0
  115. package/dist/core/features/feature-generator.js +376 -0
  116. package/dist/core/features/feature-generator.js.map +1 -0
  117. package/dist/core/generator/project.d.ts.map +1 -1
  118. package/dist/core/generator/project.js +42 -2
  119. package/dist/core/generator/project.js.map +1 -1
  120. package/dist/core/templates/engine.d.ts.map +1 -1
  121. package/dist/core/templates/engine.js +4 -0
  122. package/dist/core/templates/engine.js.map +1 -1
  123. package/dist/index.js +9 -0
  124. package/dist/index.js.map +1 -1
  125. package/dist/models/config.d.ts +1 -0
  126. package/dist/models/config.d.ts.map +1 -1
  127. package/package.json +15 -3
  128. package/templates/nodejs-express/clean/package.json.hbs +4 -4
  129. package/templates/nodejs-express/hexagonal/package.json.hbs +4 -4
  130. package/templates/nodejs-express/mvc/package.json.hbs +4 -4
  131. /package/templates/nestjs/hexagonal/prisma/{schema.prisma → schema.prisma.hbs} +0 -0
  132. /package/templates/nodejs-express/clean/prisma/{schema.prisma → schema.prisma.hbs} +0 -0
  133. /package/templates/nodejs-express/hexagonal/prisma/{schema.prisma → schema.prisma.hbs} +0 -0
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
- # Kybernus CLI 🚀
1
+ # Kybernus CLI - 3.0.0 🚀
2
2
 
3
+ **Make sure you always have the most updated version**
3
4
  **The Ultimate Backend Scaffolding Tool for Modern Developers.**
4
5
 
5
6
  Build production-ready applications in minutes. Kybernus generates robust, scalable backend projects with industry best practices built-in.
6
7
 
7
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
- [![Discord](https://img.shields.io/discord/1234567890?color=5865F2&label=discord&logo=discord&logoColor=white)](https://discord.gg/u5ANEpAAhT)
9
9
 
10
10
  ---
11
11
 
@@ -37,14 +37,19 @@ All stacks and architectures are **100% FREE** and Open Source.
37
37
  | **Java Spring Boot** | MVC, Clean, Hexagonal |
38
38
  | **NestJS** | MVC, Clean, Hexagonal |
39
39
  | **Python FastAPI** | MVC, Clean, Hexagonal |
40
+ | **n8n Automation Engine** | Default, AI Assistant, CRM Tracker, System Monitor |
40
41
 
41
42
  ---
42
43
 
43
44
  ## Commands
44
45
 
45
- ```bash
46
- kybernus init # Generate a new project
47
- ```
46
+ | Command | Description | Standalone Support |
47
+ |:---|:---|:---:|
48
+ | `kybernus init` | Generate a new backend project from scratch | - |
49
+ | `kybernus add` | Add features (Redis, Swagger, Websocket, Husky - stack dependent) | ✅ |
50
+ | `kybernus auth` | Add complete JWT Authentication logic | ✅ |
51
+ | `kybernus deploy` | Generate deployment configs (Vercel, Railway, etc.) | ✅ |
52
+ | `kybernus doctor` | Check your environment for required tools | ✅ |
48
53
 
49
54
  ---
50
55
 
@@ -99,8 +104,12 @@ kybernus init
99
104
  ## What's Included
100
105
 
101
106
  ### 100% Free & Open Source
102
- - **All stacks** including NestJS and FastAPI
107
+ - **All backend stacks** including NestJS, FastAPI and Spring Boot
108
+ - **n8n Enterprise Automation Engine** with templates
103
109
  - **Clean & Hexagonal Architecture** patterns
110
+ - **Kybernus Add**: Inject features like Redis, Websocket and Swagger into any project
111
+ - **Kybernus Auth**: Complete Authentication boilerplate (MVC/Clean)
112
+ - **Kybernus Deploy**: Configs for Vercel, Railway, Fly.io and Render
104
113
  - **Docker & docker-compose** configurations
105
114
  - **CI/CD pipelines** (GitHub Actions)
106
115
  - **Terraform** infrastructure configs
@@ -0,0 +1,54 @@
1
+ package com.example.auth;
2
+
3
+ import org.springframework.http.HttpStatus;
4
+ import org.springframework.http.ResponseEntity;
5
+ import org.springframework.security.core.Authentication;
6
+ import org.springframework.web.bind.annotation.*;
7
+
8
+ import java.util.Map;
9
+
10
+ @RestController
11
+ @RequestMapping("/auth")
12
+ public class AuthController {
13
+
14
+ private final AuthService authService;
15
+
16
+ public AuthController(AuthService authService) {
17
+ this.authService = authService;
18
+ }
19
+
20
+ @PostMapping("/register")
21
+ public ResponseEntity<?> register(@RequestBody RegisterRequest request) {
22
+ try {
23
+ Map<String, Object> response = authService.register(request.getEmail(), request.getPassword());
24
+ return ResponseEntity.status(HttpStatus.CREATED).body(response);
25
+ } catch (IllegalArgumentException e) {
26
+ return ResponseEntity.status(HttpStatus.CONFLICT).body(Map.of("error", e.getMessage()));
27
+ }
28
+ }
29
+
30
+ @PostMapping("/login")
31
+ public ResponseEntity<?> login(@RequestBody LoginRequest request) {
32
+ try {
33
+ Map<String, String> response = authService.login(request.getEmail(), request.getPassword());
34
+ return ResponseEntity.ok(response);
35
+ } catch (IllegalArgumentException e) {
36
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("error", e.getMessage()));
37
+ }
38
+ }
39
+
40
+ @GetMapping("/me")
41
+ public ResponseEntity<?> getProfile(Authentication authentication) {
42
+ if (authentication == null) {
43
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("error", "Not authenticated"));
44
+ }
45
+
46
+ String userId = (String) authentication.getPrincipal();
47
+ try {
48
+ Map<String, Object> profile = authService.getUserProfile(userId);
49
+ return ResponseEntity.ok(profile);
50
+ } catch (IllegalArgumentException e) {
51
+ return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Map.of("error", e.getMessage()));
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,85 @@
1
+ package com.example.auth;
2
+
3
+ import com.example.security.JwtUtil;
4
+ import org.springframework.security.crypto.password.PasswordEncoder;
5
+ import org.springframework.stereotype.Service;
6
+
7
+ import java.util.*;
8
+
9
+ // ==========================================
10
+ // 🚨 TODO: DATABASE INTEGRATION REQUIRED 🚨
11
+ // ==========================================
12
+ // This service currently uses an IN-MEMORY list to store users.
13
+ // You MUST replace the "mockDb" logic below with your Spring Data JPA Repository.
14
+ //
15
+ // EXAMPLE WITH JPA:
16
+ // 1. Inject UserRepository:
17
+ // public AuthService(UserRepository userRepository, PasswordEncoder passwordEncoder, JwtUtil jwtUtil) { ... }
18
+ // 2. Register: userRepository.save(new User(email, passwordEncoder.encode(password)));
19
+ // 3. Login: userRepository.findByEmail(email).orElseThrow(...);
20
+ // ==========================================
21
+
22
+ @Service
23
+ public class AuthService {
24
+
25
+ private final PasswordEncoder passwordEncoder;
26
+ private final JwtUtil jwtUtil;
27
+
28
+ // 🚨 REPLACE THIS WITH REAL DB CALLS (e.g., UserRepository) 🚨
29
+ private final List<Map<String, String>> mockDb = new ArrayList<>();
30
+
31
+ public AuthService(PasswordEncoder passwordEncoder, JwtUtil jwtUtil) {
32
+ this.passwordEncoder = passwordEncoder;
33
+ this.jwtUtil = jwtUtil;
34
+ }
35
+
36
+ public Map<String, Object> register(String email, String password) {
37
+ // 🚨 TODO: Check real DB
38
+ boolean exists = mockDb.stream().anyMatch(u -> u.get("email").equals(email));
39
+ if (exists) {
40
+ throw new IllegalArgumentException("User already exists");
41
+ }
42
+
43
+ String hashedPassword = passwordEncoder.encode(password);
44
+ String userId = UUID.randomUUID().toString().substring(0, 8);
45
+
46
+ // 🚨 TODO: Insert into real DB
47
+ Map<String, String> newUser = new HashMap<>();
48
+ newUser.put("id", userId);
49
+ newUser.put("email", email);
50
+ newUser.put("password", hashedPassword);
51
+ mockDb.add(newUser);
52
+
53
+ String token = jwtUtil.generateToken(userId, email);
54
+
55
+ Map<String, Object> response = new HashMap<>();
56
+ response.put("user", Map.of("id", userId, "email", email));
57
+ response.put("accessToken", token);
58
+ return response;
59
+ }
60
+
61
+ public Map<String, String> login(String email, String password) {
62
+ // 🚨 TODO: Fetch from real DB
63
+ Map<String, String> user = mockDb.stream()
64
+ .filter(u -> u.get("email").equals(email))
65
+ .findFirst()
66
+ .orElseThrow(() -> new IllegalArgumentException("Invalid credentials"));
67
+
68
+ if (!passwordEncoder.matches(password, user.get("password"))) {
69
+ throw new IllegalArgumentException("Invalid credentials");
70
+ }
71
+
72
+ String token = jwtUtil.generateToken(user.get("id"), user.get("email"));
73
+ return Map.of("accessToken", token);
74
+ }
75
+
76
+ public Map<String, Object> getUserProfile(String userId) {
77
+ // 🚨 TODO: Fetch from real DB
78
+ Map<String, String> user = mockDb.stream()
79
+ .filter(u -> u.get("id").equals(userId))
80
+ .findFirst()
81
+ .orElseThrow(() -> new IllegalArgumentException("User not found"));
82
+
83
+ return Map.of("id", user.get("id"), "email", user.get("email"));
84
+ }
85
+ }
@@ -0,0 +1,119 @@
1
+ # 🔐 JWT Authentication Module — Java Spring Boot
2
+
3
+ A complete, modular authentication flow was added to your project inside the `com.example.auth` and `com.example.security` packages.
4
+
5
+ ## 📁 Files generated:
6
+
7
+ - `AuthController.java` — REST Controller exposing `/auth/login`, `/auth/register`, and `/auth/me`.
8
+ - `AuthService.java` — Business logic (token generation, password validation). **🚨 ACTION REQUIRED HERE**
9
+ - `dto/LoginRequest.java` and `dto/RegisterRequest.java` — Input validation objects.
10
+ - `security/SecurityConfig.java` — Configures the `SecurityFilterChain` to protect routes.
11
+ - `security/JwtUtil.java` and `security/JwtRequestFilter.java` — Token utility and filter.
12
+
13
+ ## 📦 1. Add Dependencies (`pom.xml` / `build.gradle`)
14
+
15
+ You need the `jjwt` library and `spring-boot-starter-security`:
16
+
17
+ ```xml
18
+ <dependency>
19
+ <groupId>io.jsonwebtoken</groupId>
20
+ <artifactId>jjwt-api</artifactId>
21
+ <version>0.12.6</version>
22
+ </dependency>
23
+ <dependency>
24
+ <groupId>io.jsonwebtoken</groupId>
25
+ <artifactId>jjwt-impl</artifactId>
26
+ <version>0.12.6</version>
27
+ <scope>runtime</scope>
28
+ </dependency>
29
+ <dependency>
30
+ <groupId>io.jsonwebtoken</groupId>
31
+ <artifactId>jjwt-jackson</artifactId>
32
+ <version>0.12.6</version>
33
+ <scope>runtime</scope>
34
+ </dependency>
35
+ <dependency>
36
+ <groupId>org.springframework.boot</groupId>
37
+ <artifactId>spring-boot-starter-security</artifactId>
38
+ </dependency>
39
+ ```
40
+
41
+ ## ⚙️ 2. Environment Variables
42
+
43
+ Add these to your `application.properties` or `application.yml`:
44
+
45
+ ```properties
46
+ jwt.secret=your-super-secret-key-change-me-must-be-at-least-32-chars
47
+ jwt.expiration=604800000
48
+ ```
49
+
50
+ ---
51
+
52
+ ## 🚨 3. MANDATORY ACTION: Connect to your Database
53
+
54
+ The generated `AuthService.java` uses an **IN-MEMORY MOCK DATABASE** by default so that the API compiles and runs immediately.
55
+ You **MUST** replace this with calls to your actual Database (e.g., Spring Data JPA `UserRepository`).
56
+
57
+ **Open `AuthService.java` and look for the `🚨 TODO` blocks.**
58
+
59
+ ### Example: How to connect it to JPA:
60
+
61
+ ```java
62
+ // Inside AuthService.java
63
+
64
+ @Service
65
+ public class AuthService {
66
+ private final UserRepository userRepository; // <-- Inject your real DB Repo
67
+ private final PasswordEncoder passwordEncoder;
68
+ private final JwtUtil jwtUtil;
69
+
70
+ // Remove the mockDb completely!
71
+
72
+ public AuthService(UserRepository userRepository, PasswordEncoder passwordEncoder, JwtUtil jwtUtil) {
73
+ this.userRepository = userRepository;
74
+ this.passwordEncoder = passwordEncoder;
75
+ this.jwtUtil = jwtUtil;
76
+ }
77
+
78
+ public Map<String, Object> register(String email, String password) {
79
+ if (userRepository.existsByEmail(email)) {
80
+ throw new IllegalArgumentException("User already exists");
81
+ }
82
+
83
+ User newUser = new User();
84
+ newUser.setEmail(email);
85
+ newUser.setPassword(passwordEncoder.encode(password));
86
+ userRepository.save(newUser);
87
+
88
+ String token = jwtUtil.generateToken(newUser.getId(), newUser.getEmail());
89
+ // Return response...
90
+ }
91
+ }
92
+ ```
93
+
94
+ ---
95
+
96
+ ## 🔒 4. How to protect other controllers
97
+
98
+ The `SecurityConfig.java` is already checking the JWT token.
99
+ Any new controller you create is **automatically protected**, except for the permitAll routes (`/auth/**` and `/swagger-ui/**`).
100
+
101
+ To access the logged-in user inside any Controller:
102
+
103
+ ```java
104
+ import org.springframework.security.core.Authentication;
105
+ import org.springframework.web.bind.annotation.GetMapping;
106
+ import org.springframework.web.bind.annotation.RestController;
107
+
108
+ @RestController
109
+ public class DashboardController {
110
+
111
+ @GetMapping("/api/dashboard")
112
+ public String dashboard(Authentication authentication) {
113
+ // authentication is non-null because the route is protected
114
+ String userId = (String) authentication.getPrincipal();
115
+
116
+ return "Welcome, user ID: " + userId;
117
+ }
118
+ }
119
+ ```
@@ -0,0 +1,22 @@
1
+ package com.example.auth;
2
+
3
+ public class LoginRequest {
4
+ private String email;
5
+ private String password;
6
+
7
+ public String getEmail() {
8
+ return email;
9
+ }
10
+
11
+ public void setEmail(String email) {
12
+ this.email = email;
13
+ }
14
+
15
+ public String getPassword() {
16
+ return password;
17
+ }
18
+
19
+ public void setPassword(String password) {
20
+ this.password = password;
21
+ }
22
+ }
@@ -0,0 +1,22 @@
1
+ package com.example.auth;
2
+
3
+ public class RegisterRequest {
4
+ private String email;
5
+ private String password;
6
+
7
+ public String getEmail() {
8
+ return email;
9
+ }
10
+
11
+ public void setEmail(String email) {
12
+ this.email = email;
13
+ }
14
+
15
+ public String getPassword() {
16
+ return password;
17
+ }
18
+
19
+ public void setPassword(String password) {
20
+ this.password = password;
21
+ }
22
+ }
@@ -0,0 +1,45 @@
1
+ package com.example.security;
2
+
3
+ import jakarta.servlet.FilterChain;
4
+ import jakarta.servlet.ServletException;
5
+ import jakarta.servlet.http.HttpServletRequest;
6
+ import jakarta.servlet.http.HttpServletResponse;
7
+ import org.springframework.beans.factory.annotation.Autowired;
8
+ import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
9
+ import org.springframework.security.core.context.SecurityContextHolder;
10
+ import org.springframework.stereotype.Component;
11
+ import org.springframework.web.filter.OncePerRequestFilter;
12
+
13
+ import java.io.IOException;
14
+ import java.util.Collections;
15
+
16
+ @Component
17
+ public class JwtRequestFilter extends OncePerRequestFilter {
18
+
19
+ @Autowired
20
+ private JwtUtil jwtUtil;
21
+
22
+ @Override
23
+ protected void doFilterInternal(
24
+ HttpServletRequest request,
25
+ HttpServletResponse response,
26
+ FilterChain filterChain) throws ServletException, IOException {
27
+ final String header = request.getHeader("Authorization");
28
+
29
+ if (header != null && header.startsWith("Bearer ")) {
30
+ String token = header.substring(7);
31
+ try {
32
+ String userId = jwtUtil.extractUserId(token);
33
+ if (userId != null && !jwtUtil.isTokenExpired(token)) {
34
+ UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(userId, null,
35
+ Collections.emptyList());
36
+ SecurityContextHolder.getContext().setAuthentication(auth);
37
+ }
38
+ } catch (Exception ignored) {
39
+ // Invalid token — continue without authentication (handled by SecurityConfig)
40
+ }
41
+ }
42
+
43
+ filterChain.doFilter(request, response);
44
+ }
45
+ }
@@ -0,0 +1,59 @@
1
+ package com.example.security;
2
+
3
+ import io.jsonwebtoken.Claims;
4
+ import io.jsonwebtoken.Jwts;
5
+ import io.jsonwebtoken.SignatureAlgorithm;
6
+ import io.jsonwebtoken.security.Keys;
7
+ import org.springframework.beans.factory.annotation.Value;
8
+ import org.springframework.stereotype.Component;
9
+
10
+ import javax.crypto.SecretKey;
11
+ import java.nio.charset.StandardCharsets;
12
+ import java.util.Date;
13
+ import java.util.HashMap;
14
+ import java.util.Map;
15
+
16
+ @Component
17
+ public class JwtUtil {
18
+
19
+ @Value("${jwt.secret:change-me-in-production}")
20
+ private String secret;
21
+
22
+ @Value("${jwt.expiration:604800000}") // 7 days in ms
23
+ private long expirationMs;
24
+
25
+ private SecretKey getSigningKey() {
26
+ // Ensure secret is long enough for HS256
27
+ String paddedSecret = secret.length() < 32 ? String.format("%-32s", secret).replace(' ', '0') : secret;
28
+ return Keys.hmacShaKeyFor(paddedSecret.getBytes(StandardCharsets.UTF_8));
29
+ }
30
+
31
+ public String generateToken(String userId, String email) {
32
+ Map<String, Object> claims = new HashMap<>();
33
+ claims.put("email", email);
34
+
35
+ return Jwts.builder()
36
+ .setClaims(claims)
37
+ .setSubject(userId)
38
+ .setIssuedAt(new Date())
39
+ .setExpiration(new Date(System.currentTimeMillis() + expirationMs))
40
+ .signWith(getSigningKey(), SignatureAlgorithm.HS256)
41
+ .compact();
42
+ }
43
+
44
+ public Claims extractClaims(String token) {
45
+ return Jwts.parserBuilder()
46
+ .setSigningKey(getSigningKey())
47
+ .build()
48
+ .parseClaimsJws(token)
49
+ .getBody();
50
+ }
51
+
52
+ public String extractUserId(String token) {
53
+ return extractClaims(token).getSubject();
54
+ }
55
+
56
+ public boolean isTokenExpired(String token) {
57
+ return extractClaims(token).getExpiration().before(new Date());
58
+ }
59
+ }
@@ -0,0 +1,39 @@
1
+ package com.example.security;
2
+
3
+ import org.springframework.context.annotation.Bean;
4
+ import org.springframework.context.annotation.Configuration;
5
+ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
6
+ import org.springframework.security.config.http.SessionCreationPolicy;
7
+ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
8
+ import org.springframework.security.crypto.password.PasswordEncoder;
9
+ import org.springframework.security.web.SecurityFilterChain;
10
+ import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
11
+
12
+ @Configuration
13
+ public class SecurityConfig {
14
+
15
+ private final JwtRequestFilter jwtRequestFilter;
16
+
17
+ public SecurityConfig(JwtRequestFilter jwtRequestFilter) {
18
+ this.jwtRequestFilter = jwtRequestFilter;
19
+ }
20
+
21
+ @Bean
22
+ public PasswordEncoder passwordEncoder() {
23
+ return new BCryptPasswordEncoder();
24
+ }
25
+
26
+ @Bean
27
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
28
+ http
29
+ .csrf(csrf -> csrf.disable())
30
+ .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
31
+ .authorizeHttpRequests(auth -> auth
32
+ .requestMatchers("/auth/login", "/auth/register").permitAll()
33
+ .requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
34
+ .anyRequest().authenticated())
35
+ .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
36
+
37
+ return http.build();
38
+ }
39
+ }
@@ -0,0 +1,112 @@
1
+ # 🔐 JWT Authentication Module — NestJS
2
+
3
+ A complete, modular authentication flow was added to your project inside the `auth/` directory.
4
+
5
+ ## 📁 Files generated:
6
+
7
+ - `auth.module.ts` — The NestJS Module combining Controllers, Services, and JWT configurations.
8
+ - `auth.controller.ts` — Exposes the `/auth/login`, `/auth/register`, and `/auth/me` endpoints.
9
+ - `auth.service.ts` — Business logic (token generation, password validation). **🚨 ACTION REQUIRED HERE**
10
+ - `jwt.strategy.ts` — Connects Passport to handle `Bearer Token` parsing automatically.
11
+ - `jwt-auth.guard.ts` — The `@UseGuards(JwtAuthGuard)` decorator mapped directly to JWT.
12
+ - `dto/*.dto.ts` — Inputs for validation.
13
+
14
+ ## 📦 1. Install Dependencies
15
+
16
+ You need to install Passport, JWT, and bcrypt:
17
+
18
+ ```bash
19
+ npm install @nestjs/passport @nestjs/jwt passport passport-jwt bcryptjs
20
+ npm install -D @types/passport-jwt @types/bcryptjs
21
+ ```
22
+
23
+ ## ⚙️ 2. Environment Variables
24
+
25
+ Add these to your `.env` file at the root of your project:
26
+
27
+ ```
28
+ JWT_SECRET=your-super-secret-key-change-me
29
+ JWT_EXPIRES_IN=7d
30
+ ```
31
+
32
+ > 💡 **Tip:** Generate a strong random secret by running this in your terminal:
33
+ > `node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"`
34
+
35
+ ---
36
+
37
+ ## 🚨 3. MANDATORY ACTION: Connect to your Database
38
+
39
+ The generated `auth.service.ts` uses an **IN-MEMORY MOCK DATABASE** by default so that it compiles and runs immediately.
40
+ You **MUST** replace this with calls to your actual Database (TypeORM, Prisma, Mongoose, etc).
41
+
42
+ **Open `auth/auth.service.ts` and look for the `🚨 TODO` blocks.**
43
+
44
+ ### Example: How to connect it to TypeORM:
45
+
46
+ ```typescript
47
+ // Inside auth.service.ts
48
+
49
+ @Injectable()
50
+ export class AuthService {
51
+ constructor(
52
+ @InjectRepository(User)
53
+ private usersRepository: Repository<User>, // <-- Inject real DB
54
+ private jwtService: JwtService
55
+ ) {}
56
+
57
+ async register(email: string, pass: string) {
58
+ const existingUser = await this.usersRepository.findOne({ where: { email } });
59
+ if (existingUser) throw new ConflictException('User already exists');
60
+
61
+ const hashedPassword = await bcrypt.hash(pass, 12);
62
+
63
+ const newUser = this.usersRepository.create({ email, password: hashedPassword });
64
+ await this.usersRepository.save(newUser);
65
+
66
+ const payload = { userId: newUser.id, email: newUser.email };
67
+ return { access_token: this.jwtService.sign(payload) };
68
+ }
69
+ }
70
+ ```
71
+
72
+ ---
73
+
74
+ ## ⚡ 4. Plug the Module into your App
75
+
76
+ Currently, the `AuthModule` exists, but your root App Module doesn't know about it.
77
+ You must register it in your `app.module.ts`.
78
+
79
+ **Open `src/app.module.ts` and add:**
80
+
81
+ ```typescript
82
+ import { Module } from '@nestjs/common';
83
+ import { AuthModule } from './auth/auth.module'; // <-- IMPORT HERE
84
+
85
+ @Module({
86
+ imports: [
87
+ AuthModule, // <-- ADD HERE
88
+ // ...other modules
89
+ ],
90
+ })
91
+ export class AppModule {}
92
+ ```
93
+
94
+ ## 🔒 5. How to protect other controllers
95
+
96
+ You can secure any other route by decorating it or its controller with `@UseGuards(JwtAuthGuard)`:
97
+
98
+ ```typescript
99
+ import { Controller, Get, UseGuards, Request } from '@nestjs/common';
100
+ import { JwtAuthGuard } from './auth/jwt-auth.guard';
101
+
102
+ @Controller('admin')
103
+ @UseGuards(JwtAuthGuard) // Protects all routes inside this controller
104
+ export class AdminController {
105
+
106
+ @Get('dashboard')
107
+ getDashboard(@Request() req) {
108
+ // You now have access to the logged-in user's payload!
109
+ return { secretData: `Welcome, user ${req.user.userId}` };
110
+ }
111
+ }
112
+ ```
@@ -0,0 +1,27 @@
1
+ import { Controller, Post, Get, Body, UseGuards, Request } from '@nestjs/common';
2
+ import { AuthService } from './auth.service';
3
+ import { JwtAuthGuard } from './jwt-auth.guard';
4
+ import { LoginDto } from './dto/login.dto';
5
+ import { RegisterDto } from './dto/register.dto';
6
+
7
+ @Controller('auth')
8
+ export class AuthController {
9
+ constructor(private readonly authService: AuthService) { }
10
+
11
+ @Post('register')
12
+ async register(@Body() registerDto: RegisterDto) {
13
+ return this.authService.register(registerDto.email, registerDto.password);
14
+ }
15
+
16
+ @Post('login')
17
+ async login(@Body() loginDto: LoginDto) {
18
+ return this.authService.login(loginDto.email, loginDto.password);
19
+ }
20
+
21
+ @UseGuards(JwtAuthGuard)
22
+ @Get('me')
23
+ getProfile(@Request() req) {
24
+ // req.user is extracted by the JwtStrategy from the Bearer token
25
+ return this.authService.getUserProfile(req.user.userId);
26
+ }
27
+ }
@@ -0,0 +1,20 @@
1
+ import { Module } from '@nestjs/common';
2
+ import { JwtModule } from '@nestjs/jwt';
3
+ import { PassportModule } from '@nestjs/passport';
4
+ import { AuthService } from './auth.service';
5
+ import { AuthController } from './auth.controller';
6
+ import { JwtStrategy } from './jwt.strategy';
7
+
8
+ @Module({
9
+ imports: [
10
+ PassportModule,
11
+ JwtModule.register({
12
+ secret: process.env.JWT_SECRET || 'change-me-in-production',
13
+ signOptions: { expiresIn: process.env.JWT_EXPIRES_IN || '7d' },
14
+ }),
15
+ ],
16
+ controllers: [AuthController],
17
+ providers: [AuthService, JwtStrategy],
18
+ exports: [AuthService],
19
+ })
20
+ export class AuthModule { }