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
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
from fastapi import APIRouter, HTTPException
|
|
1
|
+
from fastapi import APIRouter, HTTPException, Depends
|
|
2
2
|
from pydantic import BaseModel, EmailStr
|
|
3
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
3
4
|
from app.core.ports import IAuthPort
|
|
5
|
+
from app.core.domain.user import User as DomainUser
|
|
6
|
+
from app.infrastructure.database.session import get_db
|
|
7
|
+
from app.infrastructure.database.models import UserModel
|
|
8
|
+
from app.adapters.outbound.postgres_user_repository import PostgresUserRepository
|
|
9
|
+
from app.core.service import AuthService
|
|
10
|
+
from app.infrastructure.security.adapters import BcryptHasher, JwtTokenGenerator
|
|
4
11
|
|
|
5
12
|
router = APIRouter()
|
|
6
13
|
|
|
@@ -9,21 +16,38 @@ class RegisterRequest(BaseModel):
|
|
|
9
16
|
name: str
|
|
10
17
|
password: str
|
|
11
18
|
|
|
19
|
+
# Dependency Injection Factory
|
|
20
|
+
def get_auth_service(db: AsyncSession = Depends(get_db)) -> IAuthPort:
|
|
21
|
+
repo = PostgresUserRepository(db)
|
|
22
|
+
hasher = BcryptHasher()
|
|
23
|
+
token_gen = JwtTokenGenerator()
|
|
24
|
+
return AuthService(repo, hasher, token_gen)
|
|
25
|
+
|
|
26
|
+
@router.post("/register")
|
|
27
|
+
async def register(
|
|
28
|
+
req: RegisterRequest,
|
|
29
|
+
service: IAuthPort = Depends(get_auth_service)
|
|
30
|
+
):
|
|
31
|
+
try:
|
|
32
|
+
result = await service.register(req.email, req.name, req.password)
|
|
33
|
+
return result
|
|
34
|
+
except ValueError as e:
|
|
35
|
+
raise HTTPException(status_code=400, detail=str(e))
|
|
36
|
+
|
|
37
|
+
@router.post("/login")
|
|
38
|
+
async def login(
|
|
39
|
+
req: RegisterRequest,
|
|
40
|
+
service: IAuthPort = Depends(get_auth_service)
|
|
41
|
+
):
|
|
42
|
+
try:
|
|
43
|
+
result = await service.login(req.email, req.password)
|
|
44
|
+
return result
|
|
45
|
+
except ValueError as e:
|
|
46
|
+
raise HTTPException(status_code=401, detail=str(e))
|
|
47
|
+
|
|
48
|
+
# Setup function for backward compatibility or direct usage
|
|
49
|
+
# though router is preferred
|
|
12
50
|
def setup_auth_routes(auth_service: IAuthPort):
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
try:
|
|
16
|
-
result = await auth_service.register(req.email, req.name, req.password)
|
|
17
|
-
return result
|
|
18
|
-
except ValueError as e:
|
|
19
|
-
raise HTTPException(status_code=400, detail=str(e))
|
|
20
|
-
|
|
21
|
-
@router.post("/login")
|
|
22
|
-
async def login(req: RegisterRequest):
|
|
23
|
-
try:
|
|
24
|
-
result = await auth_service.login(req.email, req.password)
|
|
25
|
-
return result
|
|
26
|
-
except ValueError as e:
|
|
27
|
-
raise HTTPException(status_code=401, detail=str(e))
|
|
28
|
-
|
|
51
|
+
# This pattern is tricky with FastAPIs dependency injection system
|
|
52
|
+
# It's better to rely on Depends() as implemented above
|
|
29
53
|
return router
|
package/templates/python-fastapi/hexagonal/app/adapters/outbound/postgres_user_repository.py.hbs
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
3
|
+
from sqlalchemy import select
|
|
4
|
+
from app.core.ports import IUserRepositoryPort
|
|
5
|
+
from app.core.domain.user import User
|
|
6
|
+
from app.infrastructure.database.models import UserModel
|
|
7
|
+
|
|
8
|
+
class PostgresUserRepository(IUserRepositoryPort):
|
|
9
|
+
def __init__(self, session: AsyncSession):
|
|
10
|
+
self.session = session
|
|
11
|
+
|
|
12
|
+
def _to_entity(self, model: UserModel) -> User:
|
|
13
|
+
return User(
|
|
14
|
+
id=model.id,
|
|
15
|
+
email=model.email,
|
|
16
|
+
name=model.name,
|
|
17
|
+
password=model.password,
|
|
18
|
+
stripe_customer_id=model.stripe_customer_id,
|
|
19
|
+
created_at=model.created_at
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
async def find_by_email(self, email: str) -> Optional[User]:
|
|
23
|
+
result = await self.session.execute(select(UserModel).where(UserModel.email == email))
|
|
24
|
+
model = result.scalars().first()
|
|
25
|
+
return self._to_entity(model) if model else None
|
|
26
|
+
|
|
27
|
+
async def save(self, user: User) -> User:
|
|
28
|
+
# Simplistic save/update logic
|
|
29
|
+
result = await self.session.execute(select(UserModel).where(UserModel.id == user.id))
|
|
30
|
+
model = result.scalars().first()
|
|
31
|
+
|
|
32
|
+
if model:
|
|
33
|
+
model.email = user.email
|
|
34
|
+
model.name = user.name
|
|
35
|
+
model.password = user.password
|
|
36
|
+
model.stripe_customer_id = user.stripe_customer_id
|
|
37
|
+
else:
|
|
38
|
+
model = UserModel(
|
|
39
|
+
id=user.id,
|
|
40
|
+
email=user.email,
|
|
41
|
+
name=user.name,
|
|
42
|
+
password=user.password,
|
|
43
|
+
stripe_customer_id=user.stripe_customer_id,
|
|
44
|
+
created_at=user.created_at
|
|
45
|
+
)
|
|
46
|
+
self.session.add(model)
|
|
47
|
+
|
|
48
|
+
await self.session.commit()
|
|
49
|
+
await self.session.refresh(model)
|
|
50
|
+
return self._to_entity(model)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
2
|
+
from functools import lru_cache
|
|
3
|
+
|
|
4
|
+
class Settings(BaseSettings):
|
|
5
|
+
PROJECT_NAME: str = "{{projectName}}"
|
|
6
|
+
API_V1_STR: str = "/api/v1"
|
|
7
|
+
|
|
8
|
+
# Database
|
|
9
|
+
DATABASE_URL: str = "postgresql+asyncpg://postgres:postgres@localhost:5432/{{projectName}}_db"
|
|
10
|
+
|
|
11
|
+
# Security
|
|
12
|
+
SECRET_KEY: str = "change_this_to_a_secure_random_key"
|
|
13
|
+
ALGORITHM: str = "HS256"
|
|
14
|
+
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
|
|
15
|
+
|
|
16
|
+
model_config = SettingsConfigDict(env_file=".env", case_sensitive=True)
|
|
17
|
+
|
|
18
|
+
@lru_cache
|
|
19
|
+
def get_settings():
|
|
20
|
+
return Settings()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from sqlalchemy import Column, String, DateTime, Boolean
|
|
2
|
+
from sqlalchemy.sql import func
|
|
3
|
+
from app.infrastructure.database.session import Base
|
|
4
|
+
import uuid
|
|
5
|
+
|
|
6
|
+
class UserModel(Base):
|
|
7
|
+
__tablename__ = "users"
|
|
8
|
+
|
|
9
|
+
id = Column(String, primary_key=True, index=True, default=lambda: str(uuid.uuid4()))
|
|
10
|
+
email = Column(String, unique=True, index=True, nullable=False)
|
|
11
|
+
name = Column(String, nullable=False)
|
|
12
|
+
password = Column(String, nullable=False)
|
|
13
|
+
stripe_customer_id = Column(String, nullable=True)
|
|
14
|
+
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
15
|
+
is_active = Column(Boolean, default=True)
|
|
16
|
+
|
|
17
|
+
def to_dict(self):
|
|
18
|
+
return {
|
|
19
|
+
"id": self.id,
|
|
20
|
+
"email": self.email,
|
|
21
|
+
"name": self.name,
|
|
22
|
+
"stripe_customer_id": self.stripe_customer_id,
|
|
23
|
+
"created_at": self.created_at
|
|
24
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
|
|
2
|
+
from sqlalchemy.orm import DeclarativeBase
|
|
3
|
+
from app.config import get_settings
|
|
4
|
+
|
|
5
|
+
settings = get_settings()
|
|
6
|
+
|
|
7
|
+
engine = create_async_engine(
|
|
8
|
+
settings.DATABASE_URL,
|
|
9
|
+
echo=True,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
AsyncSessionLocal = async_sessionmaker(
|
|
13
|
+
bind=engine,
|
|
14
|
+
class_=AsyncSession,
|
|
15
|
+
expire_on_commit=False,
|
|
16
|
+
autoflush=False,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
class Base(DeclarativeBase):
|
|
20
|
+
pass
|
|
@@ -1,22 +1,30 @@
|
|
|
1
|
+
from contextlib import asynccontextmanager
|
|
1
2
|
from fastapi import FastAPI
|
|
2
|
-
from app.
|
|
3
|
-
from app.
|
|
3
|
+
from app.adapters.inbound.http_adapter import router as auth_router
|
|
4
|
+
from app.infrastructure.database.session import engine, Base
|
|
5
|
+
from app.config import get_settings
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
settings = get_settings()
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
@asynccontextmanager
|
|
10
|
+
async def lifespan(app: FastAPI):
|
|
11
|
+
# Create tables on startup (for development)
|
|
12
|
+
async with engine.begin() as conn:
|
|
13
|
+
await conn.run_sync(Base.metadata.create_all)
|
|
14
|
+
yield
|
|
15
|
+
await engine.dispose()
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
app = FastAPI(
|
|
18
|
+
title="{{projectName}} - Hexagonal Architecture",
|
|
19
|
+
lifespan=lifespan
|
|
20
|
+
)
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
auth_router = setup_auth_routes(auth_service)
|
|
18
|
-
app.include_router(auth_router, prefix="/api/auth", tags=["Auth"])
|
|
22
|
+
app.include_router(auth_router, prefix=settings.API_V1_STR + "/auth", tags=["Auth"])
|
|
19
23
|
|
|
20
24
|
@app.get("/health")
|
|
21
25
|
def health():
|
|
22
|
-
return {
|
|
26
|
+
return {
|
|
27
|
+
"status": "ok",
|
|
28
|
+
"architecture": "hexagonal",
|
|
29
|
+
"project": settings.PROJECT_NAME
|
|
30
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
fastapi>=0.109.0
|
|
2
2
|
uvicorn[standard]>=0.27.0
|
|
3
3
|
pydantic>=2.5.0
|
|
4
|
+
pydantic-settings>=2.1.0
|
|
4
5
|
python-dotenv>=1.0.0
|
|
5
6
|
python-jose[cryptography]>=3.3.0
|
|
6
7
|
passlib[bcrypt]>=1.7.4
|
|
7
8
|
stripe>=8.0.0
|
|
8
9
|
sqlalchemy>=2.0.0
|
|
9
10
|
alembic>=1.13.0
|
|
11
|
+
asyncpg>=0.29.0
|
|
10
12
|
psycopg2-binary>=2.9.9
|
|
11
13
|
pytest>=8.0.0
|
|
12
|
-
httpx>=0.26.0
|
|
14
|
+
httpx>=0.26.0
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
package {{packageName}}.infrastructure.persistence;
|
|
2
|
-
|
|
3
|
-
import {{packageName}}.domain.entity.User;
|
|
4
|
-
import {{packageName}}.domain.repository.UserRepository;
|
|
5
|
-
import org.springframework.stereotype.Repository;
|
|
6
|
-
|
|
7
|
-
import java.util.Map;
|
|
8
|
-
import java.util.Optional;
|
|
9
|
-
import java.util.concurrent.ConcurrentHashMap;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* In-Memory User Repository - Infrastructure Layer
|
|
13
|
-
* Replace with JPA implementation for production
|
|
14
|
-
*/
|
|
15
|
-
@Repository
|
|
16
|
-
public class InMemoryUserRepository implements UserRepository {
|
|
17
|
-
private final Map<String, User> users = new ConcurrentHashMap<>();
|
|
18
|
-
|
|
19
|
-
@Override
|
|
20
|
-
public Optional<User> findById(String id) {
|
|
21
|
-
return Optional.ofNullable(users.get(id));
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
@Override
|
|
25
|
-
public Optional<User> findByEmail(String email) {
|
|
26
|
-
return users.values().stream()
|
|
27
|
-
.filter(user -> user.getEmail().equals(email))
|
|
28
|
-
.findFirst();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
@Override
|
|
32
|
-
public User save(User user) {
|
|
33
|
-
users.put(user.getId(), user);
|
|
34
|
-
return user;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
@Override
|
|
38
|
-
public void delete(String id) {
|
|
39
|
-
users.remove(id);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
package {{packageName}}.infrastructure.persistence;
|
|
2
|
-
|
|
3
|
-
import {{packageName}}.domain.entity.User;
|
|
4
|
-
import {{packageName}}.domain.repository.UserRepository;
|
|
5
|
-
import org.springframework.stereotype.Repository;
|
|
6
|
-
|
|
7
|
-
import java.util.Map;
|
|
8
|
-
import java.util.Optional;
|
|
9
|
-
import java.util.concurrent.ConcurrentHashMap;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* In-Memory User Repository - Infrastructure Layer
|
|
13
|
-
* Replace with JPA implementation for production
|
|
14
|
-
*/
|
|
15
|
-
@Repository
|
|
16
|
-
public class InMemoryUserRepository implements UserRepository {
|
|
17
|
-
private final Map<String, User> users = new ConcurrentHashMap<>();
|
|
18
|
-
|
|
19
|
-
@Override
|
|
20
|
-
public Optional<User> findById(String id) {
|
|
21
|
-
return Optional.ofNullable(users.get(id));
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
@Override
|
|
25
|
-
public Optional<User> findByEmail(String email) {
|
|
26
|
-
return users.values().stream()
|
|
27
|
-
.filter(user -> user.getEmail().equals(email))
|
|
28
|
-
.findFirst();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
@Override
|
|
32
|
-
public User save(User user) {
|
|
33
|
-
users.put(user.getId(), user);
|
|
34
|
-
return user;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
@Override
|
|
38
|
-
public void delete(String id) {
|
|
39
|
-
users.remove(id);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { Injectable } from '@nestjs/common';
|
|
2
|
-
import { IUserRepository } from '../../domain/repositories/user.repository';
|
|
3
|
-
import { User } from '../../domain/entities/user.entity';
|
|
4
|
-
|
|
5
|
-
@Injectable()
|
|
6
|
-
export class InMemoryUserRepository extends IUserRepository {
|
|
7
|
-
private users: User[] = [];
|
|
8
|
-
|
|
9
|
-
async findByEmail(email: string): Promise<User | null> {
|
|
10
|
-
return this.users.find((u) => u.email === email) || null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
async save(user: User): Promise<User> {
|
|
14
|
-
this.users.push(user);
|
|
15
|
-
return user;
|
|
16
|
-
}
|
|
17
|
-
}
|
package/templates/nodejs-express/clean/src/infrastructure/database/InMemoryUserRepository.ts.hbs
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { User } from '../../domain/entities/User';
|
|
2
|
-
import { IUserRepository } from '../../domain/repositories/IUserRepository';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* In-Memory User Repository - Infrastructure Layer
|
|
6
|
-
* Replace with actual database implementation (Prisma, TypeORM, etc.)
|
|
7
|
-
*/
|
|
8
|
-
export class InMemoryUserRepository implements IUserRepository {
|
|
9
|
-
private users: Map<string, User> = new Map();
|
|
10
|
-
|
|
11
|
-
async findById(id: string): Promise<User | null> {
|
|
12
|
-
return this.users.get(id) || null;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async findByEmail(email: string): Promise<User | null> {
|
|
16
|
-
for (const user of this.users.values()) {
|
|
17
|
-
if (user.email === email) {
|
|
18
|
-
return user;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async save(user: User): Promise<User> {
|
|
25
|
-
const id = Date.now().toString();
|
|
26
|
-
const savedUser = User.restore({
|
|
27
|
-
id,
|
|
28
|
-
email: user.email,
|
|
29
|
-
name: user.name,
|
|
30
|
-
password: user.password,
|
|
31
|
-
stripeCustomerId: user.stripeCustomerId,
|
|
32
|
-
});
|
|
33
|
-
this.users.set(id, savedUser);
|
|
34
|
-
return savedUser;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async update(user: User): Promise<User> {
|
|
38
|
-
if (!user.id) throw new Error('User must have an ID to update');
|
|
39
|
-
this.users.set(user.id, user);
|
|
40
|
-
return user;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async delete(id: string): Promise<void> {
|
|
44
|
-
this.users.delete(id);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { User } from '../../core/domain/entities/User';
|
|
2
|
-
import { IUserRepositoryPort } from '../../core/ports/outbound/IUserRepositoryPort';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* In-Memory User Repository Adapter
|
|
6
|
-
* Implements the outbound port
|
|
7
|
-
*/
|
|
8
|
-
export class InMemoryUserAdapter implements IUserRepositoryPort {
|
|
9
|
-
private users: Map<string, User> = new Map();
|
|
10
|
-
|
|
11
|
-
async findById(id: string): Promise<User | null> {
|
|
12
|
-
return this.users.get(id) || null;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async findByEmail(email: string): Promise<User | null> {
|
|
16
|
-
for (const user of this.users.values()) {
|
|
17
|
-
if (user.email === email) return user;
|
|
18
|
-
}
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async save(user: User): Promise<User> {
|
|
23
|
-
const id = Date.now().toString();
|
|
24
|
-
const savedUser = user.withId(id);
|
|
25
|
-
this.users.set(id, savedUser);
|
|
26
|
-
return savedUser;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async update(user: User): Promise<User> {
|
|
30
|
-
if (!user.id) throw new Error('User must have an ID');
|
|
31
|
-
this.users.set(user.id, user);
|
|
32
|
-
return user;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async delete(id: string): Promise<void> {
|
|
36
|
-
this.users.delete(id);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
File without changes
|