kybernus 2.0.10 → 2.1.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 (208) hide show
  1. package/node_modules/{@isaacs/balanced-match → balanced-match}/README.md +7 -10
  2. package/node_modules/{@isaacs/balanced-match → balanced-match}/package.json +5 -16
  3. package/node_modules/{@isaacs/brace-expansion → brace-expansion}/README.md +2 -5
  4. package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/commonjs/index.js +1 -1
  5. package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/commonjs/index.js.map +1 -1
  6. package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/esm/index.js +1 -1
  7. package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/esm/index.js.map +1 -1
  8. package/node_modules/{@isaacs/brace-expansion → brace-expansion}/package.json +9 -5
  9. package/node_modules/glob/dist/commonjs/glob.d.ts +8 -0
  10. package/node_modules/glob/dist/commonjs/glob.d.ts.map +1 -1
  11. package/node_modules/glob/dist/commonjs/glob.js +2 -1
  12. package/node_modules/glob/dist/commonjs/glob.js.map +1 -1
  13. package/node_modules/glob/dist/commonjs/index.min.js +4 -0
  14. package/node_modules/glob/dist/commonjs/index.min.js.map +7 -0
  15. package/node_modules/glob/dist/commonjs/pattern.d.ts +3 -0
  16. package/node_modules/glob/dist/commonjs/pattern.d.ts.map +1 -1
  17. package/node_modules/glob/dist/commonjs/pattern.js +4 -0
  18. package/node_modules/glob/dist/commonjs/pattern.js.map +1 -1
  19. package/node_modules/glob/dist/esm/glob.d.ts +8 -0
  20. package/node_modules/glob/dist/esm/glob.d.ts.map +1 -1
  21. package/node_modules/glob/dist/esm/glob.js +2 -1
  22. package/node_modules/glob/dist/esm/glob.js.map +1 -1
  23. package/node_modules/glob/dist/esm/index.min.js +4 -0
  24. package/node_modules/glob/dist/esm/index.min.js.map +7 -0
  25. package/node_modules/glob/dist/esm/pattern.d.ts +3 -0
  26. package/node_modules/glob/dist/esm/pattern.d.ts.map +1 -1
  27. package/node_modules/glob/dist/esm/pattern.js +4 -0
  28. package/node_modules/glob/dist/esm/pattern.js.map +1 -1
  29. package/node_modules/glob/package.json +30 -10
  30. package/node_modules/lru-cache/package.json +7 -7
  31. package/node_modules/minimatch/README.md +3 -1
  32. package/node_modules/minimatch/dist/commonjs/ast.d.ts.map +1 -1
  33. package/node_modules/minimatch/dist/commonjs/ast.js +37 -27
  34. package/node_modules/minimatch/dist/commonjs/ast.js.map +1 -1
  35. package/node_modules/minimatch/dist/commonjs/brace-expressions.d.ts.map +1 -1
  36. package/node_modules/minimatch/dist/commonjs/brace-expressions.js +2 -4
  37. package/node_modules/minimatch/dist/commonjs/brace-expressions.js.map +1 -1
  38. package/node_modules/minimatch/dist/commonjs/escape.js +4 -4
  39. package/node_modules/minimatch/dist/commonjs/escape.js.map +1 -1
  40. package/node_modules/minimatch/dist/commonjs/index.d.ts +50 -0
  41. package/node_modules/minimatch/dist/commonjs/index.d.ts.map +1 -1
  42. package/node_modules/minimatch/dist/commonjs/index.js +37 -31
  43. package/node_modules/minimatch/dist/commonjs/index.js.map +1 -1
  44. package/node_modules/minimatch/dist/commonjs/unescape.js +4 -4
  45. package/node_modules/minimatch/dist/commonjs/unescape.js.map +1 -1
  46. package/node_modules/minimatch/dist/esm/ast.d.ts.map +1 -1
  47. package/node_modules/minimatch/dist/esm/ast.js +37 -27
  48. package/node_modules/minimatch/dist/esm/ast.js.map +1 -1
  49. package/node_modules/minimatch/dist/esm/brace-expressions.d.ts.map +1 -1
  50. package/node_modules/minimatch/dist/esm/brace-expressions.js +2 -4
  51. package/node_modules/minimatch/dist/esm/brace-expressions.js.map +1 -1
  52. package/node_modules/minimatch/dist/esm/escape.js +4 -4
  53. package/node_modules/minimatch/dist/esm/escape.js.map +1 -1
  54. package/node_modules/minimatch/dist/esm/index.d.ts +50 -0
  55. package/node_modules/minimatch/dist/esm/index.d.ts.map +1 -1
  56. package/node_modules/minimatch/dist/esm/index.js +37 -31
  57. package/node_modules/minimatch/dist/esm/index.js.map +1 -1
  58. package/node_modules/minimatch/dist/esm/unescape.js +4 -4
  59. package/node_modules/minimatch/dist/esm/unescape.js.map +1 -1
  60. package/node_modules/minimatch/package.json +3 -3
  61. package/node_modules/minipass/LICENSE.md +55 -0
  62. package/node_modules/minipass/dist/commonjs/index.d.ts +12 -16
  63. package/node_modules/minipass/dist/commonjs/index.d.ts.map +1 -1
  64. package/node_modules/minipass/dist/commonjs/index.js +13 -3
  65. package/node_modules/minipass/dist/commonjs/index.js.map +1 -1
  66. package/node_modules/minipass/dist/esm/index.d.ts +12 -16
  67. package/node_modules/minipass/dist/esm/index.d.ts.map +1 -1
  68. package/node_modules/minipass/dist/esm/index.js +3 -1
  69. package/node_modules/minipass/dist/esm/index.js.map +1 -1
  70. package/node_modules/minipass/package.json +9 -14
  71. package/node_modules/rimraf/README.md +29 -0
  72. package/node_modules/rimraf/dist/commonjs/fs.d.ts +9 -8
  73. package/node_modules/rimraf/dist/commonjs/fs.d.ts.map +1 -1
  74. package/node_modules/rimraf/dist/commonjs/index.d.ts.map +1 -1
  75. package/node_modules/rimraf/dist/commonjs/index.js +9 -3
  76. package/node_modules/rimraf/dist/commonjs/index.js.map +1 -1
  77. package/node_modules/rimraf/dist/commonjs/opt-arg.d.ts.map +1 -1
  78. package/node_modules/rimraf/dist/commonjs/opt-arg.js +2 -1
  79. package/node_modules/rimraf/dist/commonjs/opt-arg.js.map +1 -1
  80. package/node_modules/rimraf/dist/commonjs/path-arg.d.ts.map +1 -1
  81. package/node_modules/rimraf/dist/commonjs/path-arg.js +2 -1
  82. package/node_modules/rimraf/dist/commonjs/path-arg.js.map +1 -1
  83. package/node_modules/rimraf/dist/commonjs/readdir-or-error.d.ts +2 -2
  84. package/node_modules/rimraf/dist/commonjs/readdir-or-error.d.ts.map +1 -1
  85. package/node_modules/rimraf/dist/commonjs/rimraf-move-remove.d.ts.map +1 -1
  86. package/node_modules/rimraf/dist/commonjs/rimraf-move-remove.js.map +1 -1
  87. package/node_modules/rimraf/dist/commonjs/rimraf-posix.d.ts.map +1 -1
  88. package/node_modules/rimraf/dist/commonjs/rimraf-posix.js +1 -2
  89. package/node_modules/rimraf/dist/commonjs/rimraf-posix.js.map +1 -1
  90. package/node_modules/rimraf/dist/commonjs/rimraf-windows.d.ts.map +1 -1
  91. package/node_modules/rimraf/dist/commonjs/rimraf-windows.js.map +1 -1
  92. package/node_modules/rimraf/dist/esm/bin.mjs.map +1 -1
  93. package/node_modules/rimraf/dist/esm/fs.d.ts +9 -8
  94. package/node_modules/rimraf/dist/esm/fs.d.ts.map +1 -1
  95. package/node_modules/rimraf/dist/esm/index.d.ts.map +1 -1
  96. package/node_modules/rimraf/dist/esm/index.js +10 -4
  97. package/node_modules/rimraf/dist/esm/index.js.map +1 -1
  98. package/node_modules/rimraf/dist/esm/opt-arg.d.ts.map +1 -1
  99. package/node_modules/rimraf/dist/esm/opt-arg.js +2 -1
  100. package/node_modules/rimraf/dist/esm/opt-arg.js.map +1 -1
  101. package/node_modules/rimraf/dist/esm/path-arg.d.ts.map +1 -1
  102. package/node_modules/rimraf/dist/esm/path-arg.js +2 -1
  103. package/node_modules/rimraf/dist/esm/path-arg.js.map +1 -1
  104. package/node_modules/rimraf/dist/esm/readdir-or-error.d.ts +2 -2
  105. package/node_modules/rimraf/dist/esm/readdir-or-error.d.ts.map +1 -1
  106. package/node_modules/rimraf/dist/esm/rimraf-move-remove.d.ts.map +1 -1
  107. package/node_modules/rimraf/dist/esm/rimraf-move-remove.js +1 -1
  108. package/node_modules/rimraf/dist/esm/rimraf-move-remove.js.map +1 -1
  109. package/node_modules/rimraf/dist/esm/rimraf-posix.d.ts.map +1 -1
  110. package/node_modules/rimraf/dist/esm/rimraf-posix.js +1 -2
  111. package/node_modules/rimraf/dist/esm/rimraf-posix.js.map +1 -1
  112. package/node_modules/rimraf/dist/esm/rimraf-windows.d.ts.map +1 -1
  113. package/node_modules/rimraf/dist/esm/rimraf-windows.js +1 -1
  114. package/node_modules/rimraf/dist/esm/rimraf-windows.js.map +1 -1
  115. package/node_modules/rimraf/package.json +4 -19
  116. package/package.json +1 -1
  117. package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/persistence/PostgresUserRepository.java.hbs +40 -0
  118. package/templates/java-spring/clean/src/main/resources/application.properties.hbs +18 -0
  119. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{infrastructure/web/controller → adapters/inbound/web}/AuthController.java.hbs +4 -5
  120. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/persistence/JpaUserAdapter.java.hbs +40 -0
  121. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/persistence/entity/UserEntity.java.hbs +61 -0
  122. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/persistence/repository/JpaUserRepository.java.hbs +11 -0
  123. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{infrastructure/security/SecurityAdapters.java.hbs → adapters/outbound/security/SecurityAdapter.java.hbs} +14 -14
  124. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{domain/entity → core/domain}/User.java.hbs +2 -2
  125. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{domain/usecase → core/ports/inbound}/LoginUserUseCase.java.hbs +8 -8
  126. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{domain/usecase → core/ports/inbound}/RegisterUserUseCase.java.hbs +7 -8
  127. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{domain/repository → core/ports/outbound}/UserRepository.java.hbs +4 -4
  128. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{application → core}/service/AuthService.java.hbs +9 -9
  129. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{{projectNamePascalCase}}Application.java.hbs +2 -2
  130. package/templates/java-spring/hexagonal/src/main/resources/application.properties.hbs +18 -0
  131. package/templates/nestjs/clean/package.json.hbs +9 -3
  132. package/templates/nestjs/clean/prisma/schema.prisma.hbs +20 -0
  133. package/templates/nestjs/clean/src/app.module.ts.hbs +17 -0
  134. package/templates/nestjs/clean/src/auth.module.ts.hbs +12 -10
  135. package/templates/nestjs/clean/src/infrastructure/database/prisma.service.ts.hbs +13 -0
  136. package/templates/nestjs/clean/src/infrastructure/database/repositories/prisma.user.repository.ts.hbs +32 -0
  137. package/templates/nestjs/clean/src/main.ts.hbs +11 -0
  138. package/templates/nestjs/hexagonal/package.json.hbs +9 -3
  139. package/templates/nestjs/hexagonal/prisma/schema.prisma +20 -0
  140. package/templates/nestjs/hexagonal/src/adapters/outbound/persistence/prisma.service.ts.hbs +13 -0
  141. package/templates/nestjs/hexagonal/src/adapters/outbound/persistence/prisma.user.adapter.ts.hbs +32 -0
  142. package/templates/nestjs/hexagonal/src/app.module.ts.hbs +17 -0
  143. package/templates/nestjs/hexagonal/src/auth.module.ts.hbs +15 -13
  144. package/templates/nestjs/hexagonal/src/main.ts.hbs +11 -0
  145. package/templates/nextjs/mvc/package.json.hbs +35 -32
  146. package/templates/nextjs/mvc/prisma/schema.prisma.hbs +12 -9
  147. package/templates/nextjs/mvc/src/lib/db.ts +15 -0
  148. package/templates/nodejs-express/clean/docker-compose.yml.hbs +5 -6
  149. package/templates/nodejs-express/clean/package.json.hbs +14 -8
  150. package/templates/nodejs-express/clean/prisma/schema.prisma +20 -0
  151. package/templates/nodejs-express/clean/src/config/index.ts +27 -0
  152. package/templates/nodejs-express/clean/src/index.ts.hbs +20 -24
  153. package/templates/nodejs-express/clean/src/infrastructure/database/PrismaUserRepository.ts.hbs +61 -0
  154. package/templates/nodejs-express/clean/src/infrastructure/database/prisma.ts.hbs +5 -0
  155. package/templates/nodejs-express/clean/src/infrastructure/http/controllers/AuthController.ts.hbs +24 -40
  156. package/templates/nodejs-express/clean/src/infrastructure/http/middlewares/errorHandler.ts +24 -0
  157. package/templates/nodejs-express/clean/tsconfig.json.hbs +8 -17
  158. package/templates/nodejs-express/hexagonal/docker-compose.yml.hbs +5 -6
  159. package/templates/nodejs-express/hexagonal/package.json.hbs +14 -8
  160. package/templates/nodejs-express/hexagonal/prisma/schema.prisma +20 -0
  161. package/templates/nodejs-express/hexagonal/src/adapters/inbound/http/AuthController.ts.hbs +29 -44
  162. package/templates/nodejs-express/hexagonal/src/adapters/inbound/http/middlewares/errorHandler.ts +24 -0
  163. package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/PrismaUserAdapter.ts.hbs +61 -0
  164. package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/prisma.ts +5 -0
  165. package/templates/nodejs-express/hexagonal/src/config/index.ts +27 -0
  166. package/templates/nodejs-express/hexagonal/src/index.ts.hbs +24 -27
  167. package/templates/nodejs-express/hexagonal/tsconfig.json.hbs +8 -17
  168. package/templates/python-fastapi/clean/app/application/services/__init__.py +0 -0
  169. package/templates/python-fastapi/clean/app/application/services/user_service.py.hbs +20 -0
  170. package/templates/python-fastapi/clean/app/config.py.hbs +24 -0
  171. package/templates/python-fastapi/clean/app/infrastructure/database/models.py.hbs +24 -0
  172. package/templates/python-fastapi/clean/app/infrastructure/database/postgres_repository.py.hbs +62 -0
  173. package/templates/python-fastapi/clean/app/infrastructure/database/session.py.hbs +27 -0
  174. package/templates/python-fastapi/clean/app/infrastructure/http/auth_controller.py.hbs +14 -8
  175. package/templates/python-fastapi/clean/app/main.py.hbs +25 -3
  176. package/templates/python-fastapi/clean/requirements.txt.hbs +3 -1
  177. package/templates/python-fastapi/hexagonal/app/adapters/inbound/http_adapter.py.hbs +41 -17
  178. package/templates/python-fastapi/hexagonal/app/adapters/outbound/postgres_user_repository.py.hbs +50 -0
  179. package/templates/python-fastapi/hexagonal/app/config.py.hbs +20 -0
  180. package/templates/python-fastapi/hexagonal/app/infrastructure/database/models.py.hbs +24 -0
  181. package/templates/python-fastapi/hexagonal/app/infrastructure/database/session.py.hbs +20 -0
  182. package/templates/python-fastapi/hexagonal/app/main.py.hbs +22 -14
  183. package/templates/python-fastapi/hexagonal/requirements.txt.hbs +3 -1
  184. package/node_modules/minipass/LICENSE +0 -15
  185. package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/persistence/InMemoryUserRepository.java.hbs +0 -41
  186. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/infrastructure/persistence/InMemoryUserRepository.java.hbs +0 -41
  187. package/templates/nestjs/clean/src/infrastructure/database/in-memory.repository.ts.hbs +0 -17
  188. package/templates/nodejs-express/clean/src/infrastructure/database/InMemoryUserRepository.ts.hbs +0 -46
  189. package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/InMemoryUserAdapter.ts.hbs +0 -38
  190. /package/node_modules/{@isaacs/balanced-match → balanced-match}/LICENSE.md +0 -0
  191. /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/commonjs/index.d.ts +0 -0
  192. /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/commonjs/index.d.ts.map +0 -0
  193. /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/commonjs/index.js +0 -0
  194. /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/commonjs/index.js.map +0 -0
  195. /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/commonjs/package.json +0 -0
  196. /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/esm/index.d.ts +0 -0
  197. /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/esm/index.d.ts.map +0 -0
  198. /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/esm/index.js +0 -0
  199. /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/esm/index.js.map +0 -0
  200. /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/esm/package.json +0 -0
  201. /package/node_modules/{@isaacs/brace-expansion → brace-expansion}/LICENSE +0 -0
  202. /package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/commonjs/index.d.ts +0 -0
  203. /package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/commonjs/index.d.ts.map +0 -0
  204. /package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/commonjs/package.json +0 -0
  205. /package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/esm/index.d.ts +0 -0
  206. /package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/esm/index.d.ts.map +0 -0
  207. /package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/esm/package.json +0 -0
  208. /package/templates/python-fastapi/hexagonal/app/core/{ports.py.hbs → ports/ports.py.hbs} +0 -0
