kybernus 3.0.1 → 3.1.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 (106) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/templates/java-spring/clean/.gitignore.hbs +72 -0
  4. package/templates/java-spring/clean/docker-compose.yml.hbs +6 -3
  5. package/templates/java-spring/clean/src/main/java/{{packagePath}}/application/usecase/PaymentUseCase.java.hbs +21 -17
  6. package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/persistence/entity/UserEntity.java.hbs +52 -0
  7. package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/persistence/repository/JpaUserRepository.java.hbs +12 -0
  8. package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/security/JwtAuthenticationFilter.java.hbs +64 -0
  9. package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/security/SecurityConfig.java.hbs +36 -0
  10. package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/stripe/StripeGateway.java.hbs +63 -0
  11. package/templates/java-spring/clean/src/main/resources/application.properties.hbs +6 -7
  12. package/templates/java-spring/hexagonal/.gitignore.hbs +72 -0
  13. package/templates/java-spring/hexagonal/docker-compose.yml.hbs +6 -3
  14. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/security/JwtFilter.java.hbs +71 -0
  15. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/security/SecurityConfig.java.hbs +35 -0
  16. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/core/service/PaymentService.java.hbs +3 -3
  17. package/templates/java-spring/hexagonal/src/main/resources/application.properties.hbs +4 -4
  18. package/templates/java-spring/mvc/.gitignore.hbs +72 -0
  19. package/templates/java-spring/mvc/docker-compose.yml.hbs +6 -3
  20. package/templates/java-spring/mvc/src/main/java/{{packagePath}}/config/SecurityConfig.java.hbs +13 -12
  21. package/templates/java-spring/mvc/src/main/java/{{packagePath}}/controller/AuthController.java.hbs +9 -8
  22. package/templates/java-spring/mvc/src/main/java/{{packagePath}}/controller/PaymentsController.java.hbs +5 -6
  23. package/templates/java-spring/mvc/src/main/java/{{packagePath}}/service/StripeService.java.hbs +3 -3
  24. package/templates/java-spring/mvc/src/main/resources/application.yml.hbs +29 -26
  25. package/templates/nestjs/clean/.gitignore.hbs +42 -0
  26. package/templates/nestjs/clean/Dockerfile.hbs +6 -3
  27. package/templates/nestjs/clean/docker-compose.yml.hbs +1 -11
  28. package/templates/nestjs/clean/src/app.module.ts.hbs +2 -1
  29. package/templates/nestjs/clean/src/application/payment.service.ts.hbs +72 -72
  30. package/templates/nestjs/clean/src/domain/entities/user.entity.ts.hbs +2 -2
  31. package/templates/nestjs/clean/src/domain/repositories/user.repository.ts.hbs +2 -2
  32. package/templates/nestjs/clean/src/infrastructure/database/repositories/prisma.user.repository.ts.hbs +18 -18
  33. package/templates/nestjs/clean/src/infrastructure/http/health.controller.ts.hbs +9 -0
  34. package/templates/nestjs/clean/src/main.ts.hbs +1 -4
  35. package/templates/nestjs/clean/src/payment.module.ts.hbs +12 -12
  36. package/templates/nestjs/hexagonal/.gitignore.hbs +42 -0
  37. package/templates/nestjs/hexagonal/Dockerfile.hbs +6 -3
  38. package/templates/nestjs/hexagonal/docker-compose.yml.hbs +1 -11
  39. package/templates/nestjs/hexagonal/src/adapters/inbound/health.controller.ts.hbs +9 -0
  40. package/templates/nestjs/hexagonal/src/app.module.ts.hbs +2 -1
  41. package/templates/nestjs/hexagonal/src/core/domain/user.entity.ts.hbs +6 -6
  42. package/templates/nestjs/hexagonal/src/core/ports/ports.ts.hbs +4 -4
  43. package/templates/nestjs/hexagonal/src/main.ts.hbs +1 -4
  44. package/templates/nestjs/mvc/.gitignore.hbs +42 -0
  45. package/templates/nestjs/mvc/Dockerfile.hbs +6 -3
  46. package/templates/nestjs/mvc/docker-compose.yml.hbs +1 -11
  47. package/templates/nestjs/mvc/src/auth/auth.controller.ts.hbs +11 -1
  48. package/templates/nestjs/mvc/src/auth/auth.service.ts.hbs +3 -1
  49. package/templates/nestjs/mvc/src/controllers/health.controller.ts.hbs +6 -6
  50. package/templates/nestjs/mvc/src/main.ts.hbs +1 -4
  51. package/templates/nestjs/mvc/src/models/create-item.dto.ts.hbs +5 -2
  52. package/templates/nestjs/mvc/src/prisma/prisma.service.ts.hbs +1 -0
  53. package/templates/nextjs/mvc/.gitignore.hbs +42 -0
  54. package/templates/nextjs/mvc/Dockerfile.hbs +23 -8
  55. package/templates/nextjs/mvc/docker-compose.yml.hbs +1 -1
  56. package/templates/nodejs-express/clean/.gitignore.hbs +42 -0
  57. package/templates/nodejs-express/clean/Dockerfile.hbs +6 -1
  58. package/templates/nodejs-express/clean/docker-compose.yml.hbs +2 -2
  59. package/templates/nodejs-express/clean/package.json.hbs +69 -69
  60. package/templates/nodejs-express/clean/src/config.ts.hbs +11 -0
  61. package/templates/nodejs-express/clean/src/domain/entities/User.ts.hbs +46 -8
  62. package/templates/nodejs-express/hexagonal/.gitignore.hbs +42 -0
  63. package/templates/nodejs-express/hexagonal/Dockerfile.hbs +1 -1
  64. package/templates/nodejs-express/hexagonal/docker-compose.yml.hbs +2 -2
  65. package/templates/nodejs-express/hexagonal/package.json.hbs +69 -69
  66. package/templates/nodejs-express/hexagonal/src/adapters/inbound/http/PaymentController.ts.hbs +21 -38
  67. package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/prisma.ts.hbs +2 -0
  68. package/templates/nodejs-express/hexagonal/src/config.ts.hbs +9 -0
  69. package/templates/nodejs-express/hexagonal/src/core/AuthService.ts.hbs +5 -5
  70. package/templates/nodejs-express/hexagonal/src/core/PaymentService.ts.hbs +7 -22
  71. package/templates/nodejs-express/hexagonal/src/core/domain/entities/User.ts.hbs +24 -4
  72. package/templates/nodejs-express/mvc/.gitignore.hbs +42 -0
  73. package/templates/nodejs-express/mvc/package.json.hbs +67 -67
  74. package/templates/python-fastapi/clean/.gitignore.hbs +76 -0
  75. package/templates/python-fastapi/clean/app/application/services/payment_service.py.hbs +3 -3
  76. package/templates/python-fastapi/clean/app/config.py.hbs +6 -7
  77. package/templates/python-fastapi/clean/app/domain/usecases/login_user.py.hbs +15 -0
  78. package/templates/python-fastapi/clean/app/infrastructure/http/auth_controller.py.hbs +40 -6
  79. package/templates/python-fastapi/clean/app/infrastructure/http/payment_controller.py.hbs +5 -4
  80. package/templates/python-fastapi/clean/app/infrastructure/security/jwt.py.hbs +23 -0
  81. package/templates/python-fastapi/clean/app/main.py.hbs +3 -0
  82. package/templates/python-fastapi/clean/docker-compose.yml.hbs +5 -12
  83. package/templates/python-fastapi/clean/requirements.txt.hbs +3 -0
  84. package/templates/python-fastapi/hexagonal/.gitignore.hbs +76 -0
  85. package/templates/python-fastapi/hexagonal/app/adapters/inbound/http_adapter.py.hbs +6 -9
  86. package/templates/python-fastapi/hexagonal/app/adapters/inbound/payment_http_adapter.py.hbs +4 -3
  87. package/templates/python-fastapi/hexagonal/app/adapters/outbound/stripe_adapter.py.hbs +30 -19
  88. package/templates/python-fastapi/hexagonal/app/config.py.hbs +14 -4
  89. package/templates/python-fastapi/hexagonal/app/core/domain/user.py.hbs +3 -1
  90. package/templates/python-fastapi/hexagonal/app/core/payment_service.py.hbs +28 -18
  91. package/templates/python-fastapi/hexagonal/app/core/ports/__init__.py.hbs +3 -0
  92. package/templates/python-fastapi/hexagonal/app/core/ports/user_repository.py.hbs +15 -0
  93. package/templates/python-fastapi/hexagonal/app/infrastructure/database/session.py.hbs +7 -0
  94. package/templates/python-fastapi/hexagonal/app/infrastructure/database/user_repository.py.hbs +53 -0
  95. package/templates/python-fastapi/hexagonal/app/infrastructure/security/__init__.py.hbs +0 -0
  96. package/templates/python-fastapi/hexagonal/app/infrastructure/security/adapters.py.hbs +23 -0
  97. package/templates/python-fastapi/hexagonal/app/infrastructure/security/jwt.py.hbs +23 -0
  98. package/templates/python-fastapi/hexagonal/docker-compose.yml.hbs +5 -12
  99. package/templates/python-fastapi/hexagonal/requirements.txt.hbs +4 -0
  100. package/templates/python-fastapi/mvc/.gitignore.hbs +76 -0
  101. package/templates/python-fastapi/mvc/app/controllers/payments.py.hbs +3 -17
  102. package/templates/python-fastapi/mvc/app/middleware/security.py.hbs +24 -3
  103. package/templates/python-fastapi/mvc/app/schemas/item.py.hbs +3 -1
  104. package/templates/python-fastapi/mvc/docker-compose.yml.hbs +5 -12
  105. package/templates/python-fastapi/mvc/requirements.txt.hbs +3 -1
  106. package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/prisma.ts +0 -5
