kybernus 2.4.0 → 3.0.1

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 (137) 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 +14 -2
  128. package/templates/nestjs/clean/package.json.hbs +51 -41
  129. package/templates/nestjs/hexagonal/package.json.hbs +51 -41
  130. package/templates/nestjs/mvc/package.json.hbs +49 -39
  131. package/templates/nextjs/mvc/package.json.hbs +46 -36
  132. package/templates/nodejs-express/clean/package.json.hbs +69 -59
  133. package/templates/nodejs-express/hexagonal/package.json.hbs +69 -59
  134. package/templates/nodejs-express/mvc/package.json.hbs +67 -57
  135. /package/templates/nestjs/hexagonal/prisma/{schema.prisma → schema.prisma.hbs} +0 -0
  136. /package/templates/nodejs-express/clean/prisma/{schema.prisma → schema.prisma.hbs} +0 -0
  137. /package/templates/nodejs-express/hexagonal/prisma/{schema.prisma → schema.prisma.hbs} +0 -0
@@ -0,0 +1,81 @@
1
+ import { Injectable, UnauthorizedException, ConflictException, NotFoundException } from '@nestjs/common';
2
+ import { JwtService } from '@nestjs/jwt';
3
+ import * as bcrypt from 'bcryptjs';
4
+
5
+ // ==========================================
6
+ // 🚨 TODO: DATABASE INTEGRATION REQUIRED 🚨
7
+ // ==========================================
8
+ // This service currently uses an IN-MEMORY array to store users.
9
+ // You MUST replace "this.mockDb" logic with your actual ORM or Repository provider.
10
+ //
11
+ // EXAMPLE WITH PRISMA/TYPEORM:
12
+ // 1. Inject your repository in the constructor:
13
+ // constructor(private usersService: UsersService, private jwtService: JwtService) {}
14
+ // 2. Change register() to: await this.usersService.create(email, hashedPassword);
15
+ // 3. Change login() to: await this.usersService.findByEmail(email);
16
+ // ==========================================
17
+
18
+ @Injectable()
19
+ export class AuthService {
20
+ private mockDb = []; // 🚨 REPLACE THIS WITH REAL DB CALLS 🚨
21
+
22
+ constructor(private jwtService: JwtService) { }
23
+
24
+ async register(email: string, pass: string) {
25
+ // 🚨 TODO: Change this to checking your real database!
26
+ const existingUser = this.mockDb.find((u) => u.email === email);
27
+ if (existingUser) {
28
+ throw new ConflictException('User already exists');
29
+ }
30
+
31
+ const hashedPassword = await bcrypt.hash(pass, 12);
32
+
33
+ // 🚨 TODO: Change this to inserting into your real database!
34
+ const newUser = {
35
+ id: Math.random().toString(36).substring(7),
36
+ email,
37
+ password: hashedPassword,
38
+ };
39
+ this.mockDb.push(newUser);
40
+
41
+ const payload = { userId: newUser.id, email: newUser.email };
42
+ const { password, ...userResult } = newUser;
43
+
44
+ return {
45
+ user: userResult,
46
+ access_token: this.jwtService.sign(payload),
47
+ };
48
+ }
49
+
50
+ async login(email: string, pass: string) {
51
+ // 🚨 TODO: Change this to fetching from your real database!
52
+ const user = this.mockDb.find((u) => u.email === email);
53
+
54
+ if (!user) {
55
+ throw new UnauthorizedException('Invalid credentials');
56
+ }
57
+
58
+ const isMatch = await bcrypt.compare(pass, user.password);
59
+ if (!isMatch) {
60
+ throw new UnauthorizedException('Invalid credentials');
61
+ }
62
+
63
+ const payload = { userId: user.id, email: user.email };
64
+
65
+ return {
66
+ access_token: this.jwtService.sign(payload),
67
+ };
68
+ }
69
+
70
+ async getUserProfile(userId: string) {
71
+ // 🚨 TODO: Change this to fetching from your real database!
72
+ const user = this.mockDb.find((u) => u.id === userId);
73
+
74
+ if (!user) {
75
+ throw new NotFoundException('User not found');
76
+ }
77
+
78
+ const { password, ...result } = user;
79
+ return result;
80
+ }
81
+ }
@@ -0,0 +1,4 @@
1
+ export class LoginDto {
2
+ email!: string;
3
+ password!: string;
4
+ }
@@ -0,0 +1,4 @@
1
+ export class RegisterDto {
2
+ email!: string;
3
+ password!: string;
4
+ }
@@ -0,0 +1,17 @@
1
+ import { Injectable, UnauthorizedException } from '@nestjs/common';
2
+ import { AuthGuard } from '@nestjs/passport';
3
+
4
+ /**
5
+ * Guard that protects routes using JWT authentication.
6
+ *
7
+ * Usage: @UseGuards(JwtAuthGuard) on controller or route handler.
8
+ */
9
+ @Injectable()
10
+ export class JwtAuthGuard extends AuthGuard('jwt') {
11
+ handleRequest(err: any, user: any) {
12
+ if (err || !user) {
13
+ throw err || new UnauthorizedException('Missing or invalid token');
14
+ }
15
+ return user;
16
+ }
17
+ }
@@ -0,0 +1,24 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { PassportStrategy } from '@nestjs/passport';
3
+ import { ExtractJwt, Strategy } from 'passport-jwt';
4
+
5
+ export interface JwtPayload {
6
+ userId: string;
7
+ email: string;
8
+ }
9
+
10
+ @Injectable()
11
+ export class JwtStrategy extends PassportStrategy(Strategy) {
12
+ constructor() {
13
+ super({
14
+ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
15
+ ignoreExpiration: false,
16
+ secretOrKey: process.env.JWT_SECRET || 'change-me-in-production',
17
+ });
18
+ }
19
+
20
+ async validate(payload: JwtPayload) {
21
+ // Passport will automatically attach this to the Request object as `req.user`
22
+ return { userId: payload.userId, email: payload.email };
23
+ }
24
+ }
@@ -0,0 +1,97 @@
1
+ # 🔐 JWT Authentication Module — Next.js (App Router)
2
+
3
+ A complete, modular authentication flow was added to your Next.js project.
4
+
5
+ ## 📁 Files generated:
6
+
7
+ - `src/lib/auth/jwt.ts` — Utilities for signing and verifying JSON Web Tokens.
8
+ - `src/lib/auth/session.ts` — Utilities for reading the session cookie from Server Components.
9
+ - `src/app/api/auth/login/route.ts` — Login API Route. **🚨 ACTION REQUIRED HERE**
10
+ - `src/app/api/auth/register/route.ts` — Registration API Route. **🚨 ACTION REQUIRED HERE**
11
+ - `src/middleware.ts` — Edge Middleware that automatically intercepts page requests and redirects unauthenticated users.
12
+
13
+ ## 📦 1. Install Dependencies
14
+
15
+ You need to install the JWT and Hash libraries:
16
+
17
+ ```bash
18
+ npm install jsonwebtoken bcryptjs
19
+ npm install -D @types/jsonwebtoken @types/bcryptjs
20
+ ```
21
+
22
+ ## ⚙️ 2. Environment Variables
23
+
24
+ Add these to your `.env` or `.env.local` file at the root of your project:
25
+
26
+ ```
27
+ JWT_SECRET=your-super-secret-key-change-me
28
+ JWT_EXPIRES_IN=7d
29
+ ```
30
+
31
+ > 💡 **Tip:** Generate a strong random secret by running this in your terminal:
32
+ > `node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"`
33
+
34
+ ---
35
+
36
+ ## 🚨 3. MANDATORY ACTION: Connect to your Database
37
+
38
+ The generated `login/route.ts` and `register/route.ts` use an **IN-MEMORY MOCK DATABASE** by default so that they compile and run immediately.
39
+ You **MUST** replace this with your actual database queries (Prisma, Drizzle, MongoDB, etc).
40
+
41
+ **Open `src/app/api/auth/login/route.ts` and `src/app/api/auth/register/route.ts` and look for the `🚨 TODO` blocks.**
42
+
43
+ ### Example: How to connect `register` to Prisma:
44
+
45
+ ```typescript
46
+ // Inside src/app/api/auth/register/route.ts
47
+
48
+ import prisma from '@/lib/prisma'; // Provide your own PrismaClient
49
+
50
+ // Inside the POST function:
51
+ const existingUser = await prisma.user.findUnique({ where: { email } });
52
+ if (existingUser) return NextResponse.json({ error: 'User exists' }, { status: 409 });
53
+
54
+ const hashedPassword = await bcrypt.hash(password, 12);
55
+
56
+ const newUser = await prisma.user.create({
57
+ data: { email, password: hashedPassword },
58
+ });
59
+
60
+ // ... continuing with token generation ...
61
+ ```
62
+
63
+ ---
64
+
65
+ ## ⚡ 4. Consuming the Session in Server Components
66
+
67
+ You can easily know who the current user is from any Server Component, without making an additional network request:
68
+
69
+ ```tsx
70
+ // src/app/dashboard/page.tsx
71
+
72
+ import { getSession } from '@/lib/auth/session';
73
+ import { redirect } from 'next/navigation';
74
+
75
+ export default async function DashboardPage() {
76
+ const session = await getSession();
77
+
78
+ // The middleware already protects /dashboard, but this is how you read the data
79
+ if (!session) {
80
+ redirect('/login');
81
+ }
82
+
83
+ return (
84
+ <div>
85
+ <h1>Welcome back!</h1>
86
+ <p>Your ID: {session.userId}</p>
87
+ <p>Your Email: {session.email}</p>
88
+ </div>
89
+ );
90
+ }
91
+ ```
92
+
93
+ ## 🔒 5. Middleware Protection
94
+
95
+ Open `src/middleware.ts`. At the top, you will see `protectedRoutes`.
96
+ Any route added to that array (e.g. `/dashboard`, `/settings`) cannot be accessed unless a valid JWT cookie is present.
97
+ Unauthenticated users will automatically redirect to `/login`.
@@ -0,0 +1,21 @@
1
+ import jwt from 'jsonwebtoken';
2
+
3
+ const JWT_SECRET = process.env.JWT_SECRET || 'change-me-in-production';
4
+ const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '7d';
5
+
6
+ export interface JwtPayload {
7
+ userId: string;
8
+ email: string;
9
+ }
10
+
11
+ export function generateToken(payload: JwtPayload): string {
12
+ return jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
13
+ }
14
+
15
+ export function verifyToken(token: string): JwtPayload | null {
16
+ try {
17
+ return jwt.verify(token, JWT_SECRET) as JwtPayload;
18
+ } catch {
19
+ return null;
20
+ }
21
+ }
@@ -0,0 +1,37 @@
1
+ import { NextResponse } from 'next/server';
2
+ import type { NextRequest } from 'next/server';
3
+ import { verifyToken } from '@/lib/auth/jwt';
4
+
5
+ // Define the routes that require authentication
6
+ const protectedRoutes = ['/dashboard', '/api/protected'];
7
+ const authRoutes = ['/login', '/register'];
8
+
9
+ export function middleware(request: NextRequest) {
10
+ const { pathname } = request.nextUrl;
11
+
12
+ // Check if it's a protected route
13
+ const isProtectedRoute = protectedRoutes.some(route => pathname.startsWith(route));
14
+ const isAuthRoute = authRoutes.some(route => pathname.startsWith(route));
15
+
16
+ if (isProtectedRoute || isAuthRoute) {
17
+ const token = request.cookies.get('token')?.value;
18
+ const payload = token ? verifyToken(token) : null;
19
+
20
+ if (isProtectedRoute && !payload) {
21
+ // Redirect to login if trying to access a protected route without a valid token
22
+ return NextResponse.redirect(new URL('/login', request.url));
23
+ }
24
+
25
+ if (isAuthRoute && payload) {
26
+ // Redirect to dashboard if logged in and trying to access login/register
27
+ return NextResponse.redirect(new URL('/dashboard', request.url));
28
+ }
29
+ }
30
+
31
+ return NextResponse.next();
32
+ }
33
+
34
+ // Ensure the middleware runs on relevant paths
35
+ export const config = {
36
+ matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
37
+ };
@@ -0,0 +1,43 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { generateToken } from '@/lib/auth/jwt';
3
+ import { sessionCookieOptions } from '@/lib/auth/session';
4
+ import bcrypt from 'bcryptjs';
5
+
6
+ // ==========================================
7
+ // 🚨 TODO: DATABASE INTEGRATION REQUIRED 🚨
8
+ // ==========================================
9
+ const mockDb: any[] = []; // 🚨 REPLACE THIS WITH REAL DB CALLS (e.g. Prisma) 🚨
10
+
11
+ export async function POST(req: NextRequest) {
12
+ try {
13
+ const { email, password } = await req.json();
14
+
15
+ if (!email || !password) {
16
+ return NextResponse.json({ error: 'Email and password are required' }, { status: 400 });
17
+ }
18
+
19
+ // 🚨 TODO: Replace with real db call (e.g. prisma.user.findUnique)
20
+ const user = mockDb.find((u) => u.email === email);
21
+
22
+ if (!user) {
23
+ return NextResponse.json({ error: 'Invalid credentials' }, { status: 401 });
24
+ }
25
+
26
+ const isMatch = await bcrypt.compare(password, user.password);
27
+ if (!isMatch) {
28
+ return NextResponse.json({ error: 'Invalid credentials' }, { status: 401 });
29
+ }
30
+
31
+ const token = generateToken({ userId: user.id, email: user.email });
32
+
33
+ const response = NextResponse.json({ success: true });
34
+
35
+ // Set the cookie
36
+ response.cookies.set('token', token, sessionCookieOptions);
37
+
38
+ return response;
39
+ } catch (error) {
40
+ console.error('Login error:', error);
41
+ return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
42
+ }
43
+ }
@@ -0,0 +1,50 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { generateToken } from '@/lib/auth/jwt';
3
+ import { sessionCookieOptions } from '@/lib/auth/session';
4
+ import bcrypt from 'bcryptjs';
5
+
6
+ // ==========================================
7
+ // 🚨 TODO: DATABASE INTEGRATION REQUIRED 🚨
8
+ // ==========================================
9
+ const mockDb: any[] = []; // 🚨 REPLACE THIS WITH REAL DB CALLS (e.g. Prisma) 🚨
10
+
11
+ export async function POST(req: NextRequest) {
12
+ try {
13
+ const { email, password } = await req.json();
14
+
15
+ if (!email || !password) {
16
+ return NextResponse.json({ error: 'Email and password are required' }, { status: 400 });
17
+ }
18
+
19
+ // 🚨 TODO: Replace with real db call (e.g. prisma.user.findUnique)
20
+ const existingUser = mockDb.find((u) => u.email === email);
21
+ if (existingUser) {
22
+ return NextResponse.json({ error: 'User already exists' }, { status: 409 });
23
+ }
24
+
25
+ const hashedPassword = await bcrypt.hash(password, 12);
26
+
27
+ // 🚨 TODO: Replace with real db call (e.g. prisma.user.create)
28
+ const newUser = {
29
+ id: Math.random().toString(36).substring(7),
30
+ email,
31
+ password: hashedPassword,
32
+ };
33
+ mockDb.push(newUser);
34
+
35
+ const token = generateToken({ userId: newUser.id, email: newUser.email });
36
+
37
+ const response = NextResponse.json(
38
+ { user: { id: newUser.id, email: newUser.email } },
39
+ { status: 201 }
40
+ );
41
+
42
+ // Set the cookie
43
+ response.cookies.set('token', token, sessionCookieOptions);
44
+
45
+ return response;
46
+ } catch (error) {
47
+ console.error('Registration error:', error);
48
+ return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
49
+ }
50
+ }
@@ -0,0 +1,28 @@
1
+ import { cookies } from 'next/headers';
2
+ import { verifyToken, JwtPayload } from './jwt';
3
+
4
+ /**
5
+ * Get the current user from the session cookie.
6
+ * This can only be called from Server Components, Server Actions, or API Routes.
7
+ */
8
+ export async function getSession(): Promise<JwtPayload | null> {
9
+ const cookieStore = await cookies();
10
+ const token = cookieStore.get('token')?.value;
11
+
12
+ if (!token) {
13
+ return null;
14
+ }
15
+
16
+ return verifyToken(token);
17
+ }
18
+
19
+ /**
20
+ * Define your cookie configuration to be used during login/logout
21
+ */
22
+ export const sessionCookieOptions = {
23
+ httpOnly: true,
24
+ secure: process.env.NODE_ENV === 'production',
25
+ sameSite: 'lax' as const,
26
+ path: '/',
27
+ maxAge: 60 * 60 * 24 * 7, // 7 days (must match your JWT expiry conceptually)
28
+ };
@@ -0,0 +1,109 @@
1
+ # 🔐 JWT Authentication Module — Node.js Express
2
+
3
+ A complete, modular authentication flow was added to your project inside the `auth/` directory.
4
+
5
+ ## 📁 Files generated:
6
+
7
+ - `auth.routes.ts` — The Express Router defining `/login`, `/register`, and `/me`.
8
+ - `auth.controller.ts` — Handles HTTP Requests and Responses.
9
+ - `auth.service.ts` — Business logic (token generation, password hashing). **🚨 ACTION REQUIRED HERE**
10
+ - `auth.middleware.ts` — Middleware to protect secure routes.
11
+ - `jwt.config.ts` — JWT sign and verify utilities.
12
+
13
+ ## 📦 1. Install Dependencies
14
+
15
+ You need to install the JWT and Cryptography packages:
16
+
17
+ ```bash
18
+ npm install jsonwebtoken bcryptjs
19
+ npm install -D @types/jsonwebtoken @types/bcryptjs
20
+ ```
21
+
22
+ ## ⚙️ 2. Environment Variables
23
+
24
+ Add these to your `.env` file at the root of your project:
25
+
26
+ ```
27
+ JWT_SECRET=your-super-secret-key-change-me
28
+ JWT_EXPIRES_IN=7d
29
+ ```
30
+
31
+ > 💡 **Tip:** Generate a strong random secret by running this in your terminal:
32
+ > `node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"`
33
+
34
+ ---
35
+
36
+ ## 🚨 3. MANDATORY ACTION: Connect to your Database
37
+
38
+ The generated `auth.service.ts` uses an **IN-MEMORY MOCK DATABASE** by default so that it compiles and runs immediately.
39
+ You **MUST** replace this with your actual Database / ORM calls.
40
+
41
+ **Open `auth/auth.service.ts` and look for the `🚨 TODO` blocks.**
42
+
43
+ ### Example: How to connect it to Prisma:
44
+
45
+ ```typescript
46
+ // Inside auth.service.ts
47
+
48
+ import { PrismaClient } from '@prisma/client';
49
+ import bcrypt from 'bcryptjs';
50
+ import { generateToken } from './jwt.config';
51
+
52
+ const prisma = new PrismaClient();
53
+
54
+ export class AuthService {
55
+ async register(email: string, password: string) {
56
+ const existingUser = await prisma.user.findUnique({ where: { email } });
57
+ if (existingUser) throw new Error('User already exists');
58
+
59
+ const hashedPassword = await bcrypt.hash(password, 12);
60
+
61
+ const newUser = await prisma.user.create({
62
+ data: { email, password: hashedPassword },
63
+ });
64
+
65
+ const token = generateToken({ userId: newUser.id, email: newUser.email });
66
+ return { token };
67
+ }
68
+ }
69
+ ```
70
+
71
+ ---
72
+
73
+ ## ⚡ 4. Plug the Router into your App
74
+
75
+ Currently, the routes exist but your Express server doesn't know about them.
76
+ You must register the `auth.routes.ts` file in your main Express app entrypoint.
77
+
78
+ **Open your `src/app.ts` or `src/index.ts` and add:**
79
+
80
+ ```typescript
81
+ import express from 'express';
82
+ // 1. Import the router
83
+ import authRoutes from './auth/auth.routes';
84
+
85
+ const app = express();
86
+ app.use(express.json());
87
+
88
+ // 2. Register the router
89
+ app.use('/api/auth', authRoutes);
90
+
91
+ app.listen(3000, () => console.log('Server running!'));
92
+ ```
93
+
94
+ ## 🔒 5. How to protect other routes
95
+
96
+ You can secure any other route in your API by importing the `authMiddleware`:
97
+
98
+ ```typescript
99
+ import { authMiddleware } from './auth/auth.middleware';
100
+
101
+ // Any route using authMiddleware requires a valid 'Authorization: Bearer <token>' header
102
+ app.get('/api/admin/dashboard', authMiddleware, (req, res) => {
103
+ // You now have access to the logged-in user's payload!
104
+ console.log(req.user.userId);
105
+ console.log(req.user.email);
106
+
107
+ res.json({ secretData: "This is locked" });
108
+ });
109
+ ```
@@ -0,0 +1,59 @@
1
+ import { Request, Response } from 'express';
2
+ import { AuthService } from './auth.service';
3
+
4
+ const authService = new AuthService();
5
+
6
+ export class AuthController {
7
+
8
+ async register(req: Request, res: Response) {
9
+ try {
10
+ const { email, password } = req.body;
11
+
12
+ if (!email || !password) {
13
+ return res.status(400).json({ error: 'Email and password are required' });
14
+ }
15
+
16
+ const result = await authService.register(email, password);
17
+ return res.status(201).json(result);
18
+ } catch (error: any) {
19
+ if (error.message === 'User already exists') {
20
+ return res.status(409).json({ error: 'User already exists' });
21
+ }
22
+ return res.status(500).json({ error: 'Internal server error' });
23
+ }
24
+ }
25
+
26
+ async login(req: Request, res: Response) {
27
+ try {
28
+ const { email, password } = req.body;
29
+
30
+ if (!email || !password) {
31
+ return res.status(400).json({ error: 'Email and password are required' });
32
+ }
33
+
34
+ const result = await authService.login(email, password);
35
+ return res.status(200).json(result);
36
+ } catch (error: any) {
37
+ if (error.message === 'Invalid credentials') {
38
+ return res.status(401).json({ error: 'Invalid credentials' });
39
+ }
40
+ return res.status(500).json({ error: 'Internal server error' });
41
+ }
42
+ }
43
+
44
+ async getProfile(req: Request, res: Response) {
45
+ // req.user is populated by the auth.middleware.ts
46
+ const user = req.user;
47
+
48
+ if (!user) {
49
+ return res.status(401).json({ error: 'Not authenticated' });
50
+ }
51
+
52
+ try {
53
+ const profile = await authService.getUserProfile(user.userId);
54
+ return res.status(200).json(profile);
55
+ } catch (error) {
56
+ return res.status(404).json({ error: 'User not found' });
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,38 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import { verifyToken, JwtPayload } from './jwt.config';
3
+
4
+ /**
5
+ * Extends Express Request to include the authenticated user payload.
6
+ */
7
+ declare global {
8
+ namespace Express {
9
+ interface Request {
10
+ user?: JwtPayload;
11
+ }
12
+ }
13
+ }
14
+
15
+ /**
16
+ * Middleware that validates the JWT from the Authorization header.
17
+ *
18
+ * Usage:
19
+ * router.get('/protected-route', authMiddleware, controllerHandler);
20
+ */
21
+ export function authMiddleware(req: Request, res: Response, next: NextFunction): void {
22
+ const header = req.headers.authorization;
23
+
24
+ if (!header || !header.startsWith('Bearer ')) {
25
+ res.status(401).json({ error: 'Missing or invalid Authorization header' });
26
+ return;
27
+ }
28
+
29
+ const token = header.split(' ')[1];
30
+
31
+ try {
32
+ const payload = verifyToken(token);
33
+ req.user = payload;
34
+ next();
35
+ } catch {
36
+ res.status(401).json({ error: 'Invalid or expired token' });
37
+ }
38
+ }
@@ -0,0 +1,15 @@
1
+ import { Router } from 'express';
2
+ import { AuthController } from './auth.controller';
3
+ import { authMiddleware } from './auth.middleware';
4
+
5
+ const router = Router();
6
+ const authController = new AuthController();
7
+
8
+ // Public Routes
9
+ router.post('/register', authController.register);
10
+ router.post('/login', authController.login);
11
+
12
+ // Protected Routes (requires valid JWT)
13
+ router.get('/me', authMiddleware, authController.getProfile);
14
+
15
+ export default router;