@@ -9,16 +9,18 @@
9
9
  "start": "node dist/index.js",
10
10
  "lint": "eslint src --ext .ts",
11
11
  "format": "prettier --write \"src/**/*.ts\"",
12
- "test": "jest"
12
+ "test": "jest",
13
+ "migrate:dev": "prisma migrate dev",
14
+ "migrate:deploy": "prisma migrate deploy",
15
+ "generate": "prisma generate"
13
16
  },
14
17
  "keywords": [
15
18
  "express",
16
19
  "api",
17
20
  "typescript",
18
- "mvc",
19
- "saas",
20
- "stripe",
21
- "auth"
21
+ "clean-architecture",
22
+ "prisma",
23
+ "zod"
22
24
  ],
23
25
  "author": "",
24
26
  "license": "MIT",
@@ -30,7 +32,10 @@
30
32
  "morgan": "^1.10.0",
31
33
  "jsonwebtoken": "^9.0.2",
32
34
  "bcryptjs": "^2.4.3",
33
- "stripe": "^14.14.0"
35
+ "stripe": "^14.14.0",
36
+ "zod": "^3.22.4",
37
+ "express-async-errors": "^3.1.1",
38
+ "@prisma/client": "^5.10.2"
34
39
  },
35
40
  "devDependencies": {
36
41
  "@types/express": "^4.17.21",
@@ -47,9 +52,10 @@
47
52
  "@typescript-eslint/parser": "^6.21.0",
48
53
  "prettier": "^3.2.5",
49
54
  "jest": "^29.7.0",
50
- "@types/jest": "^29.5.12"
55
+ "@types/jest": "^29.5.12",
56
+ "prisma": "^5.10.2"
51
57
  },