@@ -1,71 +1,71 @@
1
1
  {
2
- "name": "{{kebabCase projectName}}",
3
- "version": "1.0.0",
4
- "description": "Generated by Kybernus CLI (Pro)",
5
- "main": "dist/index.js",
6
- "scripts": {
7
- "dev": "tsx watch src/index.ts",
8
- "build": "rimraf dist && tsc",
9
- "start": "node dist/index.js",
10
- "lint": "eslint src/**/*.ts",
11
- "format": "prettier --write \"src/**/*.ts\"",
12
- "test": "jest",
13
- "migrate:dev": "prisma migrate dev",
14
- "migrate:deploy": "prisma migrate deploy",
15
- "generate": "prisma generate"
16
- },
17
- "keywords": [
18
- "express",
19
- "api",
20
- "typescript",
21
- "clean-architecture",
22
- "prisma",
23
- "zod"
24
- ],
25
- "author": "",
26
- "license": "MIT",
27
- "dependencies": {
28
- "express": "^4.18.2",
29
- "dotenv": "^16.4.5",
30
- "cors": "^2.8.5",
31
- "helmet": "^7.1.0",
32
- "morgan": "^1.10.0",
33
- "jsonwebtoken": "^9.0.2",
34
- "bcryptjs": "^2.4.3",
35
- "stripe": "^14.14.0",
36
- "zod": "^3.22.4",
37
- "express-async-errors": "^3.1.1",
38
- "@prisma/client": "^5.10.2"
39
- },
40
- "devDependencies": {
41
- "@types/express": "^4.17.21",
42
- "@types/node": "^20.11.19",
43
- "@types/cors": "^2.8.17",
44
- "@types/morgan": "^1.9.9",
45
- "@types/jsonwebtoken": "^9.0.5",
46
- "@types/bcryptjs": "^2.4.6",
47
- "typescript": "^5.3.3",
48
- "tsx": "^4.7.1",
49
- "rimraf": "^5.0.5",
50
- "eslint": "^9.0.0",
51
- "@typescript-eslint/eslint-plugin": "^8.0.0",
52
- "@typescript-eslint/parser": "^8.0.0",
53
- "prettier": "^3.2.5",
54
- "jest": "^29.7.0",
55
- "@types/jest": "^29.5.12",
56
- "prisma": "^5.10.2"
57
- },
58
- "engines": {
59
- "node": ">=18.0.0"
60
- },
61
- "overrides": {
62
- "rimraf": "^6.1.2",
63
- "glob": "^11.0.0",
64
- "inflight": "^1.0.6",
65
- "@humanwhocodes/config-array": "^0.13.0",
66
- "@humanwhocodes/object-schema": "^2.0.3",
67
- "eslint": "^9.16.0",
68
- "cookie": "^0.7.2",
69
- "minimatch": "^9.0.8"
2
+ "name": "{{kebabCase projectName}}",
3
+ "version": "1.0.0",
4
+ "description": "Generated by Kybernus CLI (Pro)",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "dev": "tsx watch src/index.ts",
8
+ "build": "rimraf dist && tsc",
9
+ "start": "node dist/index.js",
10
+ "lint": "eslint src/**/*.ts",
11
+ "format": "prettier --write \"src/**/*.ts\"",
12
+ "test": "jest",
13
+ "migrate:dev": "prisma migrate dev",
14
+ "migrate:deploy": "prisma migrate deploy",
15
+ "generate": "prisma generate"
16
+ },
17
+ "keywords": [
18
+ "express",
19
+ "api",
20
+ "typescript",
21
+ "clean-architecture",
22
+ "prisma",
23
+ "zod"
24
+ ],
25
+ "author": "",
26
+ "license": "MIT",
27
+ "dependencies": {
28
+ "express": "^4.18.2",
29
+ "dotenv": "^16.4.5",
30
+ "cors": "^2.8.5",
31
+ "helmet": "^7.1.0",
32
+ "morgan": "^1.10.0",
33
+ "jsonwebtoken": "^9.0.2",
34
+ "bcryptjs": "^2.4.3",
35
+ "stripe": "^14.14.0",
36
+ "zod": "^3.22.4",
37
+ "express-async-errors": "^3.1.1",
38
+ "@prisma/client": "^5.10.2"
39
+ },
40
+ "devDependencies": {
41
+ "@types/express": "^4.17.21",
42
+ "@types/node": "^20.11.19",
43
+ "@types/cors": "^2.8.17",
44
+ "@types/morgan": "^1.9.9",
45
+ "@types/jsonwebtoken": "^9.0.5",
46
+ "@types/bcryptjs": "^2.4.6",
47
+ "typescript": "^5.3.3",
48
+ "tsx": "^4.7.1",
49
+ "rimraf": "^6.1.2",
50
+ "eslint": "^9.16.0",
51
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
52
+ "@typescript-eslint/parser": "^8.0.0",
53
+ "prettier": "^3.2.5",
54
+ "jest": "^29.7.0",
55
+ "@types/jest": "^29.5.12",
56
+ "prisma": "^5.10.2"
57
+ },
58
+ "engines": {
59
+ "node": ">=18.0.0"
60
+ },
61
+ "overrides": {
62
+ "rimraf": "^6.1.2",
63
+ "glob": "^11.0.0",
64
+ "inflight": "^1.0.6",
65
+ "@humanwhocodes/config-array": "^0.13.0",
66
+ "@humanwhocodes/object-schema": "^2.0.3",
67
+ "eslint": "^9.16.0",
68
+ "cookie": "^0.7.2",
69
+ "minimatch": "^9.0.8"
70
+ }
70
71
  }
