kybernus 2.0.10 → 2.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 (74) hide show
  1. package/package.json +1 -1
  2. package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/persistence/PostgresUserRepository.java.hbs +40 -0
  3. package/templates/java-spring/clean/src/main/resources/application.properties.hbs +18 -0
  4. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{infrastructure/web/controller → adapters/inbound/web}/AuthController.java.hbs +4 -5
  5. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/persistence/JpaUserAdapter.java.hbs +40 -0
  6. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/persistence/entity/UserEntity.java.hbs +61 -0
  7. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/persistence/repository/JpaUserRepository.java.hbs +11 -0
  8. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{infrastructure/security/SecurityAdapters.java.hbs → adapters/outbound/security/SecurityAdapter.java.hbs} +14 -14
  9. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{domain/entity → core/domain}/User.java.hbs +2 -2
  10. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{domain/usecase → core/ports/inbound}/LoginUserUseCase.java.hbs +8 -8
  11. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{domain/usecase → core/ports/inbound}/RegisterUserUseCase.java.hbs +7 -8
  12. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{domain/repository → core/ports/outbound}/UserRepository.java.hbs +4 -4
  13. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{application → core}/service/AuthService.java.hbs +9 -9
  14. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{{projectNamePascalCase}}Application.java.hbs +2 -2
  15. package/templates/java-spring/hexagonal/src/main/resources/application.properties.hbs +18 -0
  16. package/templates/nestjs/clean/package.json.hbs +9 -3
  17. package/templates/nestjs/clean/prisma/schema.prisma.hbs +20 -0
  18. package/templates/nestjs/clean/src/app.module.ts.hbs +17 -0
  19. package/templates/nestjs/clean/src/auth.module.ts.hbs +12 -10
  20. package/templates/nestjs/clean/src/infrastructure/database/prisma.service.ts.hbs +13 -0
  21. package/templates/nestjs/clean/src/infrastructure/database/repositories/prisma.user.repository.ts.hbs +32 -0
  22. package/templates/nestjs/clean/src/main.ts.hbs +11 -0
  23. package/templates/nestjs/hexagonal/package.json.hbs +9 -3
  24. package/templates/nestjs/hexagonal/prisma/schema.prisma +20 -0
  25. package/templates/nestjs/hexagonal/src/adapters/outbound/persistence/prisma.service.ts.hbs +13 -0
  26. package/templates/nestjs/hexagonal/src/adapters/outbound/persistence/prisma.user.adapter.ts.hbs +32 -0
  27. package/templates/nestjs/hexagonal/src/app.module.ts.hbs +17 -0
  28. package/templates/nestjs/hexagonal/src/auth.module.ts.hbs +15 -13
  29. package/templates/nestjs/hexagonal/src/main.ts.hbs +11 -0
  30. package/templates/nextjs/mvc/package.json.hbs +35 -32
  31. package/templates/nextjs/mvc/prisma/schema.prisma.hbs +12 -9
  32. package/templates/nextjs/mvc/src/lib/db.ts +15 -0
  33. package/templates/nodejs-express/clean/docker-compose.yml.hbs +5 -6
  34. package/templates/nodejs-express/clean/package.json.hbs +14 -8
  35. package/templates/nodejs-express/clean/prisma/schema.prisma +20 -0
  36. package/templates/nodejs-express/clean/src/config/index.ts +27 -0
  37. package/templates/nodejs-express/clean/src/index.ts.hbs +20 -24
  38. package/templates/nodejs-express/clean/src/infrastructure/database/PrismaUserRepository.ts.hbs +61 -0
  39. package/templates/nodejs-express/clean/src/infrastructure/database/prisma.ts.hbs +5 -0
  40. package/templates/nodejs-express/clean/src/infrastructure/http/controllers/AuthController.ts.hbs +24 -40
  41. package/templates/nodejs-express/clean/src/infrastructure/http/middlewares/errorHandler.ts +24 -0
  42. package/templates/nodejs-express/clean/tsconfig.json.hbs +8 -17
  43. package/templates/nodejs-express/hexagonal/docker-compose.yml.hbs +5 -6
  44. package/templates/nodejs-express/hexagonal/package.json.hbs +14 -8
  45. package/templates/nodejs-express/hexagonal/prisma/schema.prisma +20 -0
  46. package/templates/nodejs-express/hexagonal/src/adapters/inbound/http/AuthController.ts.hbs +29 -44
  47. package/templates/nodejs-express/hexagonal/src/adapters/inbound/http/middlewares/errorHandler.ts +24 -0
  48. package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/PrismaUserAdapter.ts.hbs +61 -0
  49. package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/prisma.ts +5 -0
  50. package/templates/nodejs-express/hexagonal/src/config/index.ts +27 -0
  51. package/templates/nodejs-express/hexagonal/src/index.ts.hbs +24 -27
  52. package/templates/nodejs-express/hexagonal/tsconfig.json.hbs +8 -17
  53. package/templates/python-fastapi/clean/app/application/services/__init__.py +0 -0
  54. package/templates/python-fastapi/clean/app/application/services/user_service.py.hbs +20 -0
  55. package/templates/python-fastapi/clean/app/config.py.hbs +24 -0
  56. package/templates/python-fastapi/clean/app/infrastructure/database/models.py.hbs +24 -0
  57. package/templates/python-fastapi/clean/app/infrastructure/database/postgres_repository.py.hbs +62 -0
  58. package/templates/python-fastapi/clean/app/infrastructure/database/session.py.hbs +27 -0
  59. package/templates/python-fastapi/clean/app/infrastructure/http/auth_controller.py.hbs +14 -8
  60. package/templates/python-fastapi/clean/app/main.py.hbs +25 -3
  61. package/templates/python-fastapi/clean/requirements.txt.hbs +3 -1
  62. package/templates/python-fastapi/hexagonal/app/adapters/inbound/http_adapter.py.hbs +41 -17
  63. package/templates/python-fastapi/hexagonal/app/adapters/outbound/postgres_user_repository.py.hbs +50 -0
  64. package/templates/python-fastapi/hexagonal/app/config.py.hbs +20 -0
  65. package/templates/python-fastapi/hexagonal/app/infrastructure/database/models.py.hbs +24 -0
  66. package/templates/python-fastapi/hexagonal/app/infrastructure/database/session.py.hbs +20 -0
  67. package/templates/python-fastapi/hexagonal/app/main.py.hbs +22 -14
  68. package/templates/python-fastapi/hexagonal/requirements.txt.hbs +3 -1
  69. package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/persistence/InMemoryUserRepository.java.hbs +0 -41
  70. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/infrastructure/persistence/InMemoryUserRepository.java.hbs +0 -41
  71. package/templates/nestjs/clean/src/infrastructure/database/in-memory.repository.ts.hbs +0 -17
  72. package/templates/nodejs-express/clean/src/infrastructure/database/InMemoryUserRepository.ts.hbs +0 -46
  73. package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/InMemoryUserAdapter.ts.hbs +0 -38
  74. /package/templates/python-fastapi/hexagonal/app/core/{ports.py.hbs → ports/ports.py.hbs} +0 -0