52
58
  "engines": {
53
59
  "node": ">=18.0.0"
54
60
  }
55
- }
61
+ }
@@ -0,0 +1,20 @@
1
+ generator client {
2
+ provider = "prisma-client-js"
3
+ }
4
+
5
+ datasource db {
6
+ provider = "postgresql"
7
+ url = env("DATABASE_URL")
8
+ }
9
+
10
+ model User {
11
+ id String @id @default(uuid())
12
+ email String @unique
13
+ name String
14
+ password String
15
+ stripeCustomerId String? @map("stripe_customer_id")
16
+ createdAt DateTime @default(now()) @map("created_at")
17
+ updatedAt DateTime @updatedAt @map("updated_at")
18
+
19
+ @@map("users")
20
+ }
@@ -0,0 +1,27 @@
1
+ import dotenv from 'dotenv';
2
+ import { z } from 'zod';
3
+
4
+ dotenv.config();
5
+
6
+ const envSchema = z.object({
7
+ NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
8
+ PORT: z.string().default('3000'),
9
+ DATABASE_URL: z.string(),
10
+ JWT_SECRET: z.string().min(10),
11
+ STRIPE_SECRET_KEY: z.string().optional(),
12
+ });
13
+
14
+ const _env = envSchema.safeParse(process.env);
15
+
16
+ if (!_env.success) {
17
+ console.error('❌ Invalid environment variables:', _env.error.format());
18
+ throw new Error('Invalid environment variables');
19
+ }
20
+
21
+ export const config = {
22
+ env: _env.data.NODE_ENV,
23
+ port: parseInt(_env.data.PORT, 10),
24
+ databaseUrl: _env.data.DATABASE_URL,
25
+ jwtSecret: _env.data.JWT_SECRET,
26
+ stripeSecretKey: _env.data.STRIPE_SECRET_KEY,
27
+ };
@@ -1,40 +1,36 @@
1
+ import 'express-async-errors';
1
2
  import express from 'express';