71
- }
@@ -0,0 +1,11 @@
1
+ import dotenv from 'dotenv';
2
+
3
+ dotenv.config();
4
+
5
+ export const config = {
6
+ port: process.env.PORT ? parseInt(process.env.PORT, 10) : 3000,
7
+ jwtSecret: process.env.JWT_SECRET || 'your-secret-key',
8
+ databaseUrl: process.env.DATABASE_URL,
9
+ stripeSecretKey: process.env.STRIPE_SECRET_KEY,
10
+ stripeWebhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
11
+ };
@@ -28,11 +28,49 @@ export class User {
28
28
  if (!props.email || !props.email.includes('@')) {
29
29
  throw new Error('Invalid email format');
30
30
  }
31
- if (!props.password || props.password.length < 8) { throw new Error('Password must be at least 8 characters'); } return
32
- new User(props); } static restore(props: UserProps): User { return new User(props); } get id(): string | undefined {
33
- return this.props.id; } get email(): string { return this.props.email; } get name(): string { return
34
- this.props.name; } get password(): string { return this.props.password; } get stripeCustomerId(): string | undefined
35
- { return this.props.stripeCustomerId; } setStripeCustomerId(customerId: string): void {
36
- this.props.stripeCustomerId=customerId; this.props.updatedAt=new Date(); } toJSON() { return { id: this.props.id,
37
- email: this.props.email, name: this.props.name, stripeCustomerId: this.props.stripeCustomerId, createdAt:
38
- this.props.createdAt, updatedAt: this.props.updatedAt, }; } }
31
+ if (!props.password || props.password.length < 8) {
32
+ throw new Error('Password must be at least 8 characters');
33
+ }
34
+ return new User(props);
35
+ }
36
+
37
+ static restore(props: UserProps): User {
38
+ return new User(props);
39
+ }
40
+
41
+ get id(): string | undefined {
42
+ return this.props.id;
43
+ }
44
+
45
+ get email(): string {
46
+ return this.props.email;
47
+ }
48
+
49
+ get name(): string {
50
+ return this.props.name;
51
+ }
52
+
53
+ get password(): string {
54
+ return this.props.password;
55
+ }
56
+
57
+ get stripeCustomerId(): string | undefined {
58
+ return this.props.stripeCustomerId;
59
+ }
60
+
61
+ setStripeCustomerId(customerId: string): void {
62
+ this.props.stripeCustomerId = customerId;
63
+ this.props.updatedAt = new Date();
64
+ }
65
+
66
+ toJSON() {
67
+ return {
68
+ id: this.props.id,
69
+ email: this.props.email,
70
+ name: this.props.name,
71
+ stripeCustomerId: this.props.stripeCustomerId,
72
+ createdAt: this.props.createdAt,
73
+ updatedAt: this.props.updatedAt,
74
+ };
75
+ }
76
+ }
@@ -0,0 +1,42 @@
1
+ # Dependencies
2
+ node_modules/
3
+ .pnp
4
+ .pnp.js
5
+
6
+ # Build outputs
7
+ dist/
8
+ build/
9
+
10
+ # Environment variables
11
+ .env
12
+ .env.local
13
+ .env.development.local
14
+ .env.test.local
15
+ .env.production.local
16
+ !.env.example
17
+
18
+ # Logs
19
+ logs/
20
+ *.log
21
+ npm-debug.log*
22
+ yarn-debug.log*
23
+ yarn-error.log*
24
+ pnpm-debug.log*
25
+ lerna-debug.log*
26
+
27
+ # Coverage
28
+ coverage/
29
+ .nyc_output
30
+
31
+ # TypeScript
32
+ *.tsbuildinfo
33
+
34
+ # OS
35
+ .DS_Store
36
+ Thumbs.db
37
+
38
+ # Editor
39
+ .vscode/
40
+ .idea/
41
+ *.swp
42
+ *.swo
@@ -9,7 +9,7 @@ WORKDIR /app
9
9
  COPY package*.json ./