@@ -0,0 +1,13 @@
1
+ import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
2
+ import { PrismaClient } from '@prisma/client';
3
+
4
+ @Injectable()
5
+ export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
6
+ async onModuleInit() {
7
+ await this.$connect();
8
+ }
9
+
10
+ async onModuleDestroy() {
11
+ await this.$disconnect();
12
+ }
13
+ }
@@ -0,0 +1,32 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { IUserRepositoryPort } from '../../../../core/ports/ports';
3
+ import { User } from '../../../../core/domain/user.entity';
4
+ import { PrismaService } from './prisma.service';
5
+
6
+ @Injectable()
7
+ export class PrismaUserAdapter implements IUserRepositoryPort {
8
+ constructor(private prisma: PrismaService) {}
9
+
10
+ async findByEmail(email: string): Promise<User | null> {
11
+ const user = await this.prisma.user.findUnique({ where: { email } });
12
+ if (!user) return null;
13
+ return new User(user.id, user.name, user.email, user.password);
14
+ }
15
+
16
+ async save(user: User): Promise<User> {
17
+ const savedUser = await this.prisma.user.upsert({
18
+ where: { email: user.email },
19
+ update: {
20
+ name: user.name,
21
+ password: user.password,
22
+ },
23
+ create: {
24
+ id: user.id,
25
+ name: user.name,
26
+ email: user.email,
27
+ password: user.password,
28
+ },
29
+ });
30
+ return new User(savedUser.id, savedUser.name, savedUser.email, savedUser.password);
31
+ }
32
+ }
@@ -0,0 +1,17 @@
1
+ import { Module } from '@nestjs/common';
2
+ import { ConfigModule } from '@nestjs/config';
3
+ import { AuthModule } from './auth.module';
4
+ import { PrismaService } from './adapters/outbound/persistence/prisma.service';
5
+
6
+ @Module({
7
+ imports: [
8
+ ConfigModule.forRoot({
9
+ isGlobal: true,
10
+ }),
11
+ AuthModule,
12
+ ],
13
+ controllers: [],
14
+ providers: [PrismaService],
15
+ exports: [PrismaService],
16
+ })
17
+ export class AppModule {}
@@ -1,19 +1,21 @@
1
1
  import { Module } from '@nestjs/common';
2
2
  import { AuthService } from './core/auth.service';
3
3
  import { HttpAdapter } from './adapters/inbound/http.adapter';
4
- import { PersistenceAdapter } from './adapters/outbound/persistence.adapter';
4
+ import { PrismaUserAdapter } from './adapters/outbound/persistence/prisma.user.adapter';
5
+ import { PrismaService } from './adapters/outbound/persistence/prisma.service';
5
6
 