2
3
  import cors from 'cors';
3
4
  import helmet from 'helmet';
4
5
  import morgan from 'morgan';
5
- import dotenv from 'dotenv';
6
-
7
- import authRoutes from './infrastructure/http/controllers/AuthController';
8
-
9
- dotenv.config();
6
+ import { config } from './config';
7
+ import { PrismaUserRepository } from './infrastructure/database/PrismaUserRepository';
8
+ import { AuthController } from './infrastructure/http/controllers/AuthController';
9
+ import { errorHandler } from './infrastructure/http/middlewares/errorHandler';
10
10
 
11
11
  const app = express();
12
12
 
13
- // Security
14
- app.use(helmet());
13
+ // Middlewares
14
+ app.use(express.json());
15
15
  app.use(cors());
16
+ app.use(helmet());
16
17
  app.use(morgan('dev'));
17
18
 
18
- // Body parsing
19
- app.use(express.json());
20
- app.use(express.urlencoded({ extended: true }));
21
-
22
- // Health check
23
- app.get('/health', (_, res) => res.json({ status: 'ok' }));
19
+ // Dependency Injection
20
+ const userRepository = new PrismaUserRepository();
21
+ const authController = new AuthController(userRepository);
24
22
 
25
23
  // Routes
26
- app.use('/api/auth', authRoutes);
24
+ app.post('/api/auth/register', authController.register);
25
+ app.post('/api/auth/login', authController.login);
27
26
 