10
10
 
11
11
  # Install dependencies
12
- RUN npm ci --only=production
12
+ RUN npm ci
13
13
 
14
14
  # Copy source code
15
15
  COPY . .
@@ -7,9 +7,9 @@ services:
7
7
  environment:
8
8
  POSTGRES_USER: postgres
9
9
  POSTGRES_PASSWORD: password
10
- POSTGRES_DB: {{kebabCase projectName}}_db
10
+ POSTGRES_DB: {{kebabCase projectName}}_hexagonal_db
11
11
  ports:
12
- - '5432:5432'
12
+ - '5433:5432'
13
13
  volumes:
14
14
  - postgres_data:/var/lib/postgresql/data
15
15
 
@@ -1,71 +1,71 @@
1
1
  {
2
- "name": "{{kebabCase projectName}}",
3
- "version": "1.0.0",
4
- "description": "Generated by Kybernus CLI (Pro)",
5
- "main": "dist/index.js",
6
- "scripts": {
7
- "dev": "tsx watch src/index.ts",
8
- "build": "rimraf dist && tsc",
9
- "start": "node dist/index.js",
10
- "lint": "eslint src/**/*.ts",
11
- "format": "prettier --write \"src/**/*.ts\"",
12
- "test": "jest",
13
- "migrate:dev": "prisma migrate dev",
14
- "migrate:deploy": "prisma migrate deploy",
15
- "generate": "prisma generate"
16
- },
17
- "keywords": [
18
- "express",
19
- "api",
20
- "typescript",
21
- "hexagonal-architecture",
22
- "prisma",
23
- "zod"
24
- ],
25
- "author": "",
26
- "license": "MIT",
27
- "dependencies": {
28
- "express": "^4.18.2",
29
- "dotenv": "^16.4.5",
30
- "cors": "^2.8.5",
31
- "helmet": "^7.1.0",
32
- "morgan": "^1.10.0",
33
- "jsonwebtoken": "^9.0.2",
34
- "bcryptjs": "^2.4.3",
35
- "stripe": "^14.14.0",
36
- "zod": "^3.22.4",
37
- "express-async-errors": "^3.1.1",
38
- "@prisma/client": "^5.10.2"
39
- },
40
- "devDependencies": {
41
- "@types/express": "^4.17.21",
42
- "@types/node": "^20.11.19",
43
- "@types/cors": "^2.8.17",
44
- "@types/morgan": "^1.9.9",
45
- "@types/jsonwebtoken": "^9.0.5",
46
- "@types/bcryptjs": "^2.4.6",
47
- "typescript": "^5.3.3",
48
- "tsx": "^4.7.1",
49
- "rimraf": "^5.0.5",
50
- "eslint": "^9.0.0",
51
- "@typescript-eslint/eslint-plugin": "^8.0.0",
52
- "@typescript-eslint/parser": "^8.0.0",
53
- "prettier": "^3.2.5",
54
- "jest": "^29.7.0",
55
- "@types/jest": "^29.5.12",
56
- "prisma": "^5.10.2"
57
- },
58
- "engines": {
59
- "node": ">=18.0.0"
60
- },
61
- "overrides": {
62
- "rimraf": "^6.1.2",
63
- "glob": "^11.0.0",
64
- "inflight": "^1.0.6",
65
- "@humanwhocodes/config-array": "^0.13.0",
66
- "@humanwhocodes/object-schema": "^2.0.3",
67
- "eslint": "^9.16.0",
68
- "cookie": "^0.7.2",
69
- "minimatch": "^9.0.8"
2
+ "name": "{{kebabCase projectName}}",
3
+ "version": "1.0.0",
4
+ "description": "Generated by Kybernus CLI (Pro)",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "dev": "tsx watch src/index.ts",
8
+ "build": "rimraf dist && tsc",
9
+ "start": "node dist/index.js",
10
+ "lint": "eslint src/**/*.ts",
11
+ "format": "prettier --write \"src/**/*.ts\"",
12
+ "test": "jest",
13
+ "migrate:dev": "prisma migrate dev",
14
+ "migrate:deploy": "prisma migrate deploy",
15
+ "generate": "prisma generate"
16
+ },
17
+ "keywords": [
18
+ "express",
19
+ "api",
20
+ "typescript",
21
+ "hexagonal-architecture",
22
+ "prisma",
23
+ "zod"
24
+ ],
25
+ "author": "",
26
+ "license": "MIT",
27
+ "dependencies": {
28
+ "express": "^4.18.2",
29
+ "dotenv": "^16.4.5",
30
+ "cors": "^2.8.5",
31
+ "helmet": "^7.1.0",
32
+ "morgan": "^1.10.0",
33
+ "jsonwebtoken": "^9.0.2",
34
+ "bcryptjs": "^2.4.3",
35
+ "stripe": "^14.14.0",
36
+ "zod": "^3.22.4",
37
+ "express-async-errors": "^3.1.1",
38
+ "@prisma/client": "^5.10.2"
39
+ },
40
+ "devDependencies": {
41
+ "@types/express": "^4.17.21",
42
+ "@types/node": "^20.11.19",
43
+ "@types/cors": "^2.8.17",
44
+ "@types/morgan": "^1.9.9",
45
+ "@types/jsonwebtoken": "^9.0.5",
46
+ "@types/bcryptjs": "^2.4.6",
47
+ "typescript": "^5.3.3",
48
+ "tsx": "^4.7.1",
49
+ "rimraf": "^6.1.2",
50
+ "eslint": "^9.16.0",
51
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
52
+ "@typescript-eslint/parser": "^8.0.0",
53
+ "prettier": "^3.2.5",
54
+ "jest": "^29.7.0",
55
+ "@types/jest": "^29.5.12",
56
+ "prisma": "^5.10.2"
57
+ },
58
+ "engines": {
59
+ "node": ">=18.0.0"
60
+ },
61
+ "overrides": {
62
+ "rimraf": "^6.1.2",
63
+ "glob": "^11.0.0",
64
+ "inflight": "^1.0.6",
65
+ "@humanwhocodes/config-array": "^0.13.0",
66
+ "@humanwhocodes/object-schema": "^2.0.3",
67
+ "eslint": "^9.16.0",
68
+ "cookie": "^0.7.2",
69
+ "minimatch": "^9.0.8"
70
+ }
70
71
  }
