fixes
This commit is contained in:
parent
a16e8d263b
commit
9b857e5caa
|
|
@ -5,7 +5,10 @@ from typing import Annotated
|
|||
|
||||
from hubgw.api.deps import get_luckperms_service, verify_api_key
|
||||
from hubgw.services.luckperms_service import LuckPermsService
|
||||
from hubgw.schemas.luckperms import LuckPermsPlayer, LuckPermsGroup, LuckPermsUserPermission, LuckPermsPlayerWithPermissions
|
||||
from hubgw.schemas.luckperms import (
|
||||
LuckPermsPlayer, LuckPermsGroup, LuckPermsUserPermission,
|
||||
LuckPermsPlayerWithPermissions, LuckPermsPlayerCreateRequest
|
||||
)
|
||||
from hubgw.core.errors import AppError, create_http_exception
|
||||
|
||||
router = APIRouter()
|
||||
|
|
@ -73,4 +76,17 @@ async def get_player_with_permissions(
|
|||
try:
|
||||
return await service.get_player_with_permissions(uuid)
|
||||
except AppError as e:
|
||||
raise create_http_exception(e)
|
||||
raise create_http_exception(e)
|
||||
|
||||
|
||||
@router.post("/players", response_model=LuckPermsPlayer, status_code=201)
|
||||
async def create_player(
|
||||
request: LuckPermsPlayerCreateRequest,
|
||||
service: Annotated[LuckPermsService, Depends(get_luckperms_service)],
|
||||
_: Annotated[str, Depends(verify_api_key)]
|
||||
):
|
||||
"""Create a new player in LuckPerms."""
|
||||
try:
|
||||
return await service.create_player(request)
|
||||
except AppError as e:
|
||||
raise create_http_exception(e)
|
||||
|
|
|
|||
|
|
@ -1,40 +1,85 @@
|
|||
"""Application context singleton."""
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncEngine, async_sessionmaker, create_async_engine
|
||||
from collections.abc import AsyncGenerator
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
||||
from sqlalchemy.pool import NullPool
|
||||
|
||||
from hubgw.core.config import APP_CONFIG
|
||||
from hubgw.models.base import Base
|
||||
|
||||
|
||||
class AppContext:
|
||||
"""Application context singleton."""
|
||||
|
||||
_instance = None
|
||||
|
||||
def __new__(cls):
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
return cls._instance
|
||||
|
||||
class Singleton(type):
|
||||
_instances = {}
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
if cls not in cls._instances:
|
||||
cls._instances[cls] = super().__call__(*args, **kwargs)
|
||||
return cls._instances[cls]
|
||||
|
||||
|
||||
class AppContext(metaclass=Singleton):
|
||||
def __init__(self):
|
||||
if not hasattr(self, 'initialized'):
|
||||
self.settings = APP_CONFIG
|
||||
self.engine: AsyncEngine = None
|
||||
self.session_factory: async_sessionmaker = None
|
||||
self.initialized = True
|
||||
|
||||
self.settings = APP_CONFIG
|
||||
self._engine = None
|
||||
self._session_factory = None
|
||||
|
||||
@property
|
||||
def engine(self):
|
||||
if self._engine is None:
|
||||
connect_args = {
|
||||
"statement_cache_size": 0,
|
||||
"timeout": 20,
|
||||
}
|
||||
engine_kwargs = {
|
||||
"connect_args": connect_args,
|
||||
"pool_pre_ping": True,
|
||||
}
|
||||
|
||||
self._engine = create_async_engine(
|
||||
self.settings.database.dsn,
|
||||
pool_size=self.settings.database.pool_size,
|
||||
max_overflow=self.settings.database.max_overflow,
|
||||
pool_recycle=True,
|
||||
echo=self.settings.database.echo,
|
||||
**engine_kwargs,
|
||||
)
|
||||
|
||||
Base.metadata.bind = self._engine
|
||||
|
||||
return self._engine
|
||||
|
||||
@property
|
||||
def session_factory(self) -> async_sessionmaker[AsyncSession]:
|
||||
if self._session_factory is None:
|
||||
self._session_factory = async_sessionmaker(
|
||||
bind=self.engine,
|
||||
class_=AsyncSession,
|
||||
expire_on_commit=False,
|
||||
)
|
||||
return self._session_factory
|
||||
|
||||
async def get_db_session(self) -> AsyncGenerator[AsyncSession, None]:
|
||||
session = self.session_factory()
|
||||
try:
|
||||
yield session
|
||||
finally:
|
||||
await session.close()
|
||||
|
||||
async def startup(self):
|
||||
"""Initialize database engine and session factory."""
|
||||
self.engine = create_async_engine(
|
||||
self.settings.database.dsn,
|
||||
pool_size=self.settings.database.pool_size,
|
||||
max_overflow=self.settings.database.max_overflow,
|
||||
echo=self.settings.database.echo
|
||||
)
|
||||
self.session_factory = async_sessionmaker(
|
||||
self.engine,
|
||||
expire_on_commit=False
|
||||
)
|
||||
|
||||
pass
|
||||
|
||||
async def shutdown(self):
|
||||
"""Close database engine."""
|
||||
if self.engine:
|
||||
await self.engine.dispose()
|
||||
try:
|
||||
if self._engine is not None:
|
||||
await self._engine.dispose()
|
||||
self._engine = None
|
||||
except Exception as e:
|
||||
print(f"Error disposing engine: {e}")
|
||||
|
||||
|
||||
APP_CTX = AppContext()
|
||||
|
||||
__all__ = [
|
||||
"APP_CTX",
|
||||
]
|
||||
|
|
@ -4,6 +4,7 @@ from dotenv import load_dotenv
|
|||
from pydantic import Field, computed_field
|
||||
from pydantic_settings import BaseSettings
|
||||
from typing import Optional
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
|
@ -63,7 +64,7 @@ class DatabaseSettings(BaseSettings):
|
|||
def dsn(self) -> str:
|
||||
"""Generate database DSN from connection parameters."""
|
||||
return (
|
||||
f"postgresql+asyncpg://{self.user}:{self.password}"
|
||||
f"postgresql+asyncpg://{self.user}:{quote_plus(self.password)}"
|
||||
f"@{self.host}:{self.port}/{self.database}"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -35,4 +35,12 @@ class LuckPermsRepository:
|
|||
"""Get user permissions."""
|
||||
stmt = select(LuckPermsUserPermission).where(LuckPermsUserPermission.uuid == uuid)
|
||||
result = await self.session.execute(stmt)
|
||||
return list(result.scalars().all())
|
||||
return list(result.scalars().all())
|
||||
|
||||
async def create_player(self, uuid: str, username: str, primary_group: str) -> LuckPermsPlayer:
|
||||
"""Create a new player in LuckPerms."""
|
||||
player = LuckPermsPlayer(uuid=uuid, username=username, primary_group=primary_group)
|
||||
self.session.add(player)
|
||||
await self.session.commit()
|
||||
await self.session.refresh(player)
|
||||
return player
|
||||
|
|
@ -37,4 +37,11 @@ class LuckPermsUserPermission(BaseSchema):
|
|||
class LuckPermsPlayerWithPermissions(LuckPermsPlayer):
|
||||
"""LuckPerms Player with permissions schema."""
|
||||
|
||||
permissions: list[LuckPermsUserPermission] = []
|
||||
permissions: list[LuckPermsUserPermission] = []
|
||||
|
||||
|
||||
class LuckPermsPlayerCreateRequest(BaseModel):
|
||||
"""Request schema for creating a LuckPerms player."""
|
||||
|
||||
username: str
|
||||
primary_group: str = "default"
|
||||
|
|
@ -1,11 +1,15 @@
|
|||
"""LuckPerms service."""
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import List, Optional
|
||||
from typing import List
|
||||
from uuid import uuid3, NAMESPACE_OID
|
||||
|
||||
from hubgw.repositories.luckperms_repo import LuckPermsRepository
|
||||
from hubgw.schemas.luckperms import LuckPermsPlayer, LuckPermsGroup, LuckPermsUserPermission, LuckPermsPlayerWithPermissions
|
||||
from hubgw.core.errors import NotFoundError
|
||||
from hubgw.schemas.luckperms import (
|
||||
LuckPermsPlayer, LuckPermsGroup, LuckPermsUserPermission,
|
||||
LuckPermsPlayerWithPermissions, LuckPermsPlayerCreateRequest
|
||||
)
|
||||
from hubgw.core.errors import NotFoundError, AlreadyExistsError
|
||||
|
||||
|
||||
class LuckPermsService:
|
||||
|
|
@ -19,56 +23,26 @@ class LuckPermsService:
|
|||
player = await self.repo.get_player(uuid)
|
||||
if not player:
|
||||
raise NotFoundError(f"Player {uuid} not found")
|
||||
return LuckPermsPlayer(
|
||||
uuid=player.uuid,
|
||||
username=player.username,
|
||||
primary_group=player.primary_group,
|
||||
created_at=player.created_at,
|
||||
updated_at=player.updated_at
|
||||
)
|
||||
return LuckPermsPlayer.model_validate(player)
|
||||
|
||||
async def get_player_by_username(self, username: str) -> LuckPermsPlayer:
|
||||
"""Get player by username."""
|
||||
player = await self.repo.get_player_by_username(username)
|
||||
if not player:
|
||||
raise NotFoundError(f"Player {username} not found")
|
||||
return LuckPermsPlayer(
|
||||
uuid=player.uuid,
|
||||
username=player.username,
|
||||
primary_group=player.primary_group,
|
||||
created_at=player.created_at,
|
||||
updated_at=player.updated_at
|
||||
)
|
||||
return LuckPermsPlayer.model_validate(player)
|
||||
|
||||
async def get_group(self, name: str) -> LuckPermsGroup:
|
||||
"""Get group by name."""
|
||||
group = await self.repo.get_group(name)
|
||||
if not group:
|
||||
raise NotFoundError(f"Group {name} not found")
|
||||
return LuckPermsGroup(
|
||||
name=group.name,
|
||||
created_at=group.created_at,
|
||||
updated_at=group.updated_at
|
||||
)
|
||||
return LuckPermsGroup.model_validate(group)
|
||||
|
||||
async def get_user_permissions(self, uuid: str) -> List[LuckPermsUserPermission]:
|
||||
"""Get user permissions."""
|
||||
permissions = await self.repo.get_user_permissions(uuid)
|
||||
return [
|
||||
LuckPermsUserPermission(
|
||||
id=perm.id,
|
||||
uuid=perm.uuid,
|
||||
permission=perm.permission,
|
||||
value=perm.value,
|
||||
server=perm.server,
|
||||
world=perm.world,
|
||||
expiry=perm.expiry,
|
||||
contexts=perm.contexts,
|
||||
created_at=perm.created_at,
|
||||
updated_at=perm.updated_at
|
||||
)
|
||||
for perm in permissions
|
||||
]
|
||||
return [LuckPermsUserPermission.model_validate(perm) for perm in permissions]
|
||||
|
||||
async def get_player_with_permissions(self, uuid: str) -> LuckPermsPlayerWithPermissions:
|
||||
"""Get player with permissions."""
|
||||
|
|
@ -78,27 +52,22 @@ class LuckPermsService:
|
|||
|
||||
permissions = await self.repo.get_user_permissions(uuid)
|
||||
|
||||
permission_schemas = [
|
||||
LuckPermsUserPermission(
|
||||
id=perm.id,
|
||||
uuid=perm.uuid,
|
||||
permission=perm.permission,
|
||||
value=perm.value,
|
||||
server=perm.server,
|
||||
world=perm.world,
|
||||
expiry=perm.expiry,
|
||||
contexts=perm.contexts,
|
||||
created_at=perm.created_at,
|
||||
updated_at=perm.updated_at
|
||||
)
|
||||
for perm in permissions
|
||||
]
|
||||
permission_schemas = [LuckPermsUserPermission.model_validate(perm) for perm in permissions]
|
||||
|
||||
return LuckPermsPlayerWithPermissions(
|
||||
uuid=player.uuid,
|
||||
username=player.username,
|
||||
primary_group=player.primary_group,
|
||||
created_at=player.created_at,
|
||||
updated_at=player.updated_at,
|
||||
permissions=permission_schemas
|
||||
)
|
||||
return LuckPermsPlayerWithPermissions.model_validate(player, permissions=permission_schemas)
|
||||
|
||||
async def create_player(self, request: LuckPermsPlayerCreateRequest) -> LuckPermsPlayer:
|
||||
"""Create a new player in LuckPerms."""
|
||||
existing_player = await self.repo.get_player_by_username(request.username)
|
||||
if existing_player:
|
||||
raise AlreadyExistsError(f"Player with username {request.username} already exists")
|
||||
|
||||
uuid = str(uuid3(NAMESPACE_OID, request.username))
|
||||
|
||||
created_player = await self.repo.create_player(
|
||||
uuid=uuid,
|
||||
username=request.username,
|
||||
primary_group=request.primary_group
|
||||
)
|
||||
|
||||
return LuckPermsPlayer.model_validate(created_player)
|
||||
|
|
|
|||
Loading…
Reference in New Issue