28
- // Error handling
29
- app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
30
- console.error(err.stack);
31
- res.status(500).json({ error: 'Internal server error' });
27
+ app.get('/health', (req, res) => {
28
+ res.json({ status: 'ok', architecture: 'clean' });
32
29
  });
33
30
 
34
- const PORT = process.env.PORT || 3000;
31
+ // Error Handler
32
+ app.use(errorHandler);
35
33
 
36
- app.listen(PORT, () => {
37
- console.log(`🚀 {{pascalCase projectName}} running on port ${PORT}`);
38
- console.log(`📐 Architecture: Clean Architecture`);
39
- console.log(`🔗 Health check: http://localhost:${PORT}/health`);
40
- });
34
+ app.listen(config.port, () => {
35
+ console.log(`🚀 Server running on port ${config.port}`);
36
+ });
@@ -0,0 +1,61 @@
1
+ import { IUserRepository } from '../../domain/repositories/IUserRepository';
2
+ import { User } from '../../domain/entities/User';
3
+ import { prisma } from './prisma';
4
+
5
+ export class PrismaUserRepository implements IUserRepository {
6
+ async findById(id: string): Promise<User | null> {
7
+ const user = await prisma.user.findUnique({ where: { id } });
8
+ if (!user) return null;
9
+ return User.restore({
10
+ id: user.id,
11
+ email: user.email,
12
+ name: user.name,
13
+ password: user.password,
14
+ stripeCustomerId: user.stripeCustomerId,
15
+ });
16
+ }
17
+
18
+ async findByEmail(email: string): Promise<User | null> {
19
+ const user = await prisma.user.findUnique({ where: { email } });
20
+ if (!user) return null;
21
+ return User.restore({
22
+ id: user.id,
23
+ email: user.email,
24
+ name: user.name,
25
+ password: user.password,
26
+ stripeCustomerId: user.stripeCustomerId,
27
+ });
28
+ }
29
+
30
+ async save(user: User): Promise<User> {
31
+ const data = {
32
+ id: user.id,
33
+ email: user.email,
34
+ name: user.name,
35
+ password: user.password,
36
+ stripeCustomerId: user.stripeCustomerId,
37
+ };
38
+
39
+ const savedUser = await prisma.user.upsert({
40
+ where: { id: user.id || '' },
41
+ update: { ...data },
42
+ create: { ...data },
43
+ });
44
+
45
+ return User.restore({
46
+ id: savedUser.id,
47
+ email: savedUser.email,
48
+ name: savedUser.name,
49
+ password: savedUser.password,
50
+ stripeCustomerId: savedUser.stripeCustomerId,
51
+ });
52
+ }
53
+
54
+ async update(user: User): Promise<User> {
55
+ return this.save(user);
56
+ }
57
+
58
+ async delete(id: string): Promise<void> {
59
+ await prisma.user.delete({ where: { id } });
60
+ }
61
+ }
@@ -0,0 +1,5 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+
3
+ export const prisma = new PrismaClient({
4
+ log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
5
+ });
@@ -1,45 +1,29 @@
1
- import { Router, Request, Response } from 'express';
2
- import { AuthService } from '../../../application/services/AuthService';
3
- import { InMemoryUserRepository } from '../../database/InMemoryUserRepository';
4
- import { bcryptPasswordHasher } from '../../providers/PasswordHasher';
5
- import { jwtTokenGenerator } from '../../providers/TokenGenerator';
6
- import { authMiddleware, AuthRequest } from '../middlewares/AuthMiddleware';
1
+ import { Request, Response } from 'express';
2
+ import { RegisterUserUseCase } from '../../../domain/usecases/RegisterUserUseCase';
3
+ import { LoginUserUseCase } from '../../../domain/usecases/LoginUserUseCase';
4
+ import { z } from 'zod';
7
5
 