71
- }
@@ -1,57 +1,40 @@
1
- import { Request, Response, NextFunction } from 'express';
1
+ import { Request, Response } from 'express';
2
2
  import { PaymentService } from '../../../core/PaymentService';
3
3
 
4
4
  export class PaymentController {
5
5
  constructor(private readonly paymentService: PaymentService) {}
6
6
 
7
- /**
8
- * POST /api/payments/checkout
9
- * Requires authentication.
10
- */
11
- createCheckout = async (req: Request, res: Response, next: NextFunction) => {
12
- try {
13
- const userId = (req as any).userId as string;
14
- const { priceId } = req.body;
15
-
16
- if (!priceId) {
17
- return res.status(400).json({ error: 'priceId is required' });
18
- }
7
+ async createCheckoutSession(req: Request, res: Response) {
8
+ const { priceId } = req.body;
9
+ const userId = (req as any).user?.id as string;
19
10
 
11
+ try {
20
12
  const session = await this.paymentService.createCheckoutSession(userId, priceId);
21
13
  res.json({ url: session.url });
22
- } catch (err) {
23
- next(err);
14
+ } catch (error: any) {
15
+ res.status(400).json({ error: error.message });
24
16
  }
25
- };
17
+ }
18
+
19
+ async createPortalSession(req: Request, res: Response) {
20
+ const userId = (req as any).user?.id as string;
26
21
 
27
- /**
28
- * POST /api/payments/portal
29
- * Requires authentication.
30
- */
31
- createPortal = async (req: Request, res: Response, next: NextFunction) => {
32
22
  try {
33
- const userId = (req as any).userId as string;
34
23
  const session = await this.paymentService.createPortalSession(userId);
35
24
  res.json({ url: session.url });
36
- } catch (err) {
37
- next(err);
25
+ } catch (error: any) {
26
+ res.status(400).json({ error: error.message });
38
27
  }
39
- };
28
+ }
29
+
30
+ async handleWebhook(req: Request, res: Response) {
31
+ const sig = req.headers['stripe-signature'] as string;
40
32
 
41
- /**
42
- * POST /api/payments/webhook
43
- * No authentication – Stripe sends the raw body here.
44
- */
45
- handleWebhook = async (req: Request, res: Response, next: NextFunction) => {
46
33
  try {
47
- const signature = req.headers['stripe-signature'] as string;
48
- if (!signature) {
49
- return res.status(400).json({ error: 'Missing stripe-signature header' });
50
- }
51
- const result = await this.paymentService.handleWebhook(req.body, signature);
34
+ const result = await this.paymentService.handleWebhook(req.body, sig);
52
35
  res.json(result);
53
- } catch (err) {
54
- next(err);
36
+ } catch (error: any) {
37
+ res.status(400).send(`Webhook Error: ${error.message}`);
55
38
  }
56
- };
39
+ }
57
40
  }
@@ -0,0 +1,2 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+ export const prisma = new PrismaClient();
@@ -0,0 +1,9 @@
1
+ import dotenv from 'dotenv';
2
+ dotenv.config();
3
+
4
+ export const config = {
5
+ port: process.env.PORT || 3000,
6
+ env: process.env.NODE_ENV || 'development',
7
+ jwtSecret: process.env.JWT_SECRET || 'secret',
8
+ frontendUrl: process.env.FRONTEND_URL || 'http://localhost:3001',
9
+ };
@@ -1,7 +1,7 @@
1
- import { User } from '../domain/entities/User';
2
- import { IAuthPort } from '../ports/inbound/IAuthPort';
3
- import { IUserRepositoryPort } from '../ports/outbound/IUserRepositoryPort';
4
- import { IPasswordHasherPort, ITokenGeneratorPort } from '../ports/outbound/ISecurityPorts';
1
+ import { User } from './domain/entities/User';
2
+ import { IAuthPort } from './ports/inbound/IAuthPort';
3
+ import { IUserRepositoryPort } from './ports/outbound/IUserRepositoryPort';
4
+ import { IPasswordHasherPort, ITokenGeneratorPort } from './ports/outbound/ISecurityPorts';
5
5
 
6
6
  /**
7
7
  * Auth Service - Application Core
@@ -48,4 +48,4 @@ export class AuthService implements IAuthPort {
48
48
  if (!decoded) return null;
49
49
  return this.userRepository.findById(decoded.id);
50
50
  }
51
- }
51
+ }
@@ -1,9 +1,9 @@
1
- import { UserRepository } from './ports/UserRepository';
1
+ import { IUserRepositoryPort } from './ports/outbound/IUserRepositoryPort';
2
2
  import { StripeAdapter } from '../adapters/outbound/StripeAdapter';
3
3
 
4
4
  export class PaymentService {
5
5
  constructor(
6
- private readonly userRepository: UserRepository,
6
+ private readonly userRepository: IUserRepositoryPort,
7
7
  private readonly stripeAdapter: StripeAdapter,
8
8
  ) {}
9
9
 
@@ -17,11 +17,11 @@ export class PaymentService {
17
17
  const customer = await this.stripeAdapter.createCustomer(
18
18
  user.email,
19
19
  user.name,
20
- user.id,
20
+ user.id!,
21
21
  );
22
22
  customerId = customer.id;
23
- user.stripeCustomerId = customerId;
24
- await this.userRepository.save(user);
23
+ const updatedUser = user.withStripeCustomerId(customerId);
24
+ await this.userRepository.save(updatedUser);
25
25
  }
26
26
 
27
27
  return this.stripeAdapter.createCheckoutSession(
@@ -58,28 +58,13 @@ export class PaymentService {
58
58
  if (userId && session.customer) {
59
59
  const user = await this.userRepository.findById(userId);
60
60
  if (user) {
61
- user.stripeCustomerId = session.customer as string;
62
- await this.userRepository.save(user);
61
+ const updatedUser = user.withStripeCustomerId(session.customer as string);
62
+ await this.userRepository.save(updatedUser);
63
63
  }
64
64
  }
65
65
  console.log('Checkout completed for user:', userId);
66
66
  break;
67
67
  }
68
- case 'customer.subscription.updated': {
69
- const sub = event.data.object as any;
70
- console.log('Subscription updated:', sub.id, '| Status:', sub.status);
71
- break;
72
- }
73
- case 'customer.subscription.deleted': {
74
- const sub = event.data.object as any;
75
- console.log('Subscription deleted:', sub.id);
76
- break;
77
- }
78
- case 'invoice.payment_failed': {
79
- const invoice = event.data.object as any;
80
- console.log('Payment failed for invoice:', invoice.id);
81
- break;
82
- }
83
68
  default:
84
69
  console.log('Unhandled Stripe event:', event.type);
85
70
  }
@@ -22,7 +22,27 @@ export class User {
22
22
  }
23
23
 
24
24
  private validatePassword(password: string): void {
25
- if (!password || password.length < 8) { throw new Error('Password must be at least 8 characters'); } } withId(id:
26
- string): User { return new User(id, this.email, this.name, this.password, this.stripeCustomerId, this.createdAt); }
27
- withStripeCustomerId(customerId: string): User { return new User(this.id, this.email, this.name, this.password,
28
- customerId, this.createdAt); } }
25
+ if (!password || password.length < 8) {
26
+ throw new Error('Password must be at least 8 characters');
27
+ }
28
+ }
29
+
30
+ static restore(data: any): User {
31
+ return new User(
32
+ data.id,
33
+ data.email,
34
+ data.name,
35
+ data.password,
36
+ data.stripeCustomerId,
37
+ data.createdAt
38
+ );
39
+ }
40
+
41
+ withId(id: string): User {
42
+ return new User(id, this.email, this.name, this.password, this.stripeCustomerId, this.createdAt);
43
+ }
44
+
45
+ withStripeCustomerId(customerId: string): User {
46
+ return new User(this.id, this.email, this.name, this.password, customerId, this.createdAt);
47
+ }
48
+ }
@@ -0,0 +1,42 @@
1
+ # Dependencies
2
+ node_modules/
3
+ .pnp
4
+ .pnp.js
5
+
6
+ # Build outputs
7
+ dist/
8
+ build/
9
+
10
+ # Environment variables
11
+ .env
12
+ .env.local
13
+ .env.development.local
14
+ .env.test.local
15
+ .env.production.local
16
+ !.env.example
17
+
18
+ # Logs
19
+ logs/
20
+ *.log
21
+ npm-debug.log*
22
+ yarn-debug.log*
23
+ yarn-error.log*
24
+ pnpm-debug.log*
25
+ lerna-debug.log*
26
+
27
+ # Coverage
28
+ coverage/
29
+ .nyc_output
30
+
31
+ # TypeScript
32
+ *.tsbuildinfo
33
+
34
+ # OS
35
+ .DS_Store
36
+ Thumbs.db
37
+
38
+ # Editor
39
+ .vscode/
40
+ .idea/
41
+ *.swp
42
+ *.swo