feat(auth): add DB-backed login, token management, and idempotent auth migration
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
"""Authentication API routes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import NoReturn
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response, status
|
||||
|
||||
from app.core.auth import AuthContext, require_read
|
||||
from app.domain.models import AuthToken, User
|
||||
from app.schemas.auth import AuthTokenResponse, CreateClientTokenRequest, IssuedClientTokenResponse, LoginRequest, TokenResponse
|
||||
from app.schemas.user import UserResponse
|
||||
from app.services.auth_service import AuthService, IssuedClientToken, LoginResult, get_auth_service
|
||||
|
||||
router = APIRouter(prefix="/auth", tags=["auth"])
|
||||
|
||||
|
||||
def _to_user_response(user: User) -> UserResponse:
|
||||
return UserResponse(
|
||||
id=user.id,
|
||||
username=user.username,
|
||||
email=user.email,
|
||||
permission=user.permission,
|
||||
is_active=user.is_active,
|
||||
created_at=user.created_at,
|
||||
updated_at=user.updated_at,
|
||||
last_login_at=user.last_login_at,
|
||||
)
|
||||
|
||||
|
||||
def _to_auth_token_response(token: AuthToken) -> AuthTokenResponse:
|
||||
return AuthTokenResponse(
|
||||
id=token.id,
|
||||
user_id=token.user_id,
|
||||
name=token.name,
|
||||
token_prefix=token.token_prefix,
|
||||
permission=token.permission,
|
||||
token_type=token.token_type,
|
||||
expires_at=token.expires_at,
|
||||
last_used_at=token.last_used_at,
|
||||
revoked_at=token.revoked_at,
|
||||
created_at=token.created_at,
|
||||
)
|
||||
|
||||
|
||||
def _to_login_response(result: LoginResult) -> TokenResponse:
|
||||
return TokenResponse(
|
||||
access_token=result.token,
|
||||
expires_at=result.auth_token.expires_at,
|
||||
permission=result.auth_token.permission,
|
||||
user=_to_user_response(result.user),
|
||||
)
|
||||
|
||||
|
||||
def _to_issued_token_response(result: IssuedClientToken) -> IssuedClientTokenResponse:
|
||||
return IssuedClientTokenResponse(
|
||||
token=result.token,
|
||||
auth_token=_to_auth_token_response(result.auth_token),
|
||||
)
|
||||
|
||||
|
||||
def _raise_auth_error(exc: ValueError) -> NoReturn:
|
||||
detail = str(exc)
|
||||
if "잘못된 사용자명 또는 비밀번호" in detail:
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=detail) from exc
|
||||
if "초과" in detail:
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=detail) from exc
|
||||
if "찾을 수 없습니다" in detail:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=detail) from exc
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=detail) from exc
|
||||
|
||||
|
||||
@router.post("/login", response_model=TokenResponse)
|
||||
def login(payload: LoginRequest, service: AuthService = Depends(get_auth_service)) -> TokenResponse:
|
||||
try:
|
||||
return _to_login_response(service.login(payload))
|
||||
except ValueError as exc:
|
||||
_raise_auth_error(exc)
|
||||
|
||||
|
||||
@router.get("/tokens", response_model=list[AuthTokenResponse], dependencies=[Depends(require_read)])
|
||||
def list_tokens(
|
||||
auth_context: AuthContext = Depends(require_read),
|
||||
service: AuthService = Depends(get_auth_service),
|
||||
) -> list[AuthTokenResponse]:
|
||||
try:
|
||||
return [_to_auth_token_response(token) for token in service.list_tokens(auth_context)]
|
||||
except ValueError as exc:
|
||||
_raise_auth_error(exc)
|
||||
|
||||
|
||||
@router.post("/tokens", response_model=IssuedClientTokenResponse, status_code=status.HTTP_201_CREATED, dependencies=[Depends(require_read)])
|
||||
def create_token(
|
||||
payload: CreateClientTokenRequest,
|
||||
auth_context: AuthContext = Depends(require_read),
|
||||
service: AuthService = Depends(get_auth_service),
|
||||
) -> IssuedClientTokenResponse:
|
||||
try:
|
||||
return _to_issued_token_response(service.issue_client_token(auth_context, payload))
|
||||
except ValueError as exc:
|
||||
_raise_auth_error(exc)
|
||||
|
||||
|
||||
@router.delete("/tokens/{token_id}", status_code=status.HTTP_204_NO_CONTENT, dependencies=[Depends(require_read)])
|
||||
def revoke_token(
|
||||
token_id: str,
|
||||
auth_context: AuthContext = Depends(require_read),
|
||||
service: AuthService = Depends(get_auth_service),
|
||||
) -> Response:
|
||||
try:
|
||||
service.revoke_token(auth_context, token_id)
|
||||
except ValueError as exc:
|
||||
_raise_auth_error(exc)
|
||||
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
||||
+113
-33
@@ -1,8 +1,10 @@
|
||||
"""Portfolio API routes."""
|
||||
|
||||
from fastapi import APIRouter, Depends, Response, status
|
||||
from fastapi import APIRouter, Depends, Request, Response, status
|
||||
|
||||
from app.core.auth import require_admin, require_read, require_trade_write
|
||||
from app.core.auth import Permission, authorize_request
|
||||
from app.core.config import Settings, get_settings
|
||||
from app.repositories.factory import get_portfolio_repository_from_settings
|
||||
from app.schemas.portfolio import (
|
||||
BuyRequest,
|
||||
CreatePortfolioRequest,
|
||||
@@ -14,101 +16,179 @@ from app.schemas.portfolio import (
|
||||
TransferRequest,
|
||||
UpdatePortfolioRequest,
|
||||
)
|
||||
from app.services.portfolio_service import PortfolioService, get_portfolio_service
|
||||
from app.services.portfolio_service import PortfolioService
|
||||
|
||||
router = APIRouter(prefix="/portfolios", tags=["portfolios"])
|
||||
|
||||
|
||||
@router.get("", response_model=list[PortfolioListItem], dependencies=[Depends(require_read)])
|
||||
def _get_service(settings: Settings) -> tuple[PortfolioService, object]:
|
||||
repository = get_portfolio_repository_from_settings(settings)
|
||||
return PortfolioService(repository), repository
|
||||
|
||||
|
||||
def _close_repository(repository: object) -> None:
|
||||
if callable(getattr(type(repository), "close", None)):
|
||||
repository.close()
|
||||
|
||||
|
||||
@router.get("", response_model=list[PortfolioListItem])
|
||||
def list_portfolios(
|
||||
request: Request,
|
||||
broker: str | None = None,
|
||||
status_filter: str | None = None,
|
||||
service: PortfolioService = Depends(get_portfolio_service),
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> list[PortfolioListItem]:
|
||||
return service.list_portfolios(broker=broker, status=status_filter)
|
||||
authorize_request(Permission.READ, request, settings)
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
return service.list_portfolios(broker=broker, status=status_filter)
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
|
||||
|
||||
@router.get("/{portfolio_id}", response_model=PortfolioDetailResponse, dependencies=[Depends(require_read)])
|
||||
@router.get("/{portfolio_id}", response_model=PortfolioDetailResponse)
|
||||
def get_portfolio(
|
||||
portfolio_id: str,
|
||||
request: Request,
|
||||
*,
|
||||
recent_trades: int = 5,
|
||||
service: PortfolioService = Depends(get_portfolio_service),
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> PortfolioDetailResponse:
|
||||
return service.get_portfolio_status(portfolio_id=portfolio_id, recent_trades=recent_trades)
|
||||
authorize_request(Permission.READ, request, settings)
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
return service.get_portfolio_status(portfolio_id=portfolio_id, recent_trades=recent_trades)
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
|
||||
|
||||
@router.post(
|
||||
"",
|
||||
response_model=PortfolioDetailResponse,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
dependencies=[Depends(require_trade_write)],
|
||||
)
|
||||
def create_portfolio(
|
||||
payload: CreatePortfolioRequest,
|
||||
service: PortfolioService = Depends(get_portfolio_service),
|
||||
request: Request,
|
||||
*,
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> PortfolioDetailResponse:
|
||||
return service.create_portfolio(payload)
|
||||
authorize_request(Permission.TRADE_WRITE, request, settings)
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
return service.create_portfolio(payload)
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
|
||||
|
||||
@router.patch("/{portfolio_id}", response_model=PortfolioDetailResponse, dependencies=[Depends(require_trade_write)])
|
||||
@router.patch("/{portfolio_id}", response_model=PortfolioDetailResponse)
|
||||
def update_portfolio(
|
||||
portfolio_id: str,
|
||||
payload: UpdatePortfolioRequest,
|
||||
service: PortfolioService = Depends(get_portfolio_service),
|
||||
request: Request,
|
||||
*,
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> PortfolioDetailResponse:
|
||||
return service.update_portfolio(portfolio_id, payload)
|
||||
authorize_request(Permission.TRADE_WRITE, request, settings)
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
return service.update_portfolio(portfolio_id, payload)
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
|
||||
|
||||
@router.post("/{portfolio_id}/buy", response_model=PortfolioDetailResponse, dependencies=[Depends(require_trade_write)])
|
||||
@router.post("/{portfolio_id}/buy", response_model=PortfolioDetailResponse)
|
||||
def buy_portfolio_asset(
|
||||
portfolio_id: str,
|
||||
payload: BuyRequest,
|
||||
service: PortfolioService = Depends(get_portfolio_service),
|
||||
request: Request,
|
||||
*,
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> PortfolioDetailResponse:
|
||||
return service.buy(portfolio_id, payload)
|
||||
authorize_request(Permission.TRADE_WRITE, request, settings)
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
return service.buy(portfolio_id, payload)
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
|
||||
|
||||
@router.post("/{portfolio_id}/sell", response_model=PortfolioDetailResponse, dependencies=[Depends(require_trade_write)])
|
||||
@router.post("/{portfolio_id}/sell", response_model=PortfolioDetailResponse)
|
||||
def sell_portfolio_asset(
|
||||
portfolio_id: str,
|
||||
payload: SellRequest,
|
||||
service: PortfolioService = Depends(get_portfolio_service),
|
||||
request: Request,
|
||||
*,
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> PortfolioDetailResponse:
|
||||
return service.sell(portfolio_id, payload)
|
||||
authorize_request(Permission.TRADE_WRITE, request, settings)
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
return service.sell(portfolio_id, payload)
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
|
||||
|
||||
@router.post("/{portfolio_id}/invest", response_model=PortfolioDetailResponse, dependencies=[Depends(require_trade_write)])
|
||||
@router.post("/{portfolio_id}/invest", response_model=PortfolioDetailResponse)
|
||||
def invest_buffer(
|
||||
portfolio_id: str,
|
||||
payload: InvestRequest,
|
||||
service: PortfolioService = Depends(get_portfolio_service),
|
||||
request: Request,
|
||||
*,
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> PortfolioDetailResponse:
|
||||
return service.invest(portfolio_id, payload)
|
||||
authorize_request(Permission.TRADE_WRITE, request, settings)
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
return service.invest(portfolio_id, payload)
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
|
||||
|
||||
@router.post("/{portfolio_id}/transfer", response_model=PortfolioDetailResponse, dependencies=[Depends(require_trade_write)])
|
||||
@router.post("/{portfolio_id}/transfer", response_model=PortfolioDetailResponse)
|
||||
def transfer_buffer(
|
||||
portfolio_id: str,
|
||||
payload: TransferRequest,
|
||||
service: PortfolioService = Depends(get_portfolio_service),
|
||||
request: Request,
|
||||
*,
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> PortfolioDetailResponse:
|
||||
return service.transfer(portfolio_id, payload)
|
||||
authorize_request(Permission.TRADE_WRITE, request, settings)
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
return service.transfer(portfolio_id, payload)
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
|
||||
|
||||
@router.post("/{portfolio_id}/reset", response_model=PortfolioDetailResponse, dependencies=[Depends(require_trade_write)])
|
||||
@router.post("/{portfolio_id}/reset", response_model=PortfolioDetailResponse)
|
||||
def reset_portfolio(
|
||||
portfolio_id: str,
|
||||
payload: ResetPortfolioRequest,
|
||||
service: PortfolioService = Depends(get_portfolio_service),
|
||||
request: Request,
|
||||
*,
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> PortfolioDetailResponse:
|
||||
return service.reset(portfolio_id, payload)
|
||||
authorize_request(Permission.TRADE_WRITE, request, settings)
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
return service.reset(portfolio_id, payload)
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
|
||||
|
||||
@router.delete("/{portfolio_id}", status_code=status.HTTP_204_NO_CONTENT, dependencies=[Depends(require_admin)])
|
||||
@router.delete("/{portfolio_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def delete_portfolio(
|
||||
portfolio_id: str,
|
||||
request: Request,
|
||||
*,
|
||||
keep_ledger: bool = False,
|
||||
service: PortfolioService = Depends(get_portfolio_service),
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> Response:
|
||||
service.delete(portfolio_id, keep_ledger=keep_ledger)
|
||||
authorize_request(Permission.ADMIN, request, settings)
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
service.delete(portfolio_id, keep_ledger=keep_ledger)
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
+46
-18
@@ -2,24 +2,37 @@
|
||||
|
||||
from fastapi import APIRouter, Depends, Response, status
|
||||
|
||||
from app.core.auth import require_read, require_trade_write
|
||||
from app.core.auth import AuthContext, require_read, require_trade_write
|
||||
from app.core.config import Settings, get_settings
|
||||
from app.repositories.factory import get_portfolio_repository_from_settings
|
||||
from app.schemas.portfolio import CreateStrategySetRequest, StrategySetResponse, UpdateStrategySetRequest
|
||||
from app.services.strategy_set_service import StrategySetService, get_strategy_set_repository
|
||||
|
||||
|
||||
def get_strategy_set_service(repository=Depends(get_strategy_set_repository)) -> StrategySetService:
|
||||
return StrategySetService(repository)
|
||||
from app.services.strategy_set_service import StrategySetService
|
||||
|
||||
|
||||
router = APIRouter(prefix="/strategy-sets", tags=["strategy-sets"])
|
||||
|
||||
|
||||
@router.get("", response_model=list[StrategySetResponse], dependencies=[Depends(require_read)])
|
||||
def _get_service(settings: Settings) -> tuple[StrategySetService, object]:
|
||||
repository = get_portfolio_repository_from_settings(settings)
|
||||
return StrategySetService(repository), repository
|
||||
|
||||
|
||||
def _close_repository(repository: object) -> None:
|
||||
if callable(getattr(type(repository), "close", None)):
|
||||
repository.close()
|
||||
|
||||
|
||||
@router.get("", response_model=list[StrategySetResponse])
|
||||
def list_strategy_sets(
|
||||
service: StrategySetService = Depends(get_strategy_set_service),
|
||||
_auth: AuthContext = Depends(require_read),
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> list[StrategySetResponse]:
|
||||
"""List all strategy sets."""
|
||||
strategy_sets = service.list()
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
strategy_sets = service.list()
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
return [
|
||||
StrategySetResponse(
|
||||
id=ss.id,
|
||||
@@ -32,13 +45,18 @@ def list_strategy_sets(
|
||||
]
|
||||
|
||||
|
||||
@router.post("", response_model=StrategySetResponse, status_code=status.HTTP_201_CREATED, dependencies=[Depends(require_trade_write)])
|
||||
@router.post("", response_model=StrategySetResponse, status_code=status.HTTP_201_CREATED)
|
||||
def create_strategy_set(
|
||||
payload: CreateStrategySetRequest,
|
||||
service: StrategySetService = Depends(get_strategy_set_service),
|
||||
_auth: AuthContext = Depends(require_trade_write),
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> StrategySetResponse:
|
||||
"""Create a new strategy set (or return existing if (name, config) pair already exists)."""
|
||||
strategy_set = service.create(payload)
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
strategy_set = service.create(payload)
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
return StrategySetResponse(
|
||||
id=strategy_set.id,
|
||||
name=strategy_set.name,
|
||||
@@ -48,18 +66,23 @@ def create_strategy_set(
|
||||
)
|
||||
|
||||
|
||||
@router.patch("/{strategy_set_id}", response_model=StrategySetResponse, dependencies=[Depends(require_trade_write)])
|
||||
@router.patch("/{strategy_set_id}", response_model=StrategySetResponse)
|
||||
def update_strategy_set(
|
||||
strategy_set_id: str,
|
||||
payload: UpdateStrategySetRequest,
|
||||
service: StrategySetService = Depends(get_strategy_set_service),
|
||||
_auth: AuthContext = Depends(require_trade_write),
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> StrategySetResponse:
|
||||
"""Update a strategy set's name and/or config.
|
||||
|
||||
Note: Since strategy sets are identified by (name, config) uniqueness,
|
||||
updating may result in a new strategy set ID if name or config changes.
|
||||
"""
|
||||
strategy_set = service.update(strategy_set_id, payload)
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
strategy_set = service.update(strategy_set_id, payload)
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
return StrategySetResponse(
|
||||
id=strategy_set.id,
|
||||
name=strategy_set.name,
|
||||
@@ -69,10 +92,11 @@ def update_strategy_set(
|
||||
)
|
||||
|
||||
|
||||
@router.delete("/{strategy_set_id}", status_code=status.HTTP_204_NO_CONTENT, dependencies=[Depends(require_trade_write)])
|
||||
@router.delete("/{strategy_set_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def delete_strategy_set(
|
||||
strategy_set_id: str,
|
||||
service: StrategySetService = Depends(get_strategy_set_service),
|
||||
_auth: AuthContext = Depends(require_trade_write),
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> Response:
|
||||
"""Delete a strategy set.
|
||||
|
||||
@@ -80,7 +104,11 @@ def delete_strategy_set(
|
||||
Returns 409 Conflict if the strategy set is in use by any portfolio.
|
||||
"""
|
||||
try:
|
||||
service.delete(strategy_set_id)
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
service.delete(strategy_set_id)
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
except ValueError as e:
|
||||
# If strategy set is in use, return 409 Conflict
|
||||
raise ValueError(str(e)) from e
|
||||
|
||||
+70
-20
@@ -1,26 +1,40 @@
|
||||
"""User management API routes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import NoReturn
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response, status
|
||||
|
||||
from app.core.auth import require_admin
|
||||
from app.core.auth import AuthContext, require_admin
|
||||
from app.core.config import Settings, get_settings
|
||||
from app.domain.models import User
|
||||
from app.repositories.factory import get_portfolio_repository_from_settings
|
||||
from app.schemas.user import CreateUserRequest, UpdateUserRequest, UserResponse
|
||||
from app.services.user_service import UserService, get_user_service
|
||||
from app.services.user_service import UserService
|
||||
|
||||
router = APIRouter(prefix="/users", tags=["users"])
|
||||
|
||||
|
||||
def _get_service(settings: Settings) -> tuple[UserService, object]:
|
||||
repository = get_portfolio_repository_from_settings(settings)
|
||||
return UserService(repository), repository
|
||||
|
||||
|
||||
def _close_repository(repository: object) -> None:
|
||||
if callable(getattr(type(repository), "close", None)):
|
||||
repository.close()
|
||||
|
||||
|
||||
def _to_user_response(user: User) -> UserResponse:
|
||||
return UserResponse(
|
||||
id=user.id,
|
||||
username=user.username,
|
||||
email=user.email,
|
||||
permission=user.permission,
|
||||
is_active=user.is_active,
|
||||
created_at=user.created_at,
|
||||
updated_at=user.updated_at,
|
||||
last_login_at=user.last_login_at,
|
||||
)
|
||||
|
||||
|
||||
@@ -33,43 +47,79 @@ def _raise_user_error(exc: ValueError) -> NoReturn:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=detail) from exc
|
||||
|
||||
|
||||
@router.get("", response_model=list[UserResponse], dependencies=[Depends(require_admin)])
|
||||
def list_users(service: UserService = Depends(get_user_service)) -> list[UserResponse]:
|
||||
return [_to_user_response(user) for user in service.list()]
|
||||
|
||||
|
||||
@router.get("/{user_id}", response_model=UserResponse, dependencies=[Depends(require_admin)])
|
||||
def get_user(user_id: str, service: UserService = Depends(get_user_service)) -> UserResponse:
|
||||
@router.get("", response_model=list[UserResponse])
|
||||
def list_users(
|
||||
_auth: AuthContext = Depends(require_admin),
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> list[UserResponse]:
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
return _to_user_response(service.get(user_id))
|
||||
return [_to_user_response(user) for user in service.list()]
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
|
||||
|
||||
@router.get("/{user_id}", response_model=UserResponse)
|
||||
def get_user(
|
||||
user_id: str,
|
||||
_auth: AuthContext = Depends(require_admin),
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> UserResponse:
|
||||
try:
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
return _to_user_response(service.get(user_id))
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
except ValueError as exc:
|
||||
_raise_user_error(exc)
|
||||
|
||||
|
||||
@router.post("", response_model=UserResponse, status_code=status.HTTP_201_CREATED, dependencies=[Depends(require_admin)])
|
||||
def create_user(payload: CreateUserRequest, service: UserService = Depends(get_user_service)) -> UserResponse:
|
||||
@router.post("", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
|
||||
def create_user(
|
||||
payload: CreateUserRequest,
|
||||
_auth: AuthContext = Depends(require_admin),
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> UserResponse:
|
||||
try:
|
||||
return _to_user_response(service.create(payload))
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
return _to_user_response(service.create(payload))
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
except ValueError as exc:
|
||||
_raise_user_error(exc)
|
||||
|
||||
|
||||
@router.patch("/{user_id}", response_model=UserResponse, dependencies=[Depends(require_admin)])
|
||||
@router.patch("/{user_id}", response_model=UserResponse)
|
||||
def update_user(
|
||||
user_id: str,
|
||||
payload: UpdateUserRequest,
|
||||
service: UserService = Depends(get_user_service),
|
||||
_auth: AuthContext = Depends(require_admin),
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> UserResponse:
|
||||
try:
|
||||
return _to_user_response(service.update(user_id, payload))
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
return _to_user_response(service.update(user_id, payload))
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
except ValueError as exc:
|
||||
_raise_user_error(exc)
|
||||
|
||||
|
||||
@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT, dependencies=[Depends(require_admin)])
|
||||
def delete_user(user_id: str, service: UserService = Depends(get_user_service)) -> Response:
|
||||
@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def delete_user(
|
||||
user_id: str,
|
||||
_auth: AuthContext = Depends(require_admin),
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> Response:
|
||||
try:
|
||||
service.delete(user_id)
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
service.delete(user_id)
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
except ValueError as exc:
|
||||
_raise_user_error(exc)
|
||||
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
||||
Reference in New Issue
Block a user