From 9501ea92fe657f3af3afdbccb8aa3c7f41e2d41a Mon Sep 17 00:00:00 2001 From: itqop Date: Sat, 18 Oct 2025 16:10:17 +0300 Subject: [PATCH] refactor: clean code --- src/hubgw/__main__.py | 8 +- src/hubgw/api/deps.py | 62 +++++++++++----- src/hubgw/api/v1/audit.py | 9 ++- src/hubgw/api/v1/cooldowns.py | 13 ++-- src/hubgw/api/v1/homes.py | 15 ++-- src/hubgw/api/v1/kits.py | 9 ++- src/hubgw/api/v1/luckperms.py | 32 ++++---- src/hubgw/api/v1/punishments.py | 25 ++++--- src/hubgw/api/v1/router.py | 13 +++- src/hubgw/api/v1/teleport_history.py | 16 ++-- src/hubgw/api/v1/users.py | 9 ++- src/hubgw/api/v1/warps.py | 23 +++--- src/hubgw/api/v1/whitelist.py | 31 ++++---- src/hubgw/context.py | 8 +- src/hubgw/core/__init__.py | 3 +- src/hubgw/core/config.py | 56 +++++++------- src/hubgw/core/errors.py | 23 +++--- src/hubgw/core/logging.py | 12 +-- src/hubgw/main.py | 15 ++-- src/hubgw/models/base.py | 10 ++- src/hubgw/models/cooldown.py | 21 ++++-- src/hubgw/models/home.py | 17 ++++- src/hubgw/models/luckperms.py | 32 ++++---- src/hubgw/models/punishment.py | 49 ++++++++++--- src/hubgw/models/teleport_history.py | 18 ++++- src/hubgw/models/users.py | 11 ++- src/hubgw/models/warp.py | 13 +++- src/hubgw/models/whitelist.py | 25 +++++-- src/hubgw/repositories/cooldowns_repo.py | 21 ++++-- src/hubgw/repositories/homes_repo.py | 22 +++--- src/hubgw/repositories/kits_repo.py | 49 ++++++++----- src/hubgw/repositories/luckperms_repo.py | 34 +++++---- src/hubgw/repositories/punishments_repo.py | 64 +++++++++------- .../repositories/teleport_history_repo.py | 28 +++++-- src/hubgw/repositories/users_repo.py | 9 ++- src/hubgw/repositories/warps_repo.py | 11 +-- src/hubgw/repositories/whitelist_repo.py | 41 +++++++---- src/hubgw/schemas/audit.py | 9 ++- src/hubgw/schemas/common.py | 16 ++-- src/hubgw/schemas/cooldowns.py | 16 ++-- src/hubgw/schemas/homes.py | 21 +++--- src/hubgw/schemas/kits.py | 10 ++- src/hubgw/schemas/luckperms.py | 16 ++-- src/hubgw/schemas/punishments.py | 23 +++--- src/hubgw/schemas/teleport_history.py | 14 ++-- src/hubgw/schemas/users.py | 4 +- src/hubgw/schemas/warps.py | 23 +++--- src/hubgw/schemas/whitelist.py | 16 ++-- src/hubgw/services/audit_service.py | 5 +- src/hubgw/services/cooldowns_service.py | 31 ++++---- src/hubgw/services/homes_service.py | 32 ++++---- src/hubgw/services/kits_service.py | 33 +++++---- src/hubgw/services/luckperms_service.py | 66 +++++++++-------- src/hubgw/services/punishments_service.py | 73 ++++++++++--------- .../services/teleport_history_service.py | 22 +++--- src/hubgw/services/users_service.py | 6 +- src/hubgw/services/warps_service.py | 48 ++++++------ src/hubgw/services/whitelist_service.py | 69 +++++++++--------- src/hubgw/utils/pagination.py | 10 +-- 59 files changed, 826 insertions(+), 594 deletions(-) diff --git a/src/hubgw/__main__.py b/src/hubgw/__main__.py index 694e5f2..42046c2 100644 --- a/src/hubgw/__main__.py +++ b/src/hubgw/__main__.py @@ -1,18 +1,14 @@ """Entry point for hubgw application.""" import uvicorn + from hubgw.main import create_app def main(): """Main entry point.""" app = create_app() - uvicorn.run( - app, - host="0.0.0.0", - port=8080, - log_level="info" - ) + uvicorn.run(app, host="0.0.0.0", port=8080, log_level="info") if __name__ == "__main__": diff --git a/src/hubgw/api/deps.py b/src/hubgw/api/deps.py index b6018aa..652190f 100644 --- a/src/hubgw/api/deps.py +++ b/src/hubgw/api/deps.py @@ -1,21 +1,22 @@ """Dependency providers for FastAPI.""" from collections.abc import AsyncGenerator -from fastapi import Depends, HTTPException, Header -from sqlalchemy.ext.asyncio import AsyncSession from typing import Annotated +from fastapi import Depends, Header, HTTPException +from sqlalchemy.ext.asyncio import AsyncSession + from hubgw.context import AppContext +from hubgw.services.audit_service import AuditService +from hubgw.services.cooldowns_service import CooldownsService from hubgw.services.homes_service import HomesService from hubgw.services.kits_service import KitsService -from hubgw.services.cooldowns_service import CooldownsService +from hubgw.services.luckperms_service import LuckPermsService +from hubgw.services.punishments_service import PunishmentsService +from hubgw.services.teleport_history_service import TeleportHistoryService from hubgw.services.users_service import UserService from hubgw.services.warps_service import WarpsService from hubgw.services.whitelist_service import WhitelistService -from hubgw.services.punishments_service import PunishmentsService -from hubgw.services.audit_service import AuditService -from hubgw.services.luckperms_service import LuckPermsService -from hubgw.services.teleport_history_service import TeleportHistoryService async def get_context() -> AppContext: @@ -41,59 +42,82 @@ async def get_azuriom_session( async def verify_api_key( x_api_key: Annotated[str, Header(alias="X-API-Key")], - context: Annotated[AppContext, Depends(get_context)] + context: Annotated[AppContext, Depends(get_context)], ) -> str: """Verify API key.""" if x_api_key != context.settings.security.api_key: - raise HTTPException(status_code=401, detail=f"Invalid API key, {x_api_key=}, {context.settings.security.api_key=}") + raise HTTPException( + status_code=401, + detail=f"Invalid API key, {x_api_key=}, {context.settings.security.api_key=}", + ) return x_api_key -def get_homes_service(session: Annotated[AsyncSession, Depends(get_session)]) -> HomesService: +def get_homes_service( + session: Annotated[AsyncSession, Depends(get_session)] +) -> HomesService: """Get homes service.""" return HomesService(session) -def get_kits_service(session: Annotated[AsyncSession, Depends(get_session)]) -> KitsService: +def get_kits_service( + session: Annotated[AsyncSession, Depends(get_session)] +) -> KitsService: """Get kits service.""" return KitsService(session) -def get_cooldowns_service(session: Annotated[AsyncSession, Depends(get_session)]) -> CooldownsService: +def get_cooldowns_service( + session: Annotated[AsyncSession, Depends(get_session)] +) -> CooldownsService: """Get cooldowns service.""" return CooldownsService(session) -def get_warps_service(session: Annotated[AsyncSession, Depends(get_session)]) -> WarpsService: +def get_warps_service( + session: Annotated[AsyncSession, Depends(get_session)] +) -> WarpsService: """Get warps service.""" return WarpsService(session) -def get_whitelist_service(session: Annotated[AsyncSession, Depends(get_session)]) -> WhitelistService: +def get_whitelist_service( + session: Annotated[AsyncSession, Depends(get_session)] +) -> WhitelistService: """Get whitelist service.""" return WhitelistService(session) -def get_punishments_service(session: Annotated[AsyncSession, Depends(get_session)]) -> PunishmentsService: +def get_punishments_service( + session: Annotated[AsyncSession, Depends(get_session)] +) -> PunishmentsService: """Get punishments service.""" return PunishmentsService(session) -def get_audit_service(session: Annotated[AsyncSession, Depends(get_session)]) -> AuditService: +def get_audit_service( + session: Annotated[AsyncSession, Depends(get_session)] +) -> AuditService: """Get audit service.""" return AuditService(session) -def get_luckperms_service(session: Annotated[AsyncSession, Depends(get_session)]) -> LuckPermsService: +def get_luckperms_service( + session: Annotated[AsyncSession, Depends(get_session)] +) -> LuckPermsService: """Get luckperms service.""" return LuckPermsService(session) -def get_teleport_history_service(session: Annotated[AsyncSession, Depends(get_session)]) -> TeleportHistoryService: +def get_teleport_history_service( + session: Annotated[AsyncSession, Depends(get_session)] +) -> TeleportHistoryService: """Get teleport history service.""" return TeleportHistoryService(session) -def get_user_service(session: Annotated[AsyncSession, Depends(get_azuriom_session)]) -> UserService: +def get_user_service( + session: Annotated[AsyncSession, Depends(get_azuriom_session)] +) -> UserService: """Get user service.""" return UserService(session) diff --git a/src/hubgw/api/v1/audit.py b/src/hubgw/api/v1/audit.py index ac4ccd9..85a2554 100644 --- a/src/hubgw/api/v1/audit.py +++ b/src/hubgw/api/v1/audit.py @@ -1,12 +1,13 @@ """Audit endpoints.""" -from fastapi import APIRouter, Depends, HTTPException from typing import Annotated +from fastapi import APIRouter, Depends + from hubgw.api.deps import get_audit_service, verify_api_key -from hubgw.services.audit_service import AuditService -from hubgw.schemas.audit import CommandAuditRequest, CommandAuditResponse from hubgw.core.errors import AppError, create_http_exception +from hubgw.schemas.audit import CommandAuditRequest, CommandAuditResponse +from hubgw.services.audit_service import AuditService router = APIRouter() @@ -15,7 +16,7 @@ router = APIRouter() async def log_command( request: CommandAuditRequest, service: Annotated[AuditService, Depends(get_audit_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Log command execution for audit.""" try: diff --git a/src/hubgw/api/v1/cooldowns.py b/src/hubgw/api/v1/cooldowns.py index cdd61d0..3109176 100644 --- a/src/hubgw/api/v1/cooldowns.py +++ b/src/hubgw/api/v1/cooldowns.py @@ -1,13 +1,14 @@ """Cooldowns endpoints.""" -from fastapi import APIRouter, Depends, HTTPException, Query from typing import Annotated -from uuid import UUID + +from fastapi import APIRouter, Depends from hubgw.api.deps import get_cooldowns_service, verify_api_key -from hubgw.services.cooldowns_service import CooldownsService -from hubgw.schemas.cooldowns import CooldownCheckRequest, CooldownCheckResponse, CooldownCreate from hubgw.core.errors import AppError, create_http_exception +from hubgw.schemas.cooldowns import (CooldownCheckRequest, + CooldownCheckResponse, CooldownCreate) +from hubgw.services.cooldowns_service import CooldownsService router = APIRouter() @@ -16,7 +17,7 @@ router = APIRouter() async def check_cooldown( request: CooldownCheckRequest, service: Annotated[CooldownsService, Depends(get_cooldowns_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Check cooldown status.""" try: @@ -29,7 +30,7 @@ async def check_cooldown( async def create_cooldown( request: CooldownCreate, service: Annotated[CooldownsService, Depends(get_cooldowns_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Create cooldown.""" try: diff --git a/src/hubgw/api/v1/homes.py b/src/hubgw/api/v1/homes.py index 942f60e..55ae10f 100644 --- a/src/hubgw/api/v1/homes.py +++ b/src/hubgw/api/v1/homes.py @@ -1,13 +1,14 @@ """Homes endpoints.""" -from fastapi import APIRouter, Depends, HTTPException from typing import Annotated -from uuid import UUID + +from fastapi import APIRouter, Depends from hubgw.api.deps import get_homes_service, verify_api_key -from hubgw.services.homes_service import HomesService -from hubgw.schemas.homes import HomeUpsertRequest, HomeGetRequest, Home, HomeGetResponse, HomeListResponse from hubgw.core.errors import AppError, create_http_exception +from hubgw.schemas.homes import (Home, HomeGetRequest, HomeGetResponse, + HomeListResponse, HomeUpsertRequest) +from hubgw.services.homes_service import HomesService router = APIRouter() @@ -16,7 +17,7 @@ router = APIRouter() async def upsert_home( request: HomeUpsertRequest, service: Annotated[HomesService, Depends(get_homes_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Upsert home.""" try: @@ -29,7 +30,7 @@ async def upsert_home( async def get_home( request: HomeGetRequest, service: Annotated[HomesService, Depends(get_homes_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Get home.""" try: @@ -42,7 +43,7 @@ async def get_home( async def list_homes( player_uuid: str, service: Annotated[HomesService, Depends(get_homes_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """List homes for player.""" try: diff --git a/src/hubgw/api/v1/kits.py b/src/hubgw/api/v1/kits.py index 28a2a4e..3271503 100644 --- a/src/hubgw/api/v1/kits.py +++ b/src/hubgw/api/v1/kits.py @@ -1,12 +1,13 @@ """Kits endpoints.""" -from fastapi import APIRouter, Depends, HTTPException from typing import Annotated +from fastapi import APIRouter, Depends + from hubgw.api.deps import get_kits_service, verify_api_key -from hubgw.services.kits_service import KitsService -from hubgw.schemas.kits import KitClaimRequest, KitClaimResponse from hubgw.core.errors import AppError, create_http_exception +from hubgw.schemas.kits import KitClaimRequest, KitClaimResponse +from hubgw.services.kits_service import KitsService router = APIRouter() @@ -15,7 +16,7 @@ router = APIRouter() async def claim_kit( request: KitClaimRequest, service: Annotated[KitsService, Depends(get_kits_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Claim kit.""" try: diff --git a/src/hubgw/api/v1/luckperms.py b/src/hubgw/api/v1/luckperms.py index 45e7285..1f2deca 100644 --- a/src/hubgw/api/v1/luckperms.py +++ b/src/hubgw/api/v1/luckperms.py @@ -1,16 +1,18 @@ """LuckPerms endpoints.""" -from fastapi import APIRouter, Depends, HTTPException from typing import Annotated -from hubgw.api.deps import get_luckperms_service, get_user_service, verify_api_key +from fastapi import APIRouter, Depends + +from hubgw.api.deps import (get_luckperms_service, get_user_service, + verify_api_key) +from hubgw.core.errors import AppError, create_http_exception +from hubgw.schemas.luckperms import (LuckPermsGroup, LuckPermsPlayer, + LuckPermsPlayerCreateRequest, + LuckPermsPlayerWithPermissions, + LuckPermsUserPermission) from hubgw.services.luckperms_service import LuckPermsService from hubgw.services.users_service import UserService -from hubgw.schemas.luckperms import ( - LuckPermsPlayer, LuckPermsGroup, LuckPermsUserPermission, - LuckPermsPlayerWithPermissions, LuckPermsPlayerCreateRequest -) -from hubgw.core.errors import AppError, create_http_exception router = APIRouter() @@ -19,7 +21,7 @@ router = APIRouter() async def get_player( uuid: str, service: Annotated[LuckPermsService, Depends(get_luckperms_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Get player by UUID.""" try: @@ -32,7 +34,7 @@ async def get_player( async def get_player_by_username( username: str, service: Annotated[LuckPermsService, Depends(get_luckperms_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Get player by username.""" try: @@ -45,7 +47,7 @@ async def get_player_by_username( async def get_group( name: str, service: Annotated[LuckPermsService, Depends(get_luckperms_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Get group by name.""" try: @@ -58,7 +60,7 @@ async def get_group( async def get_user_permissions( uuid: str, service: Annotated[LuckPermsService, Depends(get_luckperms_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Get user permissions.""" try: @@ -67,11 +69,13 @@ async def get_user_permissions( raise create_http_exception(e) -@router.get("/players/{uuid}/with-permissions", response_model=LuckPermsPlayerWithPermissions) +@router.get( + "/players/{uuid}/with-permissions", response_model=LuckPermsPlayerWithPermissions +) async def get_player_with_permissions( uuid: str, service: Annotated[LuckPermsService, Depends(get_luckperms_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Get player with permissions.""" try: @@ -85,7 +89,7 @@ async def create_player( request: LuckPermsPlayerCreateRequest, luckperms_service: Annotated[LuckPermsService, Depends(get_luckperms_service)], user_service: Annotated[UserService, Depends(get_user_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Create a new player in LuckPerms.""" try: diff --git a/src/hubgw/api/v1/punishments.py b/src/hubgw/api/v1/punishments.py index 4a63200..5d1d33f 100644 --- a/src/hubgw/api/v1/punishments.py +++ b/src/hubgw/api/v1/punishments.py @@ -1,16 +1,17 @@ """Punishments endpoints.""" -from fastapi import APIRouter, Depends, HTTPException from typing import Annotated -from uuid import UUID + +from fastapi import APIRouter, Depends from hubgw.api.deps import get_punishments_service, verify_api_key -from hubgw.services.punishments_service import PunishmentsService -from hubgw.schemas.punishments import ( - PunishmentCreateRequest, PunishmentRevokeRequest, PunishmentQuery, - PunishmentBase, PunishmentListResponse, ActiveBanStatusResponse, ActiveMuteStatusResponse -) from hubgw.core.errors import AppError, create_http_exception +from hubgw.schemas.punishments import (ActiveBanStatusResponse, + ActiveMuteStatusResponse, + PunishmentBase, PunishmentCreateRequest, + PunishmentListResponse, PunishmentQuery, + PunishmentRevokeRequest) +from hubgw.services.punishments_service import PunishmentsService router = APIRouter() @@ -19,7 +20,7 @@ router = APIRouter() async def create_punishment( request: PunishmentCreateRequest, service: Annotated[PunishmentsService, Depends(get_punishments_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Create punishment.""" try: @@ -32,7 +33,7 @@ async def create_punishment( async def revoke_punishment( request: PunishmentRevokeRequest, service: Annotated[PunishmentsService, Depends(get_punishments_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Revoke punishment.""" try: @@ -45,7 +46,7 @@ async def revoke_punishment( async def query_punishments( query: PunishmentQuery, service: Annotated[PunishmentsService, Depends(get_punishments_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Query punishments.""" try: @@ -58,7 +59,7 @@ async def query_punishments( async def get_ban_status( player_uuid: str, service: Annotated[PunishmentsService, Depends(get_punishments_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Get active ban status for player.""" try: @@ -71,7 +72,7 @@ async def get_ban_status( async def get_mute_status( player_uuid: str, service: Annotated[PunishmentsService, Depends(get_punishments_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Get active mute status for player.""" try: diff --git a/src/hubgw/api/v1/router.py b/src/hubgw/api/v1/router.py index 383e3f5..a12d00f 100644 --- a/src/hubgw/api/v1/router.py +++ b/src/hubgw/api/v1/router.py @@ -1,7 +1,10 @@ """Main API v1 router.""" from fastapi import APIRouter -from hubgw.api.v1 import health, homes, kits, cooldowns, warps, whitelist, punishments, audit, luckperms, teleport_history, users + +from hubgw.api.v1 import (audit, cooldowns, health, homes, kits, luckperms, + punishments, teleport_history, users, warps, + whitelist) api_router = APIRouter() @@ -12,8 +15,12 @@ api_router.include_router(kits.router, prefix="/kits", tags=["kits"]) api_router.include_router(cooldowns.router, prefix="/cooldowns", tags=["cooldowns"]) api_router.include_router(warps.router, prefix="/warps", tags=["warps"]) api_router.include_router(whitelist.router, prefix="/whitelist", tags=["whitelist"]) -api_router.include_router(punishments.router, prefix="/punishments", tags=["punishments"]) +api_router.include_router( + punishments.router, prefix="/punishments", tags=["punishments"] +) api_router.include_router(audit.router, prefix="/audit", tags=["audit"]) api_router.include_router(luckperms.router, prefix="/luckperms", tags=["luckperms"]) -api_router.include_router(teleport_history.router, prefix="/teleport-history", tags=["teleport-history"]) +api_router.include_router( + teleport_history.router, prefix="/teleport-history", tags=["teleport-history"] +) api_router.include_router(users.router, prefix="/users", tags=["users"]) diff --git a/src/hubgw/api/v1/teleport_history.py b/src/hubgw/api/v1/teleport_history.py index edb75a2..228bc90 100644 --- a/src/hubgw/api/v1/teleport_history.py +++ b/src/hubgw/api/v1/teleport_history.py @@ -1,12 +1,14 @@ """Teleport History endpoints.""" -from fastapi import APIRouter, Depends, HTTPException, Query from typing import Annotated +from fastapi import APIRouter, Depends, Query + from hubgw.api.deps import get_teleport_history_service, verify_api_key -from hubgw.services.teleport_history_service import TeleportHistoryService -from hubgw.schemas.teleport_history import TeleportHistoryCreate, TeleportHistory from hubgw.core.errors import AppError, create_http_exception +from hubgw.schemas.teleport_history import (TeleportHistory, + TeleportHistoryCreate) +from hubgw.services.teleport_history_service import TeleportHistoryService router = APIRouter() @@ -15,7 +17,7 @@ router = APIRouter() async def create_teleport( request: TeleportHistoryCreate, service: Annotated[TeleportHistoryService, Depends(get_teleport_history_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Create teleport history entry.""" try: @@ -29,10 +31,12 @@ async def list_player_teleports( player_uuid: str, service: Annotated[TeleportHistoryService, Depends(get_teleport_history_service)], _: Annotated[str, Depends(verify_api_key)], - limit: int = Query(100, ge=1, le=1000, description="Maximum number of entries to return") + limit: int = Query( + 100, ge=1, le=1000, description="Maximum number of entries to return" + ), ): """List teleport history for player.""" try: return await service.list_player_teleports(player_uuid, limit) except AppError as e: - raise create_http_exception(e) \ No newline at end of file + raise create_http_exception(e) diff --git a/src/hubgw/api/v1/users.py b/src/hubgw/api/v1/users.py index 7deac24..2d5ce54 100644 --- a/src/hubgw/api/v1/users.py +++ b/src/hubgw/api/v1/users.py @@ -1,12 +1,13 @@ """User endpoints.""" -from fastapi import APIRouter, Depends from typing import Annotated +from fastapi import APIRouter, Depends + from hubgw.api.deps import get_user_service, verify_api_key -from hubgw.services.users_service import UserService -from hubgw.schemas.users import GetUserGameIdResponse from hubgw.core.errors import AppError, create_http_exception +from hubgw.schemas.users import GetUserGameIdResponse +from hubgw.services.users_service import UserService router = APIRouter() @@ -15,7 +16,7 @@ router = APIRouter() async def get_user_game_id( name: str, service: Annotated[UserService, Depends(get_user_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Get game ID by user name.""" try: diff --git a/src/hubgw/api/v1/warps.py b/src/hubgw/api/v1/warps.py index 7115ead..2584d85 100644 --- a/src/hubgw/api/v1/warps.py +++ b/src/hubgw/api/v1/warps.py @@ -1,15 +1,16 @@ """Warps endpoints.""" -from fastapi import APIRouter, Depends, HTTPException from typing import Annotated +from fastapi import APIRouter, Depends + from hubgw.api.deps import get_warps_service, verify_api_key -from hubgw.services.warps_service import WarpsService -from hubgw.schemas.warps import ( - WarpCreateRequest, WarpUpdateRequest, WarpDeleteRequest, WarpGetRequest, - Warp, WarpGetResponse, WarpQuery, WarpListResponse -) from hubgw.core.errors import AppError, create_http_exception +from hubgw.schemas.warps import (Warp, WarpCreateRequest, WarpDeleteRequest, + WarpGetRequest, WarpGetResponse, + WarpListResponse, WarpQuery, + WarpUpdateRequest) +from hubgw.services.warps_service import WarpsService router = APIRouter() @@ -18,7 +19,7 @@ router = APIRouter() async def create_warp( request: WarpCreateRequest, service: Annotated[WarpsService, Depends(get_warps_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Create warp.""" try: @@ -31,7 +32,7 @@ async def create_warp( async def update_warp( request: WarpUpdateRequest, service: Annotated[WarpsService, Depends(get_warps_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Update warp.""" try: @@ -44,7 +45,7 @@ async def update_warp( async def delete_warp( request: WarpDeleteRequest, service: Annotated[WarpsService, Depends(get_warps_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Delete warp.""" try: @@ -57,7 +58,7 @@ async def delete_warp( async def get_warp( request: WarpGetRequest, service: Annotated[WarpsService, Depends(get_warps_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Get warp.""" try: @@ -70,7 +71,7 @@ async def get_warp( async def list_warps( query: WarpQuery, service: Annotated[WarpsService, Depends(get_warps_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """List warps.""" try: diff --git a/src/hubgw/api/v1/whitelist.py b/src/hubgw/api/v1/whitelist.py index 3c539d1..39f3c15 100644 --- a/src/hubgw/api/v1/whitelist.py +++ b/src/hubgw/api/v1/whitelist.py @@ -1,17 +1,19 @@ """Whitelist endpoints.""" -from fastapi import APIRouter, Depends -from typing import Annotated, Optional +from typing import Annotated -from hubgw.api.deps import get_whitelist_service, get_luckperms_service, get_user_service, verify_api_key -from hubgw.services.whitelist_service import WhitelistService +from fastapi import APIRouter, Depends + +from hubgw.api.deps import (get_luckperms_service, get_user_service, + get_whitelist_service, verify_api_key) +from hubgw.core.errors import AppError, create_http_exception +from hubgw.schemas.whitelist import (WhitelistAddRequest, + WhitelistCheckRequest, + WhitelistCheckResponse, WhitelistEntry, + WhitelistListResponse, WhitelistRemoveRequest) from hubgw.services.luckperms_service import LuckPermsService from hubgw.services.users_service import UserService -from hubgw.schemas.whitelist import ( - WhitelistAddRequest, WhitelistRemoveRequest, WhitelistCheckRequest, - WhitelistEntry, WhitelistCheckResponse, WhitelistListResponse, WhitelistQuery -) -from hubgw.core.errors import AppError, create_http_exception +from hubgw.services.whitelist_service import WhitelistService router = APIRouter() @@ -22,7 +24,7 @@ async def add_player( service: Annotated[WhitelistService, Depends(get_whitelist_service)], luckperms_service: Annotated[LuckPermsService, Depends(get_luckperms_service)], user_service: Annotated[UserService, Depends(get_user_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Add player to whitelist.""" try: @@ -35,7 +37,7 @@ async def add_player( async def remove_player( request: WhitelistRemoveRequest, service: Annotated[WhitelistService, Depends(get_whitelist_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Remove player from whitelist.""" try: @@ -48,7 +50,7 @@ async def remove_player( async def check_player( request: WhitelistCheckRequest, service: Annotated[WhitelistService, Depends(get_whitelist_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Check if player is whitelisted.""" try: @@ -60,7 +62,7 @@ async def check_player( @router.get("/", response_model=WhitelistListResponse) async def list_players( service: Annotated[WhitelistService, Depends(get_whitelist_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """List all whitelisted players with optional filters and pagination.""" try: @@ -72,7 +74,7 @@ async def list_players( @router.get("/count") async def get_count( service: Annotated[WhitelistService, Depends(get_whitelist_service)], - _: Annotated[str, Depends(verify_api_key)] + _: Annotated[str, Depends(verify_api_key)], ): """Get total count of whitelisted players.""" try: @@ -80,4 +82,3 @@ async def get_count( return {"total": count} except AppError as e: raise create_http_exception(e) - \ No newline at end of file diff --git a/src/hubgw/context.py b/src/hubgw/context.py index 456b885..b00a994 100644 --- a/src/hubgw/context.py +++ b/src/hubgw/context.py @@ -2,8 +2,8 @@ from collections.abc import AsyncGenerator -from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine -from sqlalchemy.pool import NullPool +from sqlalchemy.ext.asyncio import (AsyncSession, async_sessionmaker, + create_async_engine) from hubgw.core.config import APP_CONFIG from hubgw.models.base import Base @@ -121,7 +121,7 @@ class AppContext(metaclass=Singleton): self._engine = None except Exception as e: print(f"Error disposing engine: {e}") - + try: if self._azuriom_engine is not None: await self._azuriom_engine.dispose() @@ -134,4 +134,4 @@ APP_CTX = AppContext() __all__ = [ "APP_CTX", -] \ No newline at end of file +] diff --git a/src/hubgw/core/__init__.py b/src/hubgw/core/__init__.py index fc653bd..566a047 100644 --- a/src/hubgw/core/__init__.py +++ b/src/hubgw/core/__init__.py @@ -1,6 +1,7 @@ """Core module for hubgw.""" + from .config import APP_CONFIG __all__ = [ "APP_CONFIG", -] \ No newline at end of file +] diff --git a/src/hubgw/core/config.py b/src/hubgw/core/config.py index 7c00cff..c627dd0 100644 --- a/src/hubgw/core/config.py +++ b/src/hubgw/core/config.py @@ -1,69 +1,68 @@ """Application configuration using Pydantic Settings.""" +from typing import Optional +from urllib.parse import quote_plus + 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() class DatabaseSettings(BaseSettings): """Database configuration settings.""" - + host: str = Field( default="localhost", validation_alias="DATABASE__HOST", - description="Database host" + description="Database host", ) port: int = Field( default=5432, validation_alias="DATABASE__PORT", ge=1, le=65535, - description="Database port" + description="Database port", ) user: str = Field( - default="user", - validation_alias="DATABASE__USER", - description="Database user" + default="user", validation_alias="DATABASE__USER", description="Database user" ) password: str = Field( default="pass", validation_alias="DATABASE__PASSWORD", - description="Database password" + description="Database password", ) database: str = Field( default="hubgw", validation_alias="DATABASE__DATABASE", - description="Database name" + description="Database name", ) azuriom_database: str = Field( default="azuriom", validation_alias="DATABASE__AZURIOM_DATABASE", - description="Azuriom database name" + description="Azuriom database name", ) pool_size: int = Field( default=10, validation_alias="DATABASE__POOL_SIZE", ge=1, le=100, - description="Database connection pool size" + description="Database connection pool size", ) max_overflow: int = Field( default=10, validation_alias="DATABASE__MAX_OVERFLOW", ge=0, le=100, - description="Maximum number of overflow connections" + description="Maximum number of overflow connections", ) echo: bool = Field( default=False, validation_alias="DATABASE__ECHO", - description="Enable SQLAlchemy query logging" + description="Enable SQLAlchemy query logging", ) - + @computed_field @property def dsn(self) -> str: @@ -72,7 +71,7 @@ class DatabaseSettings(BaseSettings): f"postgresql+asyncpg://{self.user}:{quote_plus(self.password)}" f"@{self.host}:{self.port}/{self.database}" ) - + @computed_field @property def azuriom_dsn(self) -> str: @@ -85,55 +84,50 @@ class DatabaseSettings(BaseSettings): class SecuritySettings(BaseSettings): """Security configuration settings.""" - + api_key: str = Field( default="your-api-key", validation_alias="SECURITY__API_KEY", min_length=8, - description="API key for authentication" + description="API key for authentication", ) rate_limit_per_min: Optional[int] = Field( default=None, validation_alias="SECURITY__RATE_LIMIT_PER_MIN", ge=1, - description="Rate limit per minute (None = disabled)" + description="Rate limit per minute (None = disabled)", ) class AppSettings(BaseSettings): """Application settings.""" - + env: str = Field( default="dev", validation_alias="APP__ENV", - description="Application environment (dev/prod/test)" + description="Application environment (dev/prod/test)", ) host: str = Field( - default="0.0.0.0", - validation_alias="APP__HOST", - description="Application host" + default="0.0.0.0", validation_alias="APP__HOST", description="Application host" ) port: int = Field( default=8080, validation_alias="APP__PORT", ge=1, le=65535, - description="Application port" + description="Application port", ) log_level: str = Field( - default="INFO", - validation_alias="APP__LOG_LEVEL", - description="Logging level" + default="INFO", validation_alias="APP__LOG_LEVEL", description="Logging level" ) -class Secrets(): +class Secrets: """Main configuration container with all settings.""" - + app: AppSettings = AppSettings() database: DatabaseSettings = DatabaseSettings() security: SecuritySettings = SecuritySettings() - APP_CONFIG = Secrets() diff --git a/src/hubgw/core/errors.py b/src/hubgw/core/errors.py index bac7aad..b71bc8c 100644 --- a/src/hubgw/core/errors.py +++ b/src/hubgw/core/errors.py @@ -1,17 +1,18 @@ """Custom exceptions and error handlers.""" -from fastapi import HTTPException from typing import Any, Dict, Optional +from fastapi import HTTPException + class AppError(Exception): """Base application error.""" - + def __init__( self, message: str, code: str = "app_error", - details: Optional[Dict[str, Any]] = None + details: Optional[Dict[str, Any]] = None, ): self.message = message self.code = code @@ -21,28 +22,28 @@ class AppError(Exception): class ValidationError(AppError): """Validation error.""" - + def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): super().__init__(message, "invalid_state", details) class NotFoundError(AppError): """Not found error.""" - + def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): super().__init__(message, "not_found", details) class AlreadyExistsError(AppError): """Already exists error.""" - + def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): super().__init__(message, "already_exists", details) class CooldownActiveError(AppError): """Cooldown active error.""" - + def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): super().__init__(message, "cooldown_active", details) @@ -54,12 +55,8 @@ def create_http_exception(error: AppError) -> HTTPException: status_code = 404 elif isinstance(error, AlreadyExistsError): status_code = 409 - + return HTTPException( status_code=status_code, - detail={ - "message": error.message, - "code": error.code, - "details": error.details - } + detail={"message": error.message, "code": error.code, "details": error.details}, ) diff --git a/src/hubgw/core/logging.py b/src/hubgw/core/logging.py index 52effa0..a0210ad 100644 --- a/src/hubgw/core/logging.py +++ b/src/hubgw/core/logging.py @@ -1,25 +1,27 @@ """Logging configuration using loguru.""" import sys + from loguru import logger + from hubgw.core.config import APP_CONFIG def setup_logging(): """Setup loguru logging configuration.""" settings = APP_CONFIG.app - + # Remove default handler logger.remove() - + # Add console handler logger.add( sys.stdout, level=settings.log_level, format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} - {message}", - colorize=True + colorize=True, ) - + # Add file handler for production if settings.env == "prod": logger.add( @@ -28,5 +30,5 @@ def setup_logging(): format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} - {message}", rotation="1 day", retention="30 days", - compression="zip" + compression="zip", ) diff --git a/src/hubgw/main.py b/src/hubgw/main.py index b5357b3..f4bb8a2 100644 --- a/src/hubgw/main.py +++ b/src/hubgw/main.py @@ -1,9 +1,12 @@ """FastAPI application factory.""" + from contextlib import asynccontextmanager + from fastapi import FastAPI -from hubgw.core.logging import setup_logging -from hubgw.context import AppContext + from hubgw.api.v1.router import api_router +from hubgw.context import AppContext +from hubgw.core.logging import setup_logging @asynccontextmanager @@ -21,11 +24,11 @@ def create_app() -> FastAPI: title="HubGW", description="FastAPI Gateway for HubMC", version="0.1.0", - lifespan=lifespan + lifespan=lifespan, ) - + setup_logging() - + app.include_router(api_router, prefix="/api/v1") - + return app diff --git a/src/hubgw/models/base.py b/src/hubgw/models/base.py index 51d017a..c3dc2cc 100644 --- a/src/hubgw/models/base.py +++ b/src/hubgw/models/base.py @@ -1,12 +1,14 @@ """Base model class for SQLAlchemy.""" -from sqlalchemy.orm import DeclarativeBase + from sqlalchemy import Column, DateTime, func -from datetime import datetime +from sqlalchemy.orm import DeclarativeBase class Base(DeclarativeBase): """Base class for all models.""" - + created_at = Column(DateTime(timezone=True), server_default=func.now()) - updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) + updated_at = Column( + DateTime(timezone=True), server_default=func.now(), onupdate=func.now() + ) diff --git a/src/hubgw/models/cooldown.py b/src/hubgw/models/cooldown.py index 80637d8..e76d153 100644 --- a/src/hubgw/models/cooldown.py +++ b/src/hubgw/models/cooldown.py @@ -1,7 +1,9 @@ """Cooldown model.""" -from sqlalchemy import Column, String, DateTime, Integer, ForeignKey, UniqueConstraint -from sqlalchemy.dialects.postgresql import UUID, JSONB +from sqlalchemy import (Column, DateTime, ForeignKey, Integer, String, + UniqueConstraint) +from sqlalchemy.dialects.postgresql import JSONB, UUID + from hubgw.models.base import Base @@ -10,11 +12,20 @@ class Cooldown(Base): __tablename__ = "hub_cooldowns" __table_args__ = ( - UniqueConstraint('player_uuid', 'cooldown_type', name='idx_hub_cooldowns_player_type'), + UniqueConstraint( + "player_uuid", "cooldown_type", name="idx_hub_cooldowns_player_type" + ), ) - id = Column(UUID(as_uuid=True), primary_key=True, server_default="gen_random_uuid()") - player_uuid = Column(String(36), ForeignKey("luckperms_players.uuid", ondelete="CASCADE"), nullable=False, index=True) + id = Column( + UUID(as_uuid=True), primary_key=True, server_default="gen_random_uuid()" + ) + player_uuid = Column( + String(36), + ForeignKey("luckperms_players.uuid", ondelete="CASCADE"), + nullable=False, + index=True, + ) cooldown_type = Column(String(50), nullable=False) expires_at = Column(DateTime(timezone=True), nullable=False, index=True) cooldown_seconds = Column(Integer, nullable=False) diff --git a/src/hubgw/models/home.py b/src/hubgw/models/home.py index 4740718..0c3aaeb 100644 --- a/src/hubgw/models/home.py +++ b/src/hubgw/models/home.py @@ -1,7 +1,9 @@ """Home model.""" -from sqlalchemy import Column, Float, String, Boolean, ForeignKey, REAL, Text, UniqueConstraint +from sqlalchemy import (REAL, Boolean, Column, Float, ForeignKey, String, Text, + UniqueConstraint) from sqlalchemy.dialects.postgresql import UUID + from hubgw.models.base import Base @@ -10,11 +12,18 @@ class Home(Base): __tablename__ = "hub_homes" __table_args__ = ( - UniqueConstraint('player_uuid', 'name', name='idx_hub_homes_player_name'), + UniqueConstraint("player_uuid", "name", name="idx_hub_homes_player_name"), ) - id = Column(UUID(as_uuid=True), primary_key=True, server_default="gen_random_uuid()") - player_uuid = Column(String(36), ForeignKey("luckperms_players.uuid", ondelete="CASCADE"), nullable=False, index=True) + id = Column( + UUID(as_uuid=True), primary_key=True, server_default="gen_random_uuid()" + ) + player_uuid = Column( + String(36), + ForeignKey("luckperms_players.uuid", ondelete="CASCADE"), + nullable=False, + index=True, + ) name = Column(String(255), nullable=False) world = Column(Text, nullable=False) x = Column(Float, nullable=False) diff --git a/src/hubgw/models/luckperms.py b/src/hubgw/models/luckperms.py index 13e11be..3e93541 100644 --- a/src/hubgw/models/luckperms.py +++ b/src/hubgw/models/luckperms.py @@ -1,14 +1,7 @@ """LuckPerms models.""" -from sqlalchemy import ( - Column, - String, - Boolean, - BigInteger, - ForeignKey, - Integer, - Index, -) +from sqlalchemy import (BigInteger, Boolean, Column, ForeignKey, Index, + Integer, String) from sqlalchemy.orm import relationship from hubgw.models.base import Base @@ -23,7 +16,9 @@ class LuckPermsPlayer(Base): username = Column(String(16), nullable=False, index=True) primary_group = Column(String(36), nullable=False) - permissions = relationship("LuckPermsUserPermission", back_populates="player", cascade="all, delete-orphan") + permissions = relationship( + "LuckPermsUserPermission", back_populates="player", cascade="all, delete-orphan" + ) class LuckPermsGroup(Base): @@ -39,11 +34,22 @@ class LuckPermsUserPermission(Base): __tablename__ = "luckperms_user_permissions" __table_args__ = ( - Index('idx_luckperms_user_permissions_lookup', 'uuid', 'permission', 'server', 'world'), + Index( + "idx_luckperms_user_permissions_lookup", + "uuid", + "permission", + "server", + "world", + ), ) id = Column(Integer, primary_key=True, autoincrement=True) - uuid = Column(String(36), ForeignKey("luckperms_players.uuid", ondelete="CASCADE"), nullable=False, index=True) + uuid = Column( + String(36), + ForeignKey("luckperms_players.uuid", ondelete="CASCADE"), + nullable=False, + index=True, + ) permission = Column(String(200), nullable=False) value = Column(Boolean, nullable=False) server = Column(String(36)) @@ -51,4 +57,4 @@ class LuckPermsUserPermission(Base): expiry = Column(BigInteger) contexts = Column(String(200)) - player = relationship("LuckPermsPlayer", back_populates="permissions") \ No newline at end of file + player = relationship("LuckPermsPlayer", back_populates="permissions") diff --git a/src/hubgw/models/punishment.py b/src/hubgw/models/punishment.py index 1030526..2abd20a 100644 --- a/src/hubgw/models/punishment.py +++ b/src/hubgw/models/punishment.py @@ -1,7 +1,9 @@ """Punishment model.""" -from sqlalchemy import Column, String, DateTime, Boolean, Text, ForeignKey, CheckConstraint, Index -from sqlalchemy.dialects.postgresql import UUID, INET +from sqlalchemy import (Boolean, CheckConstraint, Column, DateTime, ForeignKey, + Index, String, Text) +from sqlalchemy.dialects.postgresql import INET, UUID + from hubgw.models.base import Base @@ -12,25 +14,52 @@ class Punishment(Base): __table_args__ = ( CheckConstraint( "punishment_type IN ('BAN', 'MUTE', 'KICK', 'WARN', 'TEMPBAN', 'TEMPMUTE')", - name='check_punishment_type' + name="check_punishment_type", + ), + Index( + "idx_hub_punishments_player_active", + "player_uuid", + "is_active", + postgresql_where=Column("is_active"), + ), + Index( + "idx_hub_punishments_type_active", + "punishment_type", + "is_active", + postgresql_where=Column("is_active"), + ), + Index( + "idx_hub_punishments_player_ip", + "player_ip", + postgresql_where=Column("player_ip") != None, ), - Index('idx_hub_punishments_player_active', 'player_uuid', 'is_active', postgresql_where=Column('is_active')), - Index('idx_hub_punishments_type_active', 'punishment_type', 'is_active', postgresql_where=Column('is_active')), - Index('idx_hub_punishments_player_ip', 'player_ip', postgresql_where=Column('player_ip') != None), ) - id = Column(UUID(as_uuid=True), primary_key=True, server_default="gen_random_uuid()") - player_uuid = Column(String(36), ForeignKey("luckperms_players.uuid", ondelete="CASCADE"), nullable=False, index=True) + id = Column( + UUID(as_uuid=True), primary_key=True, server_default="gen_random_uuid()" + ) + player_uuid = Column( + String(36), + ForeignKey("luckperms_players.uuid", ondelete="CASCADE"), + nullable=False, + index=True, + ) player_name = Column(String(255), nullable=False) player_ip = Column(INET) punishment_type = Column(String(50), nullable=False) reason = Column(Text, nullable=False) - staff_uuid = Column(String(36), ForeignKey("luckperms_players.uuid", ondelete="SET NULL"), nullable=False) + staff_uuid = Column( + String(36), + ForeignKey("luckperms_players.uuid", ondelete="SET NULL"), + nullable=False, + ) staff_name = Column(String(255), nullable=False) expires_at = Column(DateTime(timezone=True)) is_active = Column(Boolean, default=True) revoked_at = Column(DateTime(timezone=True)) - revoked_by = Column(String(36), ForeignKey("luckperms_players.uuid", ondelete="SET NULL")) + revoked_by = Column( + String(36), ForeignKey("luckperms_players.uuid", ondelete="SET NULL") + ) revoked_reason = Column(Text) evidence_url = Column(Text) notes = Column(Text) diff --git a/src/hubgw/models/teleport_history.py b/src/hubgw/models/teleport_history.py index cf35012..f333c54 100644 --- a/src/hubgw/models/teleport_history.py +++ b/src/hubgw/models/teleport_history.py @@ -1,8 +1,9 @@ """Teleport History model.""" -from sqlalchemy import Column, String, Float, ForeignKey, Text, DateTime +from sqlalchemy import Column, DateTime, Float, ForeignKey, String, Text from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.sql import func + from hubgw.models.base import Base @@ -11,8 +12,15 @@ class TeleportHistory(Base): __tablename__ = "hub_teleport_history" - id = Column(UUID(as_uuid=True), primary_key=True, server_default="gen_random_uuid()") - player_uuid = Column(String(36), ForeignKey("luckperms_players.uuid", ondelete="CASCADE"), nullable=False, index=True) + id = Column( + UUID(as_uuid=True), primary_key=True, server_default="gen_random_uuid()" + ) + player_uuid = Column( + String(36), + ForeignKey("luckperms_players.uuid", ondelete="CASCADE"), + nullable=False, + index=True, + ) from_world = Column(Text) from_x = Column(Float) # DOUBLE PRECISION from_y = Column(Float) # DOUBLE PRECISION @@ -23,4 +31,6 @@ class TeleportHistory(Base): to_z = Column(Float, nullable=False) # DOUBLE PRECISION tp_type = Column(String(50), nullable=False) target_name = Column(String(255)) - created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False) \ No newline at end of file + created_at = Column( + DateTime(timezone=True), server_default=func.now(), nullable=False + ) diff --git a/src/hubgw/models/users.py b/src/hubgw/models/users.py index 7a008ec..28a1c35 100644 --- a/src/hubgw/models/users.py +++ b/src/hubgw/models/users.py @@ -1,7 +1,8 @@ """User model.""" -from sqlalchemy import Column, Integer, String, DateTime, Boolean, Numeric, ForeignKey, Index -from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy import (Boolean, Column, DateTime, ForeignKey, Index, Integer, + Numeric, String) + from hubgw.models.base import Base @@ -18,7 +19,7 @@ class User(Base): role_id = Column(Integer, ForeignKey("roles.id"), nullable=False) money = Column(Numeric(14, 2), default=0) game_id = Column(String(191), nullable=True) - avatar = Column(String, nullable=True) # TEXT поле + avatar = Column(String, nullable=True) # TEXT поле access_token = Column(String(191), nullable=True) two_factor_secret = Column(String(191), nullable=True) two_factor_recovery_codes = Column(String(191), nullable=True) @@ -31,6 +32,4 @@ class User(Base): deleted_at = Column(DateTime(timezone=True), nullable=True) password_changed_at = Column(DateTime(timezone=True), nullable=True) - __table_args__ = ( - Index('idx_users_email', 'email', unique=True), - ) \ No newline at end of file + __table_args__ = (Index("idx_users_email", "email", unique=True),) diff --git a/src/hubgw/models/warp.py b/src/hubgw/models/warp.py index 5b0b73d..0786027 100644 --- a/src/hubgw/models/warp.py +++ b/src/hubgw/models/warp.py @@ -1,7 +1,8 @@ """Warp model.""" -from sqlalchemy import Column, String, Float, Boolean, REAL, Text, Index +from sqlalchemy import REAL, Boolean, Column, Float, Index, String, Text from sqlalchemy.dialects.postgresql import UUID + from hubgw.models.base import Base @@ -10,11 +11,15 @@ class Warp(Base): __tablename__ = "hub_warps" __table_args__ = ( - Index('idx_hub_warps_world', 'world'), - Index('idx_hub_warps_public', 'is_public', postgresql_where=Column('is_public')), + Index("idx_hub_warps_world", "world"), + Index( + "idx_hub_warps_public", "is_public", postgresql_where=Column("is_public") + ), ) - id = Column(UUID(as_uuid=True), primary_key=True, server_default="gen_random_uuid()") + id = Column( + UUID(as_uuid=True), primary_key=True, server_default="gen_random_uuid()" + ) name = Column(String(255), nullable=False, unique=True, index=True) world = Column(Text, nullable=False) x = Column(Float, nullable=False) diff --git a/src/hubgw/models/whitelist.py b/src/hubgw/models/whitelist.py index 4ab5483..7d57b5d 100644 --- a/src/hubgw/models/whitelist.py +++ b/src/hubgw/models/whitelist.py @@ -1,7 +1,8 @@ """Whitelist model.""" -from sqlalchemy import Column, String, DateTime, Boolean, ForeignKey, Index +from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Index, String from sqlalchemy.dialects.postgresql import UUID + from hubgw.models.base import Base @@ -10,13 +11,27 @@ class WhitelistEntry(Base): __tablename__ = "hub_whitelist" __table_args__ = ( - Index('idx_hub_whitelist_active', 'is_active', postgresql_where=Column('is_active')), - Index('idx_hub_whitelist_expires', 'expires_at', postgresql_where=Column('expires_at') != None), + Index( + "idx_hub_whitelist_active", + "is_active", + postgresql_where=Column("is_active"), + ), + Index( + "idx_hub_whitelist_expires", + "expires_at", + postgresql_where=Column("expires_at") != None, + ), ) - id = Column(UUID(as_uuid=True), primary_key=True, server_default="gen_random_uuid()") + id = Column( + UUID(as_uuid=True), primary_key=True, server_default="gen_random_uuid()" + ) player_name = Column(String(255), nullable=False, unique=True, index=True) - player_uuid = Column(String(36), ForeignKey("luckperms_players.uuid", ondelete="SET NULL"), index=True) + player_uuid = Column( + String(36), + ForeignKey("luckperms_players.uuid", ondelete="SET NULL"), + index=True, + ) added_by = Column(String(255), nullable=False) added_at = Column(DateTime(timezone=True), nullable=False) expires_at = Column(DateTime(timezone=True)) diff --git a/src/hubgw/repositories/cooldowns_repo.py b/src/hubgw/repositories/cooldowns_repo.py index 774c4aa..4fa9133 100644 --- a/src/hubgw/repositories/cooldowns_repo.py +++ b/src/hubgw/repositories/cooldowns_repo.py @@ -1,10 +1,11 @@ """Cooldowns repository.""" -from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy import select, insert, update, delete, func -from typing import Optional, List -from uuid import UUID from datetime import datetime, timedelta +from typing import List, Optional +from uuid import UUID + +from sqlalchemy import delete, func, select +from sqlalchemy.ext.asyncio import AsyncSession from hubgw.models.cooldown import Cooldown from hubgw.schemas.cooldowns import CooldownCreate, CooldownQuery @@ -25,7 +26,7 @@ class CooldownsRepository: cooldown_type=request.cooldown_type, expires_at=expires_at, cooldown_seconds=request.cooldown_seconds, - metadata=request.metadata + metadata=request.metadata, ) self.session.add(cooldown) await self.session.commit() @@ -38,12 +39,14 @@ class CooldownsRepository: result = await self.session.execute(stmt) return result.scalar_one_or_none() - async def get_active_by_type(self, player_uuid: str, cooldown_type: str) -> Optional[Cooldown]: + async def get_active_by_type( + self, player_uuid: str, cooldown_type: str + ) -> Optional[Cooldown]: """Get active cooldown by type.""" stmt = select(Cooldown).where( Cooldown.player_uuid == player_uuid, Cooldown.cooldown_type == cooldown_type, - Cooldown.expires_at > func.now() + Cooldown.expires_at > func.now(), ) result = await self.session.execute(stmt) return result.scalar_one_or_none() @@ -87,7 +90,9 @@ class CooldownsRepository: # Apply pagination offset = (query.page - 1) * query.size - stmt = stmt.offset(offset).limit(query.size).order_by(Cooldown.expires_at.desc()) + stmt = ( + stmt.offset(offset).limit(query.size).order_by(Cooldown.expires_at.desc()) + ) result = await self.session.execute(stmt) cooldowns = list(result.scalars().all()) diff --git a/src/hubgw/repositories/homes_repo.py b/src/hubgw/repositories/homes_repo.py index 4a832fa..5f7620e 100644 --- a/src/hubgw/repositories/homes_repo.py +++ b/src/hubgw/repositories/homes_repo.py @@ -1,11 +1,12 @@ """Homes repository.""" -from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy import select, update, delete, func -from sqlalchemy.dialects.postgresql import insert from typing import List, Optional from uuid import UUID +from sqlalchemy import delete, func, select +from sqlalchemy.dialects.postgresql import insert +from sqlalchemy.ext.asyncio import AsyncSession + from hubgw.models.home import Home from hubgw.schemas.homes import HomeCreate, HomeQuery @@ -27,10 +28,10 @@ class HomesRepository: z=request.z, yaw=request.yaw, pitch=request.pitch, - is_public=request.is_public + is_public=request.is_public, ) stmt = stmt.on_conflict_do_update( - index_elements=['player_uuid', 'name'], + index_elements=["player_uuid", "name"], set_={ "world": stmt.excluded.world, "x": stmt.excluded.x, @@ -39,7 +40,7 @@ class HomesRepository: "yaw": stmt.excluded.yaw, "pitch": stmt.excluded.pitch, "is_public": stmt.excluded.is_public, - } + }, ).returning(Home) result = await self.session.execute(stmt) await self.session.commit() @@ -51,12 +52,11 @@ class HomesRepository: result = await self.session.execute(stmt) return result.scalar_one_or_none() - async def get_by_player_and_name(self, player_uuid: str, name: str) -> Optional[Home]: + async def get_by_player_and_name( + self, player_uuid: str, name: str + ) -> Optional[Home]: """Get home by player and name.""" - stmt = select(Home).where( - Home.player_uuid == player_uuid, - Home.name == name - ) + stmt = select(Home).where(Home.player_uuid == player_uuid, Home.name == name) result = await self.session.execute(stmt) return result.scalar_one_or_none() diff --git a/src/hubgw/repositories/kits_repo.py b/src/hubgw/repositories/kits_repo.py index 3a7c524..9851e62 100644 --- a/src/hubgw/repositories/kits_repo.py +++ b/src/hubgw/repositories/kits_repo.py @@ -1,10 +1,11 @@ """Kits repository.""" -from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy import select, delete, func -from typing import Optional, List -from uuid import UUID from datetime import datetime, timedelta +from typing import List, Optional +from uuid import UUID + +from sqlalchemy import delete, func, select +from sqlalchemy.ext.asyncio import AsyncSession from hubgw.models.cooldown import Cooldown from hubgw.schemas.kits import KitCooldownQuery @@ -13,7 +14,7 @@ from hubgw.schemas.kits import KitCooldownQuery class KitsRepository: """ Kits repository for database operations. - + NOTE: This repository manages cooldowns for kits, not the kits themselves. Consider refactoring to move this logic to CooldownsRepository to follow SRP. """ @@ -21,24 +22,28 @@ class KitsRepository: def __init__(self, session: AsyncSession): self.session = session - async def get_active_cooldown(self, player_uuid: UUID, kit_name: str) -> Optional[Cooldown]: + async def get_active_cooldown( + self, player_uuid: UUID, kit_name: str + ) -> Optional[Cooldown]: """Check if player has active cooldown for kit.""" stmt = select(Cooldown).where( Cooldown.player_uuid == str(player_uuid), Cooldown.cooldown_type == f"kit_{kit_name}", - Cooldown.expires_at > func.now() + Cooldown.expires_at > func.now(), ) result = await self.session.execute(stmt) return result.scalar_one_or_none() - async def create_cooldown(self, player_uuid: UUID, kit_name: str, cooldown_seconds: int) -> Cooldown: + async def create_cooldown( + self, player_uuid: UUID, kit_name: str, cooldown_seconds: int + ) -> Cooldown: """Create cooldown for kit.""" expires_at = datetime.utcnow() + timedelta(seconds=cooldown_seconds) cooldown = Cooldown( player_uuid=str(player_uuid), cooldown_type=f"kit_{kit_name}", expires_at=expires_at, - cooldown_seconds=cooldown_seconds + cooldown_seconds=cooldown_seconds, ) self.session.add(cooldown) await self.session.commit() @@ -48,8 +53,7 @@ class KitsRepository: async def get_cooldown_by_id(self, cooldown_id: UUID) -> Optional[Cooldown]: """Get kit cooldown by id.""" stmt = select(Cooldown).where( - Cooldown.id == cooldown_id, - Cooldown.cooldown_type.like("kit_%") + Cooldown.id == cooldown_id, Cooldown.cooldown_type.like("kit_%") ) result = await self.session.execute(stmt) return result.scalar_one_or_none() @@ -57,25 +61,32 @@ class KitsRepository: async def delete_cooldown_by_id(self, cooldown_id: UUID) -> bool: """Delete kit cooldown by id.""" stmt = delete(Cooldown).where( - Cooldown.id == cooldown_id, - Cooldown.cooldown_type.like("kit_%") + Cooldown.id == cooldown_id, Cooldown.cooldown_type.like("kit_%") ) result = await self.session.execute(stmt) await self.session.commit() return result.rowcount > 0 - async def query_cooldowns(self, query: KitCooldownQuery) -> tuple[List[Cooldown], int]: + async def query_cooldowns( + self, query: KitCooldownQuery + ) -> tuple[List[Cooldown], int]: """Query kit cooldowns with filters and pagination.""" stmt = select(Cooldown).where(Cooldown.cooldown_type.like("kit_%")) - count_stmt = select(func.count(Cooldown.id)).where(Cooldown.cooldown_type.like("kit_%")) + count_stmt = select(func.count(Cooldown.id)).where( + Cooldown.cooldown_type.like("kit_%") + ) # Apply filters if query.player_uuid: stmt = stmt.where(Cooldown.player_uuid == str(query.player_uuid)) - count_stmt = count_stmt.where(Cooldown.player_uuid == str(query.player_uuid)) + count_stmt = count_stmt.where( + Cooldown.player_uuid == str(query.player_uuid) + ) if query.kit_name: stmt = stmt.where(Cooldown.cooldown_type == f"kit_{query.kit_name}") - count_stmt = count_stmt.where(Cooldown.cooldown_type == f"kit_{query.kit_name}") + count_stmt = count_stmt.where( + Cooldown.cooldown_type == f"kit_{query.kit_name}" + ) if query.is_active is not None: if query.is_active: stmt = stmt.where(Cooldown.expires_at > func.now()) @@ -90,7 +101,9 @@ class KitsRepository: # Apply pagination offset = (query.page - 1) * query.size - stmt = stmt.offset(offset).limit(query.size).order_by(Cooldown.expires_at.desc()) + stmt = ( + stmt.offset(offset).limit(query.size).order_by(Cooldown.expires_at.desc()) + ) result = await self.session.execute(stmt) cooldowns = list(result.scalars().all()) diff --git a/src/hubgw/repositories/luckperms_repo.py b/src/hubgw/repositories/luckperms_repo.py index 4349594..1d3496b 100644 --- a/src/hubgw/repositories/luckperms_repo.py +++ b/src/hubgw/repositories/luckperms_repo.py @@ -1,46 +1,54 @@ """LuckPerms repository.""" -from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy import select from typing import List, Optional -from hubgw.models.luckperms import LuckPermsPlayer, LuckPermsGroup, LuckPermsUserPermission +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from hubgw.models.luckperms import (LuckPermsGroup, LuckPermsPlayer, + LuckPermsUserPermission) class LuckPermsRepository: """LuckPerms repository for database operations.""" - + def __init__(self, session: AsyncSession): self.session = session - + async def get_player(self, uuid: str) -> Optional[LuckPermsPlayer]: """Get player by UUID.""" stmt = select(LuckPermsPlayer).where(LuckPermsPlayer.uuid == uuid) result = await self.session.execute(stmt) return result.scalar_one_or_none() - + async def get_player_by_username(self, username: str) -> Optional[LuckPermsPlayer]: """Get player by username.""" stmt = select(LuckPermsPlayer).where(LuckPermsPlayer.username == username) result = await self.session.execute(stmt) return result.scalar_one_or_none() - + async def get_group(self, name: str) -> Optional[LuckPermsGroup]: """Get group by name.""" stmt = select(LuckPermsGroup).where(LuckPermsGroup.name == name) result = await self.session.execute(stmt) return result.scalar_one_or_none() - + async def get_user_permissions(self, uuid: str) -> List[LuckPermsUserPermission]: """Get user permissions.""" - stmt = select(LuckPermsUserPermission).where(LuckPermsUserPermission.uuid == uuid) + stmt = select(LuckPermsUserPermission).where( + LuckPermsUserPermission.uuid == uuid + ) result = await self.session.execute(stmt) return list(result.scalars().all()) - - async def create_player(self, uuid: str, username: str, primary_group: str) -> LuckPermsPlayer: + + 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) + 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 \ No newline at end of file + return player diff --git a/src/hubgw/repositories/punishments_repo.py b/src/hubgw/repositories/punishments_repo.py index bb97dd7..5cae222 100644 --- a/src/hubgw/repositories/punishments_repo.py +++ b/src/hubgw/repositories/punishments_repo.py @@ -1,21 +1,23 @@ """Punishments repository.""" -from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy import select, insert, update, func -from typing import List, Optional -from uuid import UUID from datetime import datetime +from typing import List, Optional + +from sqlalchemy import func, select +from sqlalchemy.ext.asyncio import AsyncSession from hubgw.models.punishment import Punishment -from hubgw.schemas.punishments import PunishmentCreateRequest, PunishmentRevokeRequest, PunishmentQuery +from hubgw.schemas.punishments import (PunishmentCreateRequest, + PunishmentQuery, + PunishmentRevokeRequest) class PunishmentsRepository: """Punishments repository for database operations.""" - + def __init__(self, session: AsyncSession): self.session = session - + async def create(self, request: PunishmentCreateRequest) -> Punishment: """Create punishment.""" punishment = Punishment( @@ -28,80 +30,86 @@ class PunishmentsRepository: staff_name=request.staff_name, expires_at=request.expires_at, evidence_url=request.evidence_url, - notes=request.notes + notes=request.notes, ) self.session.add(punishment) await self.session.commit() await self.session.refresh(punishment) return punishment - + async def revoke(self, request: PunishmentRevokeRequest) -> Optional[Punishment]: """Revoke punishment.""" stmt = select(Punishment).where(Punishment.id == request.punishment_id) result = await self.session.execute(stmt) punishment = result.scalar_one_or_none() - + if not punishment: return None - + punishment.is_active = False punishment.revoked_at = datetime.utcnow() punishment.revoked_by = request.revoked_by punishment.revoked_reason = request.revoked_reason - + await self.session.commit() await self.session.refresh(punishment) return punishment - + async def query(self, query: PunishmentQuery) -> tuple[List[Punishment], int]: """Query punishments with filters and pagination.""" stmt = select(Punishment) count_stmt = select(func.count(Punishment.id)) - + # Apply filters if query.player_uuid: stmt = stmt.where(Punishment.player_uuid == query.player_uuid) count_stmt = count_stmt.where(Punishment.player_uuid == query.player_uuid) - + if query.punishment_type: stmt = stmt.where(Punishment.punishment_type == query.punishment_type) - count_stmt = count_stmt.where(Punishment.punishment_type == query.punishment_type) - + count_stmt = count_stmt.where( + Punishment.punishment_type == query.punishment_type + ) + if query.is_active is not None: stmt = stmt.where(Punishment.is_active == query.is_active) count_stmt = count_stmt.where(Punishment.is_active == query.is_active) - + # Get total count count_result = await self.session.execute(count_stmt) total = count_result.scalar() - + # Apply pagination offset = (query.page - 1) * query.size - stmt = stmt.offset(offset).limit(query.size).order_by(Punishment.created_at.desc()) - + stmt = ( + stmt.offset(offset).limit(query.size).order_by(Punishment.created_at.desc()) + ) + result = await self.session.execute(stmt) punishments = list(result.scalars().all()) - + return punishments, total - + async def get_active_ban(self, player_uuid: str) -> Optional[Punishment]: """Get active ban for player.""" stmt = select(Punishment).where( Punishment.player_uuid == player_uuid, - Punishment.punishment_type.in_(['BAN', 'TEMPBAN']), + Punishment.punishment_type.in_(["BAN", "TEMPBAN"]), Punishment.is_active == True, - (Punishment.expires_at.is_(None)) | (Punishment.expires_at > datetime.utcnow()) + (Punishment.expires_at.is_(None)) + | (Punishment.expires_at > datetime.utcnow()), ) result = await self.session.execute(stmt) return result.scalar_one_or_none() - + async def get_active_mute(self, player_uuid: str) -> Optional[Punishment]: """Get active mute for player.""" stmt = select(Punishment).where( Punishment.player_uuid == player_uuid, - Punishment.punishment_type.in_(['MUTE', 'TEMPMUTE']), + Punishment.punishment_type.in_(["MUTE", "TEMPMUTE"]), Punishment.is_active == True, - (Punishment.expires_at.is_(None)) | (Punishment.expires_at > datetime.utcnow()) + (Punishment.expires_at.is_(None)) + | (Punishment.expires_at > datetime.utcnow()), ) result = await self.session.execute(stmt) return result.scalar_one_or_none() diff --git a/src/hubgw/repositories/teleport_history_repo.py b/src/hubgw/repositories/teleport_history_repo.py index 9fef8e1..d339ca3 100644 --- a/src/hubgw/repositories/teleport_history_repo.py +++ b/src/hubgw/repositories/teleport_history_repo.py @@ -1,12 +1,14 @@ """Teleport History repository.""" -from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy import select, func from typing import List, Optional from uuid import UUID +from sqlalchemy import func, select +from sqlalchemy.ext.asyncio import AsyncSession + from hubgw.models.teleport_history import TeleportHistory -from hubgw.schemas.teleport_history import TeleportHistoryCreate, TeleportHistoryQuery +from hubgw.schemas.teleport_history import (TeleportHistoryCreate, + TeleportHistoryQuery) class TeleportHistoryRepository: @@ -29,7 +31,9 @@ class TeleportHistoryRepository: result = await self.session.execute(stmt) return result.scalar_one_or_none() - async def query(self, query: TeleportHistoryQuery) -> tuple[List[TeleportHistory], int]: + async def query( + self, query: TeleportHistoryQuery + ) -> tuple[List[TeleportHistory], int]: """Query teleport history entries with filters and pagination.""" stmt = select(TeleportHistory) count_stmt = select(func.count(TeleportHistory.id)) @@ -37,13 +41,17 @@ class TeleportHistoryRepository: # Apply filters if query.player_uuid: stmt = stmt.where(TeleportHistory.player_uuid == query.player_uuid) - count_stmt = count_stmt.where(TeleportHistory.player_uuid == query.player_uuid) + count_stmt = count_stmt.where( + TeleportHistory.player_uuid == query.player_uuid + ) if query.tp_type: stmt = stmt.where(TeleportHistory.tp_type == query.tp_type) count_stmt = count_stmt.where(TeleportHistory.tp_type == query.tp_type) if query.from_world: stmt = stmt.where(TeleportHistory.from_world == query.from_world) - count_stmt = count_stmt.where(TeleportHistory.from_world == query.from_world) + count_stmt = count_stmt.where( + TeleportHistory.from_world == query.from_world + ) if query.to_world: stmt = stmt.where(TeleportHistory.to_world == query.to_world) count_stmt = count_stmt.where(TeleportHistory.to_world == query.to_world) @@ -54,9 +62,13 @@ class TeleportHistoryRepository: # Apply pagination offset = (query.page - 1) * query.size - stmt = stmt.offset(offset).limit(query.size).order_by(TeleportHistory.created_at.desc()) + stmt = ( + stmt.offset(offset) + .limit(query.size) + .order_by(TeleportHistory.created_at.desc()) + ) result = await self.session.execute(stmt) entries = list(result.scalars().all()) - return entries, total \ No newline at end of file + return entries, total diff --git a/src/hubgw/repositories/users_repo.py b/src/hubgw/repositories/users_repo.py index 0a649ee..eed60d4 100644 --- a/src/hubgw/repositories/users_repo.py +++ b/src/hubgw/repositories/users_repo.py @@ -1,18 +1,19 @@ """User repository.""" -from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy import select from typing import Optional +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + from hubgw.models.users import User class UserRepository: """User repository for database operations.""" - + def __init__(self, session: AsyncSession): self.session = session - + async def get_game_id_by_name(self, name: str) -> Optional[str]: """Get game_id by user name.""" stmt = select(User.game_id).where(User.name == name) diff --git a/src/hubgw/repositories/warps_repo.py b/src/hubgw/repositories/warps_repo.py index 9bbec9c..c27004a 100644 --- a/src/hubgw/repositories/warps_repo.py +++ b/src/hubgw/repositories/warps_repo.py @@ -1,12 +1,13 @@ """Warps repository.""" -from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy import select, insert, update, delete, func from typing import List, Optional from uuid import UUID +from sqlalchemy import delete, func, select +from sqlalchemy.ext.asyncio import AsyncSession + from hubgw.models.warp import Warp -from hubgw.schemas.warps import WarpCreateRequest, WarpUpdateRequest, WarpQuery +from hubgw.schemas.warps import WarpCreateRequest, WarpQuery, WarpUpdateRequest class WarpsRepository: @@ -26,7 +27,7 @@ class WarpsRepository: yaw=request.yaw, pitch=request.pitch, is_public=request.is_public, - description=request.description + description=request.description, ) self.session.add(warp) await self.session.commit() @@ -54,7 +55,7 @@ class WarpsRepository: if not warp: return None - update_data = request.model_dump(exclude_unset=True, exclude={'name'}) + update_data = request.model_dump(exclude_unset=True, exclude={"name"}) for field, value in update_data.items(): setattr(warp, field, value) diff --git a/src/hubgw/repositories/whitelist_repo.py b/src/hubgw/repositories/whitelist_repo.py index d949397..69524b3 100644 --- a/src/hubgw/repositories/whitelist_repo.py +++ b/src/hubgw/repositories/whitelist_repo.py @@ -1,13 +1,15 @@ """Whitelist repository.""" -from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy import select, delete, func, and_ from typing import List, Optional from uuid import UUID -from datetime import datetime + +from sqlalchemy import and_, delete, func, select +from sqlalchemy.ext.asyncio import AsyncSession from hubgw.models.whitelist import WhitelistEntry -from hubgw.schemas.whitelist import WhitelistAddRequest, WhitelistCheckRequest, WhitelistQuery, WhitelistRemoveRequest +from hubgw.schemas.whitelist import (WhitelistAddRequest, + WhitelistCheckRequest, WhitelistQuery, + WhitelistRemoveRequest) class WhitelistRepository: @@ -25,7 +27,7 @@ class WhitelistRepository: added_at=request.added_at, expires_at=request.expires_at, is_active=request.is_active, - reason=request.reason + reason=request.reason, ) self.session.add(entry) await self.session.commit() @@ -63,14 +65,22 @@ class WhitelistRepository: count_stmt = select(func.count(WhitelistEntry.id)) if query.player_name: - stmt = stmt.where(WhitelistEntry.player_name.ilike(f"%{query.player_name}%")) - count_stmt = count_stmt.where(WhitelistEntry.player_name.ilike(f"%{query.player_name}%")) + stmt = stmt.where( + WhitelistEntry.player_name.ilike(f"%{query.player_name}%") + ) + count_stmt = count_stmt.where( + WhitelistEntry.player_name.ilike(f"%{query.player_name}%") + ) if query.player_uuid: stmt = stmt.where(WhitelistEntry.player_uuid == query.player_uuid) - count_stmt = count_stmt.where(WhitelistEntry.player_uuid == query.player_uuid) + count_stmt = count_stmt.where( + WhitelistEntry.player_uuid == query.player_uuid + ) if query.added_by: stmt = stmt.where(WhitelistEntry.added_by.ilike(f"%{query.added_by}%")) - count_stmt = count_stmt.where(WhitelistEntry.added_by.ilike(f"%{query.added_by}%")) + count_stmt = count_stmt.where( + WhitelistEntry.added_by.ilike(f"%{query.added_by}%") + ) if query.is_active is not None: stmt = stmt.where(WhitelistEntry.is_active == query.is_active) count_stmt = count_stmt.where(WhitelistEntry.is_active == query.is_active) @@ -79,20 +89,23 @@ class WhitelistRepository: total = count_result.scalar_one() offset = (query.page - 1) * query.size - stmt = stmt.offset(offset).limit(query.size).order_by(WhitelistEntry.added_at.desc()) + stmt = ( + stmt.offset(offset) + .limit(query.size) + .order_by(WhitelistEntry.added_at.desc()) + ) result = await self.session.execute(stmt) entries = list(result.scalars().all()) return entries, total - async def check(self, request: WhitelistCheckRequest) -> Optional[WhitelistEntry]: """Check if player is whitelisted.""" stmt = select(WhitelistEntry).where( and_( WhitelistEntry.player_name == request.player_name, - WhitelistEntry.is_active == True + WhitelistEntry.is_active == True, ) ) result = await self.session.execute(stmt) @@ -100,7 +113,9 @@ class WhitelistRepository: async def remove(self, request: WhitelistRemoveRequest) -> bool: """Remove player from whitelist.""" - stmt = delete(WhitelistEntry).where(WhitelistEntry.player_name == request.player_name) + stmt = delete(WhitelistEntry).where( + WhitelistEntry.player_name == request.player_name + ) result = await self.session.execute(stmt) await self.session.commit() return result.rowcount > 0 diff --git a/src/hubgw/schemas/audit.py b/src/hubgw/schemas/audit.py index 9991f0f..6ae9cd4 100644 --- a/src/hubgw/schemas/audit.py +++ b/src/hubgw/schemas/audit.py @@ -1,14 +1,15 @@ """Audit schemas.""" -from pydantic import BaseModel -from typing import List, Dict, Any from datetime import datetime +from typing import List from uuid import UUID +from pydantic import BaseModel + class CommandAuditRequest(BaseModel): """Command audit request schema.""" - + player_uuid: UUID player_name: str command: str @@ -19,5 +20,5 @@ class CommandAuditRequest(BaseModel): class CommandAuditResponse(BaseModel): """Command audit response schema.""" - + accepted: int diff --git a/src/hubgw/schemas/common.py b/src/hubgw/schemas/common.py index 1a5b07c..7e1e309 100644 --- a/src/hubgw/schemas/common.py +++ b/src/hubgw/schemas/common.py @@ -1,21 +1,21 @@ """Common schemas.""" -from pydantic import BaseModel -from typing import Optional from datetime import datetime -from uuid import UUID +from typing import Optional + +from pydantic import BaseModel class BaseSchema(BaseModel): """Base schema with common fields.""" - + created_at: datetime updated_at: datetime class ErrorResponse(BaseModel): """Error response schema.""" - + message: str code: str details: Optional[dict] = None @@ -23,10 +23,10 @@ class ErrorResponse(BaseModel): class PaginationParams(BaseModel): """Pagination parameters.""" - + page: int = 1 size: int = 20 - + @property def offset(self) -> int: return (self.page - 1) * self.size @@ -34,7 +34,7 @@ class PaginationParams(BaseModel): class PaginatedResponse(BaseModel): """Paginated response schema.""" - + items: list total: int page: int diff --git a/src/hubgw/schemas/cooldowns.py b/src/hubgw/schemas/cooldowns.py index b591987..a810a83 100644 --- a/src/hubgw/schemas/cooldowns.py +++ b/src/hubgw/schemas/cooldowns.py @@ -1,22 +1,24 @@ """Cooldown schemas.""" -from pydantic import BaseModel -from uuid import UUID from datetime import datetime from typing import Optional +from uuid import UUID + +from pydantic import BaseModel + from hubgw.schemas.common import BaseSchema, PaginationParams class CooldownCheckRequest(BaseModel): """Cooldown check request schema.""" - + player_uuid: str cooldown_type: str class CooldownCheckResponse(BaseModel): """Cooldown check response schema.""" - + is_active: bool expires_at: Optional[datetime] = None remaining_seconds: Optional[int] = None @@ -24,7 +26,7 @@ class CooldownCheckResponse(BaseModel): class CooldownBase(BaseModel): """Base cooldown schema.""" - + cooldown_type: str expires_at: datetime cooldown_seconds: int @@ -33,13 +35,13 @@ class CooldownBase(BaseModel): class CooldownCreate(CooldownBase): """Cooldown creation schema.""" - + player_uuid: str class Cooldown(CooldownBase, BaseSchema): """Cooldown schema.""" - + id: UUID player_uuid: str diff --git a/src/hubgw/schemas/homes.py b/src/hubgw/schemas/homes.py index 4b402f9..34b4014 100644 --- a/src/hubgw/schemas/homes.py +++ b/src/hubgw/schemas/homes.py @@ -1,15 +1,16 @@ """Home schemas.""" -from pydantic import BaseModel from typing import Optional -from datetime import datetime from uuid import UUID + +from pydantic import BaseModel + from hubgw.schemas.common import BaseSchema, PaginationParams class HomeBase(BaseModel): """Base home schema.""" - + name: str world: str x: float @@ -22,13 +23,13 @@ class HomeBase(BaseModel): class HomeCreate(HomeBase): """Home creation schema.""" - + player_uuid: str class HomeUpdate(BaseModel): """Home update schema.""" - + name: Optional[str] = None world: Optional[str] = None x: Optional[float] = None @@ -41,33 +42,33 @@ class HomeUpdate(BaseModel): class HomeUpsertRequest(HomeBase): """Home upsert request schema.""" - + player_uuid: str class Home(HomeBase, BaseSchema): """Home response schema.""" - + id: UUID player_uuid: str class HomeGetRequest(BaseModel): """Home get request schema.""" - + player_uuid: str name: str class HomeGetResponse(Home): """Home get response schema.""" - + pass class HomeListResponse(BaseModel): """Home list response schema.""" - + homes: list[Home] total: int diff --git a/src/hubgw/schemas/kits.py b/src/hubgw/schemas/kits.py index ff24740..0de527d 100644 --- a/src/hubgw/schemas/kits.py +++ b/src/hubgw/schemas/kits.py @@ -1,21 +1,23 @@ """Kit schemas.""" -from pydantic import BaseModel -from uuid import UUID from typing import Optional +from uuid import UUID + +from pydantic import BaseModel + from hubgw.schemas.common import PaginationParams class KitClaimRequest(BaseModel): """Kit claim request schema.""" - + player_uuid: UUID kit_name: str class KitClaimResponse(BaseModel): """Kit claim response schema.""" - + success: bool message: str cooldown_remaining: Optional[int] = None diff --git a/src/hubgw/schemas/luckperms.py b/src/hubgw/schemas/luckperms.py index 7392ebf..eea3dbc 100644 --- a/src/hubgw/schemas/luckperms.py +++ b/src/hubgw/schemas/luckperms.py @@ -1,13 +1,15 @@ """LuckPerms schemas.""" -from pydantic import BaseModel from typing import Optional + +from pydantic import BaseModel + from hubgw.schemas.common import BaseSchema class LuckPermsPlayer(BaseSchema): """LuckPerms Player schema.""" - + uuid: str username: str primary_group: str @@ -15,13 +17,13 @@ class LuckPermsPlayer(BaseSchema): class LuckPermsGroup(BaseSchema): """LuckPerms Group schema.""" - + name: str class LuckPermsUserPermission(BaseSchema): """LuckPerms User Permission schema.""" - + id: int uuid: str permission: str @@ -34,12 +36,12 @@ class LuckPermsUserPermission(BaseSchema): class LuckPermsPlayerWithPermissions(LuckPermsPlayer): """LuckPerms Player with permissions schema.""" - + permissions: list[LuckPermsUserPermission] = [] class LuckPermsPlayerCreateRequest(BaseModel): """Request schema for creating a LuckPerms player.""" - + username: str - primary_group: str = "default" \ No newline at end of file + primary_group: str = "default" diff --git a/src/hubgw/schemas/punishments.py b/src/hubgw/schemas/punishments.py index d44c34b..0c7f13e 100644 --- a/src/hubgw/schemas/punishments.py +++ b/src/hubgw/schemas/punishments.py @@ -1,17 +1,18 @@ """Punishment schemas.""" -from pydantic import BaseModel -from typing import Optional from datetime import datetime +from ipaddress import IPv4Address +from typing import Optional from uuid import UUID + +from pydantic import BaseModel + from hubgw.schemas.common import BaseSchema -from ipaddress import IPv4Address - class PunishmentCreateRequest(BaseModel): """Punishment create request schema.""" - + player_uuid: str player_name: str player_ip: Optional[IPv4Address] = None @@ -26,7 +27,7 @@ class PunishmentCreateRequest(BaseModel): class PunishmentRevokeRequest(BaseModel): """Punishment revoke request schema.""" - + punishment_id: UUID revoked_by: str revoked_reason: str @@ -34,7 +35,7 @@ class PunishmentRevokeRequest(BaseModel): class PunishmentQuery(BaseModel): """Punishment query schema.""" - + player_uuid: Optional[str] = None punishment_type: Optional[str] = None is_active: Optional[bool] = None @@ -44,7 +45,7 @@ class PunishmentQuery(BaseModel): class PunishmentBase(BaseSchema): """Base punishment schema.""" - + id: UUID player_uuid: str player_name: str @@ -64,7 +65,7 @@ class PunishmentBase(BaseSchema): class PunishmentListResponse(BaseModel): """Punishment list response schema.""" - + punishments: list[PunishmentBase] total: int page: int @@ -74,13 +75,13 @@ class PunishmentListResponse(BaseModel): class ActiveBanStatusResponse(BaseModel): """Active ban status response schema.""" - + is_banned: bool punishment: Optional[PunishmentBase] = None class ActiveMuteStatusResponse(BaseModel): """Active mute status response schema.""" - + is_muted: bool punishment: Optional[PunishmentBase] = None diff --git a/src/hubgw/schemas/teleport_history.py b/src/hubgw/schemas/teleport_history.py index cd81bcd..c814d85 100644 --- a/src/hubgw/schemas/teleport_history.py +++ b/src/hubgw/schemas/teleport_history.py @@ -1,15 +1,17 @@ """Teleport History schemas.""" -from pydantic import BaseModel -from typing import Optional from datetime import datetime +from typing import Optional from uuid import UUID + +from pydantic import BaseModel + from hubgw.schemas.common import BaseSchema, PaginationParams class TeleportHistoryBase(BaseModel): """Base teleport history schema.""" - + from_world: Optional[str] = None from_x: Optional[float] = None from_y: Optional[float] = None @@ -24,13 +26,13 @@ class TeleportHistoryBase(BaseModel): class TeleportHistoryCreate(TeleportHistoryBase): """Teleport history creation schema.""" - + player_uuid: str class TeleportHistory(TeleportHistoryBase, BaseSchema): """Teleport history schema.""" - + id: UUID player_uuid: str created_at: datetime @@ -42,4 +44,4 @@ class TeleportHistoryQuery(PaginationParams): player_uuid: Optional[str] = None tp_type: Optional[str] = None from_world: Optional[str] = None - to_world: Optional[str] = None \ No newline at end of file + to_world: Optional[str] = None diff --git a/src/hubgw/schemas/users.py b/src/hubgw/schemas/users.py index db5ffc7..9fef304 100644 --- a/src/hubgw/schemas/users.py +++ b/src/hubgw/schemas/users.py @@ -1,10 +1,10 @@ """User schemas.""" + from pydantic import BaseModel -from typing import Optional class GetUserGameIdResponse(BaseModel): """Response schema for getting user's game ID.""" - + game_id: str diff --git a/src/hubgw/schemas/warps.py b/src/hubgw/schemas/warps.py index d38829e..259565a 100644 --- a/src/hubgw/schemas/warps.py +++ b/src/hubgw/schemas/warps.py @@ -1,15 +1,16 @@ """Warp schemas.""" -from pydantic import BaseModel from typing import Optional -from datetime import datetime from uuid import UUID + +from pydantic import BaseModel + from hubgw.schemas.common import BaseSchema, PaginationParams class WarpBase(BaseModel): """Base warp schema.""" - + name: str world: str x: float @@ -23,13 +24,13 @@ class WarpBase(BaseModel): class WarpCreateRequest(WarpBase): """Warp create request schema.""" - + pass class WarpUpdateRequest(BaseModel): """Warp update request schema.""" - + name: str world: Optional[str] = None x: Optional[float] = None @@ -43,31 +44,31 @@ class WarpUpdateRequest(BaseModel): class WarpDeleteRequest(BaseModel): """Warp delete request schema.""" - + name: str class WarpGetRequest(BaseModel): """Warp get request schema.""" - + name: str class Warp(WarpBase, BaseSchema): """Warp response schema.""" - + id: UUID class WarpGetResponse(Warp): """Warp get response schema.""" - + pass class WarpQuery(PaginationParams): """Warp query schema.""" - + name: Optional[str] = None world: Optional[str] = None is_public: Optional[bool] = None @@ -75,7 +76,7 @@ class WarpQuery(PaginationParams): class WarpListResponse(BaseModel): """Warp list response schema.""" - + warps: list[Warp] total: int page: int diff --git a/src/hubgw/schemas/whitelist.py b/src/hubgw/schemas/whitelist.py index 04907f7..3189454 100644 --- a/src/hubgw/schemas/whitelist.py +++ b/src/hubgw/schemas/whitelist.py @@ -1,14 +1,15 @@ """Whitelist schemas.""" -from pydantic import BaseModel, ConfigDict -from typing import Optional from datetime import datetime +from typing import Optional from uuid import UUID +from pydantic import BaseModel, ConfigDict + class WhitelistAddRequest(BaseModel): """Whitelist add request schema.""" - + player_name: str player_uuid: str added_by: str @@ -20,25 +21,26 @@ class WhitelistAddRequest(BaseModel): class WhitelistRemoveRequest(BaseModel): """Whitelist remove request schema.""" - + player_name: str class WhitelistCheckRequest(BaseModel): """Whitelist check request schema.""" - + player_name: str class WhitelistCheckResponse(BaseModel): """Whitelist check response schema.""" - + is_whitelisted: bool player_uuid: Optional[str] = None class WhitelistEntry(BaseModel): """Whitelist entry schema.""" + model_config = ConfigDict(from_attributes=True) id: UUID @@ -53,7 +55,7 @@ class WhitelistEntry(BaseModel): class WhitelistListResponse(BaseModel): """Whitelist list response schema.""" - + entries: list[WhitelistEntry] total: int diff --git a/src/hubgw/services/audit_service.py b/src/hubgw/services/audit_service.py index 26d1c82..506e57d 100644 --- a/src/hubgw/services/audit_service.py +++ b/src/hubgw/services/audit_service.py @@ -1,15 +1,16 @@ """Audit service.""" from sqlalchemy.ext.asyncio import AsyncSession + from hubgw.schemas.audit import CommandAuditRequest, CommandAuditResponse class AuditService: """Audit service for business logic.""" - + def __init__(self, session: AsyncSession): self.session = session - + async def log_command(self, request: CommandAuditRequest) -> CommandAuditResponse: """Log command execution for audit.""" # In a real implementation, this would store the command in an audit table diff --git a/src/hubgw/services/cooldowns_service.py b/src/hubgw/services/cooldowns_service.py index 32e6ed7..89f2e25 100644 --- a/src/hubgw/services/cooldowns_service.py +++ b/src/hubgw/services/cooldowns_service.py @@ -1,39 +1,44 @@ """Cooldowns service.""" -from sqlalchemy.ext.asyncio import AsyncSession -from uuid import UUID from datetime import datetime +from sqlalchemy.ext.asyncio import AsyncSession + from hubgw.repositories.cooldowns_repo import CooldownsRepository -from hubgw.schemas.cooldowns import CooldownCheckRequest, CooldownCheckResponse, CooldownCreate +from hubgw.schemas.cooldowns import (CooldownCheckRequest, + CooldownCheckResponse, CooldownCreate) class CooldownsService: """Cooldowns service for business logic.""" - + def __init__(self, session: AsyncSession): self.repo = CooldownsRepository(session) - - async def check_cooldown(self, request: CooldownCheckRequest) -> CooldownCheckResponse: + + async def check_cooldown( + self, request: CooldownCheckRequest + ) -> CooldownCheckResponse: """Check cooldown status.""" cooldown = await self.repo.check(request.player_uuid, request.cooldown_type) - + if not cooldown: return CooldownCheckResponse(is_active=False) - - remaining_seconds = int((cooldown.expires_at - datetime.utcnow()).total_seconds()) - + + remaining_seconds = int( + (cooldown.expires_at - datetime.utcnow()).total_seconds() + ) + return CooldownCheckResponse( is_active=remaining_seconds > 0, expires_at=cooldown.expires_at, - remaining_seconds=max(0, remaining_seconds) + remaining_seconds=max(0, remaining_seconds), ) - + async def create_cooldown(self, request: CooldownCreate) -> None: """Create new cooldown.""" await self.repo.create( player_uuid=request.player_uuid, cooldown_type=request.cooldown_type, cooldown_seconds=request.cooldown_seconds, - metadata=request.metadata + metadata=request.metadata, ) diff --git a/src/hubgw/services/homes_service.py b/src/hubgw/services/homes_service.py index 6fbbf24..db2dd21 100644 --- a/src/hubgw/services/homes_service.py +++ b/src/hubgw/services/homes_service.py @@ -1,30 +1,32 @@ """Homes service.""" -from sqlalchemy.ext.asyncio import AsyncSession -from typing import List -from uuid import UUID -from hubgw.repositories.homes_repo import HomesRepository -from hubgw.schemas.homes import HomeUpsertRequest, HomeGetRequest, Home, HomeGetResponse, HomeListResponse +from sqlalchemy.ext.asyncio import AsyncSession + from hubgw.core.errors import NotFoundError +from hubgw.repositories.homes_repo import HomesRepository +from hubgw.schemas.homes import (Home, HomeGetRequest, HomeGetResponse, + HomeListResponse, HomeUpsertRequest) class HomesService: """Homes service for business logic.""" - + def __init__(self, session: AsyncSession): self.repo = HomesRepository(session) - + async def upsert_home(self, request: HomeUpsertRequest) -> Home: """Upsert home with business logic.""" return await self.repo.upsert(request) - + async def get_home(self, request: HomeGetRequest) -> HomeGetResponse: """Get home with business logic.""" home = await self.repo.get_by_request(request) if not home: - raise NotFoundError(f"Home '{request.name}' not found for player {request.player_uuid}") - + raise NotFoundError( + f"Home '{request.name}' not found for player {request.player_uuid}" + ) + return HomeGetResponse( id=home.id, player_uuid=home.player_uuid, @@ -37,13 +39,13 @@ class HomesService: pitch=home.pitch, is_public=home.is_public, created_at=home.created_at, - updated_at=home.updated_at + updated_at=home.updated_at, ) - + async def list_homes(self, player_uuid: str) -> HomeListResponse: """List homes with business logic.""" homes = await self.repo.list_by_player(player_uuid) - + home_list = [ Home( id=home.id, @@ -57,9 +59,9 @@ class HomesService: pitch=home.pitch, is_public=home.is_public, created_at=home.created_at, - updated_at=home.updated_at + updated_at=home.updated_at, ) for home in homes ] - + return HomeListResponse(homes=home_list, total=len(home_list)) diff --git a/src/hubgw/services/kits_service.py b/src/hubgw/services/kits_service.py index a0ef2ed..6887e3f 100644 --- a/src/hubgw/services/kits_service.py +++ b/src/hubgw/services/kits_service.py @@ -1,38 +1,41 @@ """Kits service.""" -from sqlalchemy.ext.asyncio import AsyncSession -from uuid import UUID -from datetime import datetime, timedelta +from datetime import datetime +from sqlalchemy.ext.asyncio import AsyncSession + +from hubgw.core.errors import CooldownActiveError from hubgw.repositories.kits_repo import KitsRepository from hubgw.schemas.kits import KitClaimRequest, KitClaimResponse -from hubgw.core.errors import CooldownActiveError class KitsService: """Kits service for business logic.""" - + def __init__(self, session: AsyncSession): self.repo = KitsRepository(session) - + async def claim_kit(self, request: KitClaimRequest) -> KitClaimResponse: """Claim kit with cooldown logic.""" # Check if player has active cooldown cooldown = await self.repo.check_cooldown(request.player_uuid, request.kit_name) - + if cooldown: - remaining_seconds = int((cooldown.expires_at - datetime.utcnow()).total_seconds()) + remaining_seconds = int( + (cooldown.expires_at - datetime.utcnow()).total_seconds() + ) if remaining_seconds > 0: raise CooldownActiveError( f"Kit '{request.kit_name}' is on cooldown for {remaining_seconds} seconds", - {"cooldown_remaining": remaining_seconds} + {"cooldown_remaining": remaining_seconds}, ) - + # Create cooldown (assuming 1 hour cooldown for all kits) cooldown_seconds = 3600 # 1 hour - await self.repo.create_cooldown(request.player_uuid, request.kit_name, cooldown_seconds) - - return KitClaimResponse( - success=True, - message=f"Kit '{request.kit_name}' claimed successfully" + await self.repo.create_cooldown( + request.player_uuid, request.kit_name, cooldown_seconds + ) + + return KitClaimResponse( + success=True, message=f"Kit '{request.kit_name}' claimed successfully" ) diff --git a/src/hubgw/services/luckperms_service.py b/src/hubgw/services/luckperms_service.py index a83d715..9805c28 100644 --- a/src/hubgw/services/luckperms_service.py +++ b/src/hubgw/services/luckperms_service.py @@ -1,75 +1,83 @@ """LuckPerms service.""" -from loguru import logger -from sqlalchemy.ext.asyncio import AsyncSession -from typing import List, TYPE_CHECKING +from typing import List +from sqlalchemy.ext.asyncio import AsyncSession + +from hubgw.core.errors import AlreadyExistsError, NotFoundError from hubgw.repositories.luckperms_repo import LuckPermsRepository -from hubgw.schemas.luckperms import ( - LuckPermsPlayer, LuckPermsGroup, LuckPermsUserPermission, - LuckPermsPlayerWithPermissions, LuckPermsPlayerCreateRequest -) -from hubgw.core.errors import NotFoundError, AlreadyExistsError +from hubgw.schemas.luckperms import (LuckPermsGroup, LuckPermsPlayer, + LuckPermsPlayerCreateRequest, + LuckPermsPlayerWithPermissions, + LuckPermsUserPermission) from hubgw.services.users_service import UserService class LuckPermsService: """LuckPerms service for business logic.""" - + def __init__(self, session: AsyncSession): self.repo = LuckPermsRepository(session) - + async def get_player(self, uuid: str) -> LuckPermsPlayer: """Get player by UUID.""" player = await self.repo.get_player(uuid) if not player: raise NotFoundError(f"Player {uuid} not found") 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.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.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.model_validate(perm) for perm in permissions] - - async def get_player_with_permissions(self, uuid: str) -> LuckPermsPlayerWithPermissions: + + async def get_player_with_permissions( + self, uuid: str + ) -> LuckPermsPlayerWithPermissions: """Get player with permissions.""" player = await self.repo.get_player(uuid) if not player: raise NotFoundError(f"Player {uuid} not found") - + permissions = await self.repo.get_user_permissions(uuid) - - permission_schemas = [LuckPermsUserPermission.model_validate(perm) for perm in permissions] - - return LuckPermsPlayerWithPermissions.model_validate(player, permissions=permission_schemas) - - async def create_player(self, request: LuckPermsPlayerCreateRequest, user_service: UserService) -> LuckPermsPlayer: + + permission_schemas = [ + LuckPermsUserPermission.model_validate(perm) for perm in permissions + ] + + return LuckPermsPlayerWithPermissions.model_validate( + player, permissions=permission_schemas + ) + + async def create_player( + self, request: LuckPermsPlayerCreateRequest, user_service: UserService + ) -> 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") - + raise AlreadyExistsError( + f"Player with username {request.username} already exists" + ) + user_game_id = await user_service.get_game_id_by_name(request.username) uuid = user_game_id.game_id - + created_player = await self.repo.create_player( - uuid=uuid, - username=request.username, - primary_group=request.primary_group + uuid=uuid, username=request.username, primary_group=request.primary_group ) - + return created_player diff --git a/src/hubgw/services/punishments_service.py b/src/hubgw/services/punishments_service.py index 830bb2b..27d5c22 100644 --- a/src/hubgw/services/punishments_service.py +++ b/src/hubgw/services/punishments_service.py @@ -1,27 +1,30 @@ """Punishments service.""" -from sqlalchemy.ext.asyncio import AsyncSession -from typing import List from uuid import UUID -from hubgw.repositories.punishments_repo import PunishmentsRepository -from hubgw.schemas.punishments import ( - PunishmentCreateRequest, PunishmentRevokeRequest, PunishmentQuery, - PunishmentBase, PunishmentListResponse, ActiveBanStatusResponse, ActiveMuteStatusResponse -) +from sqlalchemy.ext.asyncio import AsyncSession + from hubgw.core.errors import NotFoundError +from hubgw.repositories.punishments_repo import PunishmentsRepository +from hubgw.schemas.punishments import (ActiveBanStatusResponse, + ActiveMuteStatusResponse, + PunishmentBase, PunishmentCreateRequest, + PunishmentListResponse, PunishmentQuery, + PunishmentRevokeRequest) class PunishmentsService: """Punishments service for business logic.""" - + def __init__(self, session: AsyncSession): self.repo = PunishmentsRepository(session) - - async def create_punishment(self, request: PunishmentCreateRequest) -> PunishmentBase: + + async def create_punishment( + self, request: PunishmentCreateRequest + ) -> PunishmentBase: """Create punishment with business logic.""" punishment = await self.repo.create(request) - + return PunishmentBase( id=punishment.id, player_uuid=punishment.player_uuid, @@ -38,15 +41,17 @@ class PunishmentsService: revoked_by=punishment.revoked_by, revoked_reason=punishment.revoked_reason, evidence_url=punishment.evidence_url, - notes=punishment.notes + notes=punishment.notes, ) - - async def revoke_punishment(self, request: PunishmentRevokeRequest) -> PunishmentBase: + + async def revoke_punishment( + self, request: PunishmentRevokeRequest + ) -> PunishmentBase: """Revoke punishment with business logic.""" punishment = await self.repo.revoke(request) if not punishment: raise NotFoundError(f"Punishment {request.punishment_id} not found") - + return PunishmentBase( id=punishment.id, player_uuid=punishment.player_uuid, @@ -63,13 +68,13 @@ class PunishmentsService: revoked_by=punishment.revoked_by, revoked_reason=punishment.revoked_reason, evidence_url=punishment.evidence_url, - notes=punishment.notes + notes=punishment.notes, ) - + async def query_punishments(self, query: PunishmentQuery) -> PunishmentListResponse: """Query punishments with business logic.""" punishments, total = await self.repo.query(query) - + punishment_list = [ PunishmentBase( id=p.id, @@ -87,28 +92,28 @@ class PunishmentsService: revoked_by=p.revoked_by, revoked_reason=p.revoked_reason, evidence_url=p.evidence_url, - notes=p.notes + notes=p.notes, ) for p in punishments ] - + pages = (total + query.size - 1) // query.size - + return PunishmentListResponse( punishments=punishment_list, total=total, page=query.page, size=query.size, - pages=pages + pages=pages, ) - + async def get_active_ban_status(self, player_uuid: UUID) -> ActiveBanStatusResponse: """Get active ban status for player.""" ban = await self.repo.get_active_ban(player_uuid) - + if not ban: return ActiveBanStatusResponse(is_banned=False) - + punishment = PunishmentBase( id=ban.id, player_uuid=ban.player_uuid, @@ -125,18 +130,20 @@ class PunishmentsService: revoked_by=ban.revoked_by, revoked_reason=ban.revoked_reason, evidence_url=ban.evidence_url, - notes=ban.notes + notes=ban.notes, ) - + return ActiveBanStatusResponse(is_banned=True, punishment=punishment) - - async def get_active_mute_status(self, player_uuid: UUID) -> ActiveMuteStatusResponse: + + async def get_active_mute_status( + self, player_uuid: UUID + ) -> ActiveMuteStatusResponse: """Get active mute status for player.""" mute = await self.repo.get_active_mute(player_uuid) - + if not mute: return ActiveMuteStatusResponse(is_muted=False) - + punishment = PunishmentBase( id=mute.id, player_uuid=mute.player_uuid, @@ -153,7 +160,7 @@ class PunishmentsService: revoked_by=mute.revoked_by, revoked_reason=mute.revoked_reason, evidence_url=mute.evidence_url, - notes=mute.notes + notes=mute.notes, ) - + return ActiveMuteStatusResponse(is_muted=True, punishment=punishment) diff --git a/src/hubgw/services/teleport_history_service.py b/src/hubgw/services/teleport_history_service.py index 559ee01..03c2ac2 100644 --- a/src/hubgw/services/teleport_history_service.py +++ b/src/hubgw/services/teleport_history_service.py @@ -1,18 +1,20 @@ """Teleport History service.""" -from sqlalchemy.ext.asyncio import AsyncSession from typing import List +from sqlalchemy.ext.asyncio import AsyncSession + from hubgw.repositories.teleport_history_repo import TeleportHistoryRepository -from hubgw.schemas.teleport_history import TeleportHistoryCreate, TeleportHistory +from hubgw.schemas.teleport_history import (TeleportHistory, + TeleportHistoryCreate) class TeleportHistoryService: """Teleport History service for business logic.""" - + def __init__(self, session: AsyncSession): self.repo = TeleportHistoryRepository(session) - + async def create_teleport(self, request: TeleportHistoryCreate) -> TeleportHistory: """Create teleport history entry.""" entry = await self.repo.create(request) @@ -29,10 +31,12 @@ class TeleportHistoryService: to_z=entry.to_z, tp_type=entry.tp_type, target_name=entry.target_name, - created_at=entry.created_at + created_at=entry.created_at, ) - - async def list_player_teleports(self, player_uuid: str, limit: int = 100) -> List[TeleportHistory]: + + async def list_player_teleports( + self, player_uuid: str, limit: int = 100 + ) -> List[TeleportHistory]: """List teleport history for player.""" entries = await self.repo.list_by_player(player_uuid, limit) return [ @@ -49,7 +53,7 @@ class TeleportHistoryService: to_z=entry.to_z, tp_type=entry.tp_type, target_name=entry.target_name, - created_at=entry.created_at + created_at=entry.created_at, ) for entry in entries - ] \ No newline at end of file + ] diff --git a/src/hubgw/services/users_service.py b/src/hubgw/services/users_service.py index b5af149..4f43af1 100644 --- a/src/hubgw/services/users_service.py +++ b/src/hubgw/services/users_service.py @@ -2,17 +2,17 @@ from sqlalchemy.ext.asyncio import AsyncSession +from hubgw.core.errors import NotFoundError from hubgw.repositories.users_repo import UserRepository from hubgw.schemas.users import GetUserGameIdResponse -from hubgw.core.errors import NotFoundError class UserService: """User service for business logic.""" - + def __init__(self, session: AsyncSession): self.repo = UserRepository(session) - + async def get_game_id_by_name(self, name: str) -> GetUserGameIdResponse: """Get game_id by user name.""" game_id = await self.repo.get_game_id_by_name(name) diff --git a/src/hubgw/services/warps_service.py b/src/hubgw/services/warps_service.py index 7cae397..bb17f12 100644 --- a/src/hubgw/services/warps_service.py +++ b/src/hubgw/services/warps_service.py @@ -1,51 +1,51 @@ """Warps service.""" -from sqlalchemy.ext.asyncio import AsyncSession -from typing import List +from sqlalchemy.ext.asyncio import AsyncSession + +from hubgw.core.errors import AlreadyExistsError, NotFoundError from hubgw.repositories.warps_repo import WarpsRepository -from hubgw.schemas.warps import ( - WarpCreateRequest, WarpUpdateRequest, WarpDeleteRequest, WarpGetRequest, - Warp, WarpGetResponse, WarpQuery, WarpListResponse -) -from hubgw.core.errors import NotFoundError, AlreadyExistsError +from hubgw.schemas.warps import (Warp, WarpCreateRequest, WarpDeleteRequest, + WarpGetRequest, WarpGetResponse, + WarpListResponse, WarpQuery, + WarpUpdateRequest) class WarpsService: """Warps service for business logic.""" - + def __init__(self, session: AsyncSession): self.repo = WarpsRepository(session) - + async def create_warp(self, request: WarpCreateRequest) -> Warp: """Create warp with business logic.""" # Check if warp with same name already exists existing = await self.repo.get_by_request(WarpGetRequest(name=request.name)) if existing: raise AlreadyExistsError(f"Warp '{request.name}' already exists") - + return await self.repo.create(request) - + async def update_warp(self, request: WarpUpdateRequest) -> Warp: """Update warp with business logic.""" warp = await self.repo.update(request) if not warp: raise NotFoundError(f"Warp '{request.name}' not found") - + return warp - + async def delete_warp(self, request: WarpDeleteRequest) -> None: """Delete warp with business logic.""" success = await self.repo.delete(request) if not success: raise NotFoundError(f"Warp '{request.name}' not found") - + async def get_warp(self, request: WarpGetRequest) -> WarpGetResponse: """Get warp with business logic.""" warp = await self.repo.get_by_request(request) if not warp: raise NotFoundError(f"Warp '{request.name}' not found") - + return WarpGetResponse( id=warp.id, name=warp.name, @@ -58,13 +58,13 @@ class WarpsService: is_public=warp.is_public, description=warp.description, created_at=warp.created_at, - updated_at=warp.updated_at + updated_at=warp.updated_at, ) - + async def list_warps(self, query: WarpQuery) -> WarpListResponse: """List warps with business logic.""" warps, total = await self.repo.list(query) - + warp_list = [ Warp( id=warp.id, @@ -78,17 +78,13 @@ class WarpsService: is_public=warp.is_public, description=warp.description, created_at=warp.created_at, - updated_at=warp.updated_at + updated_at=warp.updated_at, ) for warp in warps ] - + pages = (total + query.size - 1) // query.size - + return WarpListResponse( - warps=warp_list, - total=total, - page=query.page, - size=query.size, - pages=pages + warps=warp_list, total=total, page=query.page, size=query.size, pages=pages ) diff --git a/src/hubgw/services/whitelist_service.py b/src/hubgw/services/whitelist_service.py index 0e05596..987957a 100644 --- a/src/hubgw/services/whitelist_service.py +++ b/src/hubgw/services/whitelist_service.py @@ -1,44 +1,49 @@ """Whitelist service.""" -from sqlalchemy.ext.asyncio import AsyncSession -from typing import List -from hubgw.repositories.whitelist_repo import WhitelistRepository -from hubgw.schemas.whitelist import ( - WhitelistAddRequest, WhitelistRemoveRequest, WhitelistCheckRequest, - WhitelistEntry as SchemaWhitelistEntry, WhitelistCheckResponse, WhitelistListResponse, WhitelistQuery -) -from hubgw.schemas.luckperms import LuckPermsPlayerCreateRequest +from sqlalchemy.ext.asyncio import AsyncSession + from hubgw.core.errors import AlreadyExistsError, NotFoundError +from hubgw.repositories.whitelist_repo import WhitelistRepository +from hubgw.schemas.luckperms import LuckPermsPlayerCreateRequest +from hubgw.schemas.whitelist import (WhitelistAddRequest, + WhitelistCheckRequest, + WhitelistCheckResponse) +from hubgw.schemas.whitelist import WhitelistEntry as SchemaWhitelistEntry +from hubgw.schemas.whitelist import (WhitelistListResponse, WhitelistQuery, + WhitelistRemoveRequest) from hubgw.services.luckperms_service import LuckPermsService from hubgw.services.users_service import UserService class WhitelistService: """Whitelist service for business logic.""" - + def __init__(self, session: AsyncSession): self.repo = WhitelistRepository(session) - + async def add_player( - self, + self, request: WhitelistAddRequest, luckperms_service: LuckPermsService, - user_service: UserService + user_service: UserService, ) -> SchemaWhitelistEntry: try: luckperms_create_request = LuckPermsPlayerCreateRequest( - username=request.player_name, - primary_group="default" + username=request.player_name, primary_group="default" + ) + await luckperms_service.create_player( + luckperms_create_request, user_service ) - await luckperms_service.create_player(luckperms_create_request, user_service) except AlreadyExistsError: pass - + existing = await self.repo.get_by_player_name(request.player_name) if existing: if existing.is_active: - raise AlreadyExistsError(f"Player '{request.player_name}' is already whitelisted") + raise AlreadyExistsError( + f"Player '{request.player_name}' is already whitelisted" + ) else: existing.player_uuid = request.player_uuid existing.added_by = request.added_by @@ -51,37 +56,35 @@ class WhitelistService: created_entry = await self.repo.create(request) return SchemaWhitelistEntry.model_validate(created_entry) - + async def remove_player(self, request: WhitelistRemoveRequest) -> None: success = await self.repo.delete_by_player_name(request.player_name) if not success: - raise NotFoundError(f"Player '{request.player_name}' not found in whitelist") - - async def check_player(self, request: WhitelistCheckRequest) -> WhitelistCheckResponse: + raise NotFoundError( + f"Player '{request.player_name}' not found in whitelist" + ) + + async def check_player( + self, request: WhitelistCheckRequest + ) -> WhitelistCheckResponse: entry = await self.repo.check(request) - + return WhitelistCheckResponse( is_whitelisted=entry is not None, - player_uuid=entry.player_uuid if entry else None + player_uuid=entry.player_uuid if entry else None, ) - + async def list_players(self) -> WhitelistListResponse: entries = await self.repo.list_all() total = len(entries) - entry_list = [ - SchemaWhitelistEntry.model_validate(entry) - for entry in entries - ] - + entry_list = [SchemaWhitelistEntry.model_validate(entry) for entry in entries] + return WhitelistListResponse(entries=entry_list, total=total) async def query_players(self, query: WhitelistQuery) -> WhitelistListResponse: entries, total = await self.repo.query(query) - entry_list = [ - SchemaWhitelistEntry.model_validate(entry) - for entry in entries - ] + entry_list = [SchemaWhitelistEntry.model_validate(entry) for entry in entries] return WhitelistListResponse(entries=entry_list, total=total) diff --git a/src/hubgw/utils/pagination.py b/src/hubgw/utils/pagination.py index ff88588..9a3a4b9 100644 --- a/src/hubgw/utils/pagination.py +++ b/src/hubgw/utils/pagination.py @@ -1,21 +1,21 @@ """Pagination utility functions.""" -from typing import List, TypeVar, Generic from math import ceil +from typing import Generic, List, TypeVar -T = TypeVar('T') +T = TypeVar("T") class PaginatedResult(Generic[T]): """Paginated result container.""" - + def __init__(self, items: List[T], total: int, page: int, size: int): self.items = items self.total = total self.page = page self.size = size self.pages = ceil(total / size) if size > 0 else 0 - + def to_dict(self) -> dict: """Convert to dictionary.""" return { @@ -23,7 +23,7 @@ class PaginatedResult(Generic[T]): "total": self.total, "page": self.page, "size": self.size, - "pages": self.pages + "pages": self.pages, }