8
- const router = Router();
9
-
10
- // Dependency Injection
11
- const userRepository = new InMemoryUserRepository();
12
- const authService = new AuthService(userRepository, bcryptPasswordHasher, jwtTokenGenerator);
13
-
14
- /**
15
- * @route POST /api/auth/register
16
- */
17
- router.post('/register', async (req: Request, res: Response) => {
18
- try {
19
- const result = await authService.register(req.body);
20
- res.status(201).json(result);
21
- } catch (error: any) {
22
- res.status(400).json({ error: error.message });
23
- }
6
+ const registerSchema = z.object({
7
+ email: z.string().email(),
8
+ name: z.string().min(2),
9
+ password: z.string().min(6),
24
10
  });
25
11
 
26
- /**
27
- * @route POST /api/auth/login
28
- */
29
- router.post('/login', async (req: Request, res: Response) => {
30
- try {
31
- const result = await authService.login(req.body);
32
- res.json(result);
33
- } catch (error: any) {
34
- res.status(401).json({ error: error.message });
35
- }
36
- });
12
+ export class AuthController {
13
+ constructor(
14
+ private registerUseCase: RegisterUserUseCase,
15
+ private loginUseCase: LoginUserUseCase
16
+ ) {}
37
17
 
38
- /**
39
- * @route GET /api/auth/me
40
- */
41
- router.get('/me', authMiddleware, (req: AuthRequest, res: Response) => {
42
- res.json({ user: req.user });
43
- });
18
+ register = async (req: Request, res: Response) => {
19
+ const { email, name, password } = registerSchema.parse(req.body);
20
+ const result = await this.registerUseCase.execute({ email, name, password });
21
+ res.status(201).json(result);
22
+ };
44
23
 
45
- export default router;
24
+ login = async (req: Request, res: Response) => {
25
+ const { email, password } = req.body;
26
+ const result = await this.loginUseCase.execute({ email, password });
27
+ res.json(result);
28
+ };
29
+ }
@@ -0,0 +1,24 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import { ZodError } from 'zod';
3
+
4
+ export function errorHandler(
5
+ err: Error,
6
+ req: Request,
7
+ res: Response,
8
+ next: NextFunction
9
+ ) {
10
+ console.error(err);
11
+
12
+ if (err instanceof ZodError) {
13
+ return res.status(400).json({
14
+ error: 'Validation Error',
15
+ details: err.format(),
16
+ });
17
+ }
18
+
19
+ if (err.message === 'User already exists' || err.message === 'Invalid credentials') {
20
+ return res.status(400).json({ error: err.message });
21
+ }
22
+
23
+ res.status(500).json({ error: 'Internal Server Error' });
24
+ }
@@ -1,27 +1,18 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "ES2022",
3
+ "target": "es2020",
4
4
  "module": "commonjs",
5
- "lib": [
6
- "ES2022"
7
- ],
5
+ "lib": ["es2020"],
8
6
  "outDir": "./dist",
9
7
  "rootDir": "./src",
10
8
  "strict": true,
11
9
  "esModuleInterop": true,
12
10
  "skipLibCheck": true,
13
11
  "forceConsistentCasingInFileNames": true,
14
- "resolveJsonModule": true,
15
- "moduleResolution": "node",
16
- "types": [
17
- "node"
18
- ]
12
+ "experimentalDecorators": true,
13
+ "emitDecoratorMetadata": true,
14
+ "resolveJsonModule": true
19
15
  },
20
- "include": [
21
- "src/**/*"
22
- ],
23
- "exclude": [
24
- "node_modules",
25
- "dist"
26
- ]
27
- }
16
+ "include": ["src/**/*"],
17
+ "exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"]
18
+ }
@@ -1,18 +1,17 @@
1
1
  version: '3.8'