6
7
  @Module({
7
- providers: [
8
- {
9
- provide: 'IAuthPort',
10
- useClass: AuthService,
11
- },
12
- {
13
- provide: 'IUserRepositoryPort',
14
- useClass: PersistenceAdapter,
15
- },
16
- ],
17
- controllers: [HttpAdapter],
8
+ providers: [
9
+ PrismaService,
10
+ {
11
+ provide: 'IAuthPort',
12
+ useClass: AuthService,
13
+ },
14
+ {
15
+ provide: 'IUserRepositoryPort',
16
+ useClass: PrismaUserAdapter,
17
+ },
18
+ ],
19
+ controllers: [HttpAdapter],
18
20
  })
19
- export class AuthModule {}
21
+ export class AuthModule {}
@@ -0,0 +1,11 @@
1
+ import { NestFactory } from '@nestjs/core';
2
+ import { AppModule } from './app.module';
3
+ import { ValidationPipe } from '@nestjs/common';
4
+
5
+ async function bootstrap() {
6
+ const app = await NestFactory.create(AppModule);
7
+ app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
8
+ app.enableCors();
9
+ await app.listen(process.env.PORT || 3000);
10
+ }
11
+ bootstrap();
@@ -1,34 +1,37 @@
1
1
  {
2
- "name": "{{kebabCase projectName}}",
3
- "version": "1.0.0",
4
- "private": true,
5
- "scripts": {
6
- "dev": "next dev",
7
- "build": "next build",
8
- "start": "next start",
9
- "lint": "next lint",
10
- "db:generate": "prisma generate",
11
- "db:push": "prisma db push",
12
- "db:studio": "prisma studio"
13
- },
14
- "dependencies": {
15
- "next": "^16.0.0",
16
- "react": "^19.0.0",
17
- "react-dom": "^19.0.0",
18
- "@prisma/client": "^5.9.0",
19
- "next-auth": "^4.24.5",
20
- "stripe": "^14.14.0"
21
- },
22
- "devDependencies": {
23
- "@types/node": "^20.11.0",
24
- "@types/react": "^19.0.0",
25
- "@types/react-dom": "^19.0.0",
26
- "autoprefixer": "^10.4.17",
27
- "eslint": "^9.0.0",
28
- "eslint-config-next": "^16.0.0",
29
- "postcss": "^8.4.33",
30
- "prisma": "^5.9.0",
31
- "tailwindcss": "^3.4.1",
32
- "typescript": "^5.3.3"
2
+ "name": "{{kebabCase projectName}}",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "next lint",
10
+ "db:generate": "prisma generate",
11
+ "db:push": "prisma db push",
12
+ "db:studio": "prisma studio"
13
+ },
14
+ "dependencies": {
15
+ "next": "^16.0.0",
16
+ "react": "^18.0.0",
17
+ "react-dom": "^18.0.0",
18
+ "@prisma/client": "^5.10.2",
19
+ "next-auth": "^4.24.6",
20
+ "stripe": "^14.14.0",
21
+ "zod": "^3.22.4",
22
+ "bcryptjs": "^2.4.3"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "^20.11.19",
26
+ "@types/react": "^18.2.57",
27
+ "@types/react-dom": "^18.2.19",
28
+ "@types/bcryptjs": "^2.4.6",
29
+ "autoprefixer": "^10.4.17",
30
+ "eslint": "^8.56.0",
31
+ "eslint-config-next": "14.1.0",
32
+ "postcss": "^8.4.33",
33
+ "prisma": "^5.10.2",
34
+ "tailwindcss": "^3.4.1",
35
+ "typescript": "^5.3.3"
36
+ }
33
37
  }
34
- }
@@ -7,12 +7,15 @@ datasource db {
7
7
  url = env("DATABASE_URL")
8
8
  }
9
9
 
10
- // Add your models here
11
- // Example:
12
- // model User {
13
- // id Int @id @default(autoincrement())
14
- // email String @unique
15
- // name String?
16
- // createdAt DateTime @default(now())
17
- // updatedAt DateTime @updatedAt
18
- // }
10
+ model User {
11
+ id String @id @default(uuid())
12
+ email String @unique
13
+ name String?
14
+ password String? // Optional for OAuth, required for credentials
15
+ stripeCustomerId String? @map("stripe_customer_id")
16
+ image String?
17
+ createdAt DateTime @default(now()) @map("created_at")
18
+ updatedAt DateTime @updatedAt @map("updated_at")
19
+
20
+ @@map("users")
21
+ }
@@ -0,0 +1,15 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+
3
+ const prismaClientSingleton = () => {
4
+ return new PrismaClient();
5
+ };
6
+
7
+ type PrismaClientSingleton = ReturnType<typeof prismaClientSingleton>;
8
+
9
+ const globalForPrisma = globalThis as unknown as {
10
+ prisma: PrismaClientSingleton | undefined;
11
+ };
12
+
13
+ export const db = globalForPrisma.prisma ?? prismaClientSingleton();
14
+
15
+ if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db;
@@ -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
+ "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: