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.
- package/package.json +1 -1
- package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/persistence/PostgresUserRepository.java.hbs +40 -0
- package/templates/java-spring/clean/src/main/resources/application.properties.hbs +18 -0
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{infrastructure/web/controller → adapters/inbound/web}/AuthController.java.hbs +4 -5
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/persistence/JpaUserAdapter.java.hbs +40 -0
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/persistence/entity/UserEntity.java.hbs +61 -0
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/persistence/repository/JpaUserRepository.java.hbs +11 -0
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{infrastructure/security/SecurityAdapters.java.hbs → adapters/outbound/security/SecurityAdapter.java.hbs} +14 -14
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{domain/entity → core/domain}/User.java.hbs +2 -2
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{domain/usecase → core/ports/inbound}/LoginUserUseCase.java.hbs +8 -8
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{domain/usecase → core/ports/inbound}/RegisterUserUseCase.java.hbs +7 -8
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{domain/repository → core/ports/outbound}/UserRepository.java.hbs +4 -4
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{application → core}/service/AuthService.java.hbs +9 -9
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{{projectNamePascalCase}}Application.java.hbs +2 -2
- package/templates/java-spring/hexagonal/src/main/resources/application.properties.hbs +18 -0
- package/templates/nestjs/clean/package.json.hbs +9 -3
- package/templates/nestjs/clean/prisma/schema.prisma.hbs +20 -0
- package/templates/nestjs/clean/src/app.module.ts.hbs +17 -0
- package/templates/nestjs/clean/src/auth.module.ts.hbs +12 -10
- package/templates/nestjs/clean/src/infrastructure/database/prisma.service.ts.hbs +13 -0
- package/templates/nestjs/clean/src/infrastructure/database/repositories/prisma.user.repository.ts.hbs +32 -0
- package/templates/nestjs/clean/src/main.ts.hbs +11 -0
- package/templates/nestjs/hexagonal/package.json.hbs +9 -3
- package/templates/nestjs/hexagonal/prisma/schema.prisma +20 -0
- package/templates/nestjs/hexagonal/src/adapters/outbound/persistence/prisma.service.ts.hbs +13 -0
- package/templates/nestjs/hexagonal/src/adapters/outbound/persistence/prisma.user.adapter.ts.hbs +32 -0
- package/templates/nestjs/hexagonal/src/app.module.ts.hbs +17 -0
- package/templates/nestjs/hexagonal/src/auth.module.ts.hbs +15 -13
- package/templates/nestjs/hexagonal/src/main.ts.hbs +11 -0
- package/templates/nextjs/mvc/package.json.hbs +35 -32
- package/templates/nextjs/mvc/prisma/schema.prisma.hbs +12 -9
- package/templates/nextjs/mvc/src/lib/db.ts +15 -0
- package/templates/nodejs-express/clean/docker-compose.yml.hbs +5 -6
- package/templates/nodejs-express/clean/package.json.hbs +14 -8
- package/templates/nodejs-express/clean/prisma/schema.prisma +20 -0
- package/templates/nodejs-express/clean/src/config/index.ts +27 -0
- package/templates/nodejs-express/clean/src/index.ts.hbs +20 -24
- package/templates/nodejs-express/clean/src/infrastructure/database/PrismaUserRepository.ts.hbs +61 -0
- package/templates/nodejs-express/clean/src/infrastructure/database/prisma.ts.hbs +5 -0
- package/templates/nodejs-express/clean/src/infrastructure/http/controllers/AuthController.ts.hbs +24 -40
- package/templates/nodejs-express/clean/src/infrastructure/http/middlewares/errorHandler.ts +24 -0
- package/templates/nodejs-express/clean/tsconfig.json.hbs +8 -17
- package/templates/nodejs-express/hexagonal/docker-compose.yml.hbs +5 -6
- package/templates/nodejs-express/hexagonal/package.json.hbs +14 -8
- package/templates/nodejs-express/hexagonal/prisma/schema.prisma +20 -0
- package/templates/nodejs-express/hexagonal/src/adapters/inbound/http/AuthController.ts.hbs +29 -44
- package/templates/nodejs-express/hexagonal/src/adapters/inbound/http/middlewares/errorHandler.ts +24 -0
- package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/PrismaUserAdapter.ts.hbs +61 -0
- package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/prisma.ts +5 -0
- package/templates/nodejs-express/hexagonal/src/config/index.ts +27 -0
- package/templates/nodejs-express/hexagonal/src/index.ts.hbs +24 -27
- package/templates/nodejs-express/hexagonal/tsconfig.json.hbs +8 -17
- package/templates/python-fastapi/clean/app/application/services/__init__.py +0 -0
- package/templates/python-fastapi/clean/app/application/services/user_service.py.hbs +20 -0
- package/templates/python-fastapi/clean/app/config.py.hbs +24 -0
- package/templates/python-fastapi/clean/app/infrastructure/database/models.py.hbs +24 -0
- package/templates/python-fastapi/clean/app/infrastructure/database/postgres_repository.py.hbs +62 -0
- package/templates/python-fastapi/clean/app/infrastructure/database/session.py.hbs +27 -0
- package/templates/python-fastapi/clean/app/infrastructure/http/auth_controller.py.hbs +14 -8
- package/templates/python-fastapi/clean/app/main.py.hbs +25 -3
- package/templates/python-fastapi/clean/requirements.txt.hbs +3 -1
- package/templates/python-fastapi/hexagonal/app/adapters/inbound/http_adapter.py.hbs +41 -17
- package/templates/python-fastapi/hexagonal/app/adapters/outbound/postgres_user_repository.py.hbs +50 -0
- package/templates/python-fastapi/hexagonal/app/config.py.hbs +20 -0
- package/templates/python-fastapi/hexagonal/app/infrastructure/database/models.py.hbs +24 -0
- package/templates/python-fastapi/hexagonal/app/infrastructure/database/session.py.hbs +20 -0
- package/templates/python-fastapi/hexagonal/app/main.py.hbs +22 -14
- package/templates/python-fastapi/hexagonal/requirements.txt.hbs +3 -1
- package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/persistence/InMemoryUserRepository.java.hbs +0 -41
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/infrastructure/persistence/InMemoryUserRepository.java.hbs +0 -41
- package/templates/nestjs/clean/src/infrastructure/database/in-memory.repository.ts.hbs +0 -17
- package/templates/nodejs-express/clean/src/infrastructure/database/InMemoryUserRepository.ts.hbs +0 -46
- package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/InMemoryUserAdapter.ts.hbs +0 -38
- /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
|
+
}
|
package/templates/nestjs/hexagonal/src/adapters/outbound/persistence/prisma.user.adapter.ts.hbs
ADDED
|
@@ -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 {
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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": "^
|
|
17
|
-
"react-dom": "^
|
|
18
|
-
"@prisma/client": "^5.
|
|
19
|
-
"next-auth": "^4.24.
|
|
20
|
-
"stripe": "^14.14.0"
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
"@types/
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
4
|
+
db:
|
|
5
5
|
image: postgres:15-alpine
|
|
6
|
-
|
|
6
|
+
restart: always
|
|
7
7
|
environment:
|
|
8
8
|
POSTGRES_USER: postgres
|
|
9
|
-
POSTGRES_PASSWORD:
|
|
10
|
-
POSTGRES_DB: {{
|
|
9
|
+
POSTGRES_PASSWORD: password
|
|
10
|
+
POSTGRES_DB: {{kebabCase projectName}}_db
|
|
11
11
|
ports:
|
|
12
|
-
-
|
|
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
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
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
|
|
6
|
-
|
|
7
|
-
import
|
|
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
|
-
//
|
|
14
|
-
app.use(
|
|
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
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
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.
|
|
24
|
+
app.post('/api/auth/register', authController.register);
|
|
25
|
+
app.post('/api/auth/login', authController.login);
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
31
|
+
// Error Handler
|
|
32
|
+
app.use(errorHandler);
|
|
35
33
|
|
|
36
|
-
app.listen(
|
|
37
|
-
|
|
38
|
-
|
|
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
|
+
});
|
package/templates/nodejs-express/clean/src/infrastructure/database/PrismaUserRepository.ts.hbs
ADDED
|
@@ -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
|
+
}
|
package/templates/nodejs-express/clean/src/infrastructure/http/controllers/AuthController.ts.hbs
CHANGED
|
@@ -1,45 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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": "
|
|
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
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"node"
|
|
18
|
-
]
|
|
12
|
+
"experimentalDecorators": true,
|
|
13
|
+
"emitDecoratorMetadata": true,
|
|
14
|
+
"resolveJsonModule": true
|
|
19
15
|
},
|
|
20
|
-
"include": [
|
|
21
|
-
|
|
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
|
-
|
|
4
|
+
db:
|
|
5
5
|
image: postgres:15-alpine
|
|
6
|
-
|
|
6
|
+
restart: always
|
|
7
7
|
environment:
|
|
8
8
|
POSTGRES_USER: postgres
|
|
9
|
-
POSTGRES_PASSWORD:
|
|
10
|
-
POSTGRES_DB: {{
|
|
9
|
+
POSTGRES_PASSWORD: password
|
|
10
|
+
POSTGRES_DB: {{kebabCase projectName}}_db
|
|
11
11
|
ports:
|
|
12
|
-
-
|
|
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:
|