2
2
 
3
3
  services:
4
- postgres:
4
+ db:
5
5
  image: postgres:15-alpine
6
- container_name: {{kebabCase projectName}}-db
6
+ restart: always
7
7
  environment:
8
8
  POSTGRES_USER: postgres
9
- POSTGRES_PASSWORD: postgres
10
- POSTGRES_DB: {{snakeCase projectName}}
9
+ POSTGRES_PASSWORD: password
10
+ POSTGRES_DB: {{kebabCase projectName}}_db
11
11
  ports:
12
- - "5432:5432"
12
+ - '5432:5432'
13
13
  volumes:
14
14
  - postgres_data:/var/lib/postgresql/data
15
- restart: unless-stopped
16
15
 
17
16
  volumes:
18
17
  postgres_data:
@@ -9,16 +9,18 @@
9
9
  "start": "node dist/index.js",
10
10
  "lint": "eslint src --ext .ts",
11
11
  "format": "prettier --write \"src/**/*.ts\"",
12
- "test": "jest"
12
+ "test": "jest",
13
+ "migrate:dev": "prisma migrate dev",
14
+ "migrate:deploy": "prisma migrate deploy",
15
+ "generate": "prisma generate"
13
16
  },
14
17
  "keywords": [
15
18
  "express",
16
19
  "api",
17
20
  "typescript",
18
- "mvc",
19
- "saas",
20
- "stripe",
21
- "auth"
21
+ "hexagonal-architecture",
22
+ "prisma",
23
+ "zod"
22
24
  ],
23
25
  "author": "",
24
26
  "license": "MIT",
@@ -30,7 +32,10 @@
30
32
  "morgan": "^1.10.0",
31
33
  "jsonwebtoken": "^9.0.2",
32
34
  "bcryptjs": "^2.4.3",
33
- "stripe": "^14.14.0"
35
+ "stripe": "^14.14.0",
36
+ "zod": "^3.22.4",
37
+ "express-async-errors": "^3.1.1",
38
+ "@prisma/client": "^5.10.2"
34
39
  },
35
40
  "devDependencies": {
36
41
  "@types/express": "^4.17.21",
@@ -47,9 +52,10 @@
47
52
  "@typescript-eslint/parser": "^6.21.0",
48
53
  "prettier": "^3.2.5",
49
54
  "jest": "^29.7.0",
50
- "@types/jest": "^29.5.12"
55
+ "@types/jest": "^29.5.12",
56
+ "prisma": "^5.10.2"
51
57
  },
52
58
  "engines": {
53
59
  "node": ">=18.0.0"
54
60
  }
55
- }
61
+ }
@@ -0,0 +1,20 @@
1
+ generator client {
2
+ provider = "prisma-client-js"
3
+ }
4
+
5
+ datasource db {
6
+ provider = "postgresql"
7
+ url = env("DATABASE_URL")
8
+ }
9
+
10
+ model User {
11
+ id String @id @default(uuid())
12
+ email String @unique
13
+ name String
14
+ password String
15
+ stripeCustomerId String? @map("stripe_customer_id")
16
+ createdAt DateTime @default(now()) @map("created_at")
17
+ updatedAt DateTime @updatedAt @map("updated_at")
18
+
19
+ @@map("users")
20
+ }
@@ -1,48 +1,33 @@
1
- import { Router, Request, Response } from 'express';
2
- import { IAuthPort } from '../../../core/ports/inbound/IAuthPort';
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import { IAuthService } from '../../../core/ports/inbound/IAuthService';
3
+ import { z } from 'zod';
3
4
 
4
- /**
5
- * HTTP Auth Controller - Inbound Adapter
6
- * Adapts HTTP requests to the application port
7
- */
8
- export function createAuthController(authService: IAuthPort): Router {
9
- const router = Router();
5
+ const registerSchema = z.object({
6
+ email: z.string().email(),
7
+ name: z.string().min(2),
8
+ password: z.string().min(6),
9
+ });
10
10
 
11
- router.post('/register', async (req: Request, res: Response) => {
12
- try {
13
- const { email, name, password } = req.body;
14
- const result = await authService.register(email, name, password);
15
- res.status(201).json({
16
- token: result.token,
17
- user: { id: result.user.id, email: result.user.email, name: result.user.name },
18
- });
19
- } catch (error: any) {
20
- res.status(400).json({ error: error.message });
21
- }
22
- });
11
+ export class AuthController {
12
+ constructor(private authService: IAuthService) {}
23
13
 
24
- router.post('/login', async (req: Request, res: Response) => {
25
- try {
26
- const { email, password } = req.body;
27
- const result = await authService.login(email, password);
28
- res.json({
29
- token: result.token,
30
- user: { id: result.user.id, email: result.user.email, name: result.user.name },
31
- });
32
- } catch (error: any) {
33
- res.status(401).json({ error: error.message });
34
- }
35
- });
14
+ register = async (req: Request, res: Response, next: NextFunction) => {
15
+ try {
16
+ const { email, name, password } = registerSchema.parse(req.body);
17
+ const result = await this.authService.register(email, name, password);
18
+ res.status(201).json(result);
19
+ } catch (error) {
20
+ next(error);
21
+ }
22
+ };
36
23
 
37
- router.get('/me', async (req: Request, res: Response) => {
38
- const token = req.headers.authorization?.split(' ')[1];
39
- if (!token) return res.status(401).json({ error: 'No token' });
40
-
41
- const user = await authService.validateToken(token);
42
- if (!user) return res.status(401).json({ error: 'Invalid token' });
43
-
44
- res.json({ user: { id: user.id, email: user.email, name: user.name } });
45
- });
46
-
47
- return router;
48
- }
24
+ login = async (req: Request, res: Response, next: NextFunction) => {
25
+ try {
26
+ const { email, password } = req.body;
27
+ const result = await this.authService.login(email, password);
28
+ res.json(result);
29
+ } catch (error) {
30
+ next(error);
31
+ }
32
+ };
33
+ }
@@ -0,0 +1,24 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import { ZodError } from 'zod';
3
+
4
+ export function errorHandler(
5
+ err: Error,
6
+ req: Request,
7
+ res: Response,
8
+ next: NextFunction
9
+ ) {
10
+ console.error(err);
11
+
12
+ if (err instanceof ZodError) {
13
+ return res.status(400).json({
14
+ error: 'Validation Error',
15
+ details: err.format(),
16
+ });
17
+ }
18
+
19
+ if (err.message === 'User already exists' || err.message === 'Invalid credentials') {
20
+ return res.status(400).json({ error: err.message });
21
+ }
22
+
23
+ res.status(500).json({ error: 'Internal Server Error' });
24
+ }
@@ -0,0 +1,61 @@
1
+ import { User } from '../../../core/domain/entities/User';
2
+ import { IUserRepositoryPort } from '../../../core/ports/outbound/IUserRepositoryPort';
3
+ import { prisma } from './prisma';
4
+
5
+ export class PrismaUserAdapter implements IUserRepositoryPort {
6
+ async findById(id: string): Promise<User | null> {
7
+ const user = await prisma.user.findUnique({ where: { id } });
8
+ if (!user) return null;
9
+ return User.restore({
10
+ id: user.id,
11
+ email: user.email,
12
+ name: user.name,
13
+ password: user.password,
14
+ stripeCustomerId: user.stripeCustomerId,
15
+ });
16
+ }
17
+
18
+ async findByEmail(email: string): Promise<User | null> {
19
+ const user = await prisma.user.findUnique({ where: { email } });
20
+ if (!user) return null;
21
+ return User.restore({
22
+ id: user.id,
23
+ email: user.email,
24
+ name: user.name,
25
+ password: user.password,
26
+ stripeCustomerId: user.stripeCustomerId,
27
+ });
28
+ }
29
+
30
+ async save(user: User): Promise<User> {
31
+ const data = {
32
+ id: user.id,
33
+ email: user.email,
34
+ name: user.name,
35
+ password: user.password,
36
+ stripeCustomerId: user.stripeCustomerId,
37
+ };
38
+
39
+ const savedUser = await prisma.user.upsert({
40
+ where: { id: user.id || '' },
41
+ update: { ...data },
42
+ create: { ...data },
43
+ });
44
+
45
+ return User.restore({
46
+ id: savedUser.id,
47
+ email: savedUser.email,
48
+ name: savedUser.name,
49
+ password: savedUser.password,
50
+ stripeCustomerId: savedUser.stripeCustomerId,
51
+ });
52
+ }
53
+
54
+ async update(user: User): Promise<User> {
55
+ return this.save(user);
56
+ }
57
+
58
+ async delete(id: string): Promise<void> {
59
+ await prisma.user.delete({ where: { id } });
60
+ }
61
+ }
@@ -0,0 +1,5 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+
3
+ export const prisma = new PrismaClient({
4
+ log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
5
+ });
@@ -0,0 +1,27 @@
1
+ import dotenv from 'dotenv';
2
+ import { z } from 'zod';
3
+
4
+ dotenv.config();
5
+
6
+ const envSchema = z.object({
7
+ NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
8
+ PORT: z.string().default('3000'),
9
+ DATABASE_URL: z.string(),
10
+ JWT_SECRET: z.string().min(10),
11
+ STRIPE_SECRET_KEY: z.string().optional(),
12
+ });
13
+
14
+ const _env = envSchema.safeParse(process.env);
15
+
16
+ if (!_env.success) {
17
+ console.error('❌ Invalid environment variables:', _env.error.format());
18
+ throw new Error('Invalid environment variables');
19
+ }
20
+
21
+ export const config = {
22
+ env: _env.data.NODE_ENV,
23
+ port: parseInt(_env.data.PORT, 10),
24
+ databaseUrl: _env.data.DATABASE_URL,
25
+ jwtSecret: _env.data.JWT_SECRET,
26
+ stripeSecretKey: _env.data.STRIPE_SECRET_KEY,
27
+ };