Compare commits
3 Commits
905f4da2d9
...
c2510fa9dc
| Author | SHA1 | Date |
|---|---|---|
|
|
c2510fa9dc | |
|
|
6a14e2d65c | |
|
|
527146e651 |
|
|
@ -6,8 +6,9 @@ from fastapi import APIRouter, Depends
|
|||
|
||||
from hubgw.api.deps import get_homes_service, verify_api_key
|
||||
from hubgw.core.errors import AppError, create_http_exception
|
||||
from hubgw.schemas.homes import (Home, HomeGetRequest, HomeGetResponse,
|
||||
HomeListResponse, HomeUpsertRequest)
|
||||
from hubgw.schemas.homes import (Home, HomeCountResponse, HomeGetRequest,
|
||||
HomeGetResponse, HomeListResponse,
|
||||
HomeUpsertRequest)
|
||||
from hubgw.services.homes_service import HomesService
|
||||
|
||||
router = APIRouter()
|
||||
|
|
@ -39,6 +40,33 @@ async def get_home(
|
|||
raise create_http_exception(e)
|
||||
|
||||
|
||||
@router.post("/delete")
|
||||
async def delete_home(
|
||||
request: HomeGetRequest,
|
||||
service: Annotated[HomesService, Depends(get_homes_service)],
|
||||
_: Annotated[str, Depends(verify_api_key)],
|
||||
):
|
||||
"""Delete home by player UUID and name."""
|
||||
try:
|
||||
await service.delete_home(request)
|
||||
return {"success": True}
|
||||
except AppError as e:
|
||||
raise create_http_exception(e)
|
||||
|
||||
|
||||
@router.get("/count/{player_uuid}", response_model=HomeCountResponse)
|
||||
async def count_homes(
|
||||
player_uuid: str,
|
||||
service: Annotated[HomesService, Depends(get_homes_service)],
|
||||
_: Annotated[str, Depends(verify_api_key)],
|
||||
):
|
||||
"""Count homes for player."""
|
||||
try:
|
||||
return await service.count_homes(player_uuid)
|
||||
except AppError as e:
|
||||
raise create_http_exception(e)
|
||||
|
||||
|
||||
@router.get("/{player_uuid}", response_model=HomeListResponse)
|
||||
async def list_homes(
|
||||
player_uuid: str,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
"""Teleport History model."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from sqlalchemy import Column, DateTime, Float, ForeignKey, String, Text
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.sql import func
|
||||
|
|
@ -13,7 +15,11 @@ _LUCKPERMS_SCHEMA = APP_CONFIG.database.luckperms_schema
|
|||
|
||||
|
||||
class TeleportHistory(Base):
|
||||
"""Teleport History model."""
|
||||
"""Teleport History model.
|
||||
|
||||
This is an append-only audit log table, so it doesn't have updated_at column.
|
||||
We override updated_at to None to exclude it from this model.
|
||||
"""
|
||||
|
||||
__tablename__ = "hub_teleport_history"
|
||||
__table_args__ = {"schema": _HUB_SCHEMA}
|
||||
|
|
@ -35,8 +41,11 @@ class TeleportHistory(Base):
|
|||
to_x = Column(Float, nullable=False) # DOUBLE PRECISION
|
||||
to_y = Column(Float, nullable=False) # DOUBLE PRECISION
|
||||
to_z = Column(Float, nullable=False) # DOUBLE PRECISION
|
||||
tp_type = Column(String(50), nullable=False)
|
||||
tp_type = Column(String(50), nullable=False, index=True)
|
||||
target_name = Column(String(255))
|
||||
created_at = Column(
|
||||
DateTime(timezone=True), server_default=func.now(), nullable=False
|
||||
DateTime(timezone=True), server_default=func.now(), nullable=False, index=True
|
||||
)
|
||||
|
||||
# Override updated_at from Base to exclude it
|
||||
updated_at = None
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ 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.cooldown import Cooldown
|
||||
|
|
@ -17,16 +18,30 @@ class CooldownsRepository:
|
|||
self.session = session
|
||||
|
||||
async def create(self, request: CooldownCreate) -> Cooldown:
|
||||
"""Create new cooldown."""
|
||||
cooldown = Cooldown(
|
||||
player_uuid=request.player_uuid,
|
||||
cooldown_type=request.cooldown_type,
|
||||
expires_at=request.expires_at,
|
||||
cooldown_seconds=request.cooldown_seconds,
|
||||
cooldown_metadata=request.metadata,
|
||||
"""Create or update cooldown (UPSERT)."""
|
||||
stmt = (
|
||||
insert(Cooldown)
|
||||
.values(
|
||||
player_uuid=request.player_uuid,
|
||||
cooldown_type=request.cooldown_type,
|
||||
expires_at=request.expires_at,
|
||||
cooldown_seconds=request.cooldown_seconds,
|
||||
cooldown_metadata=request.metadata,
|
||||
)
|
||||
.on_conflict_do_update(
|
||||
index_elements=["player_uuid", "cooldown_type"],
|
||||
set_={
|
||||
"expires_at": request.expires_at,
|
||||
"cooldown_seconds": request.cooldown_seconds,
|
||||
"metadata": request.metadata,
|
||||
"updated_at": func.now(),
|
||||
},
|
||||
)
|
||||
.returning(Cooldown)
|
||||
)
|
||||
self.session.add(cooldown)
|
||||
result = await self.session.execute(stmt)
|
||||
await self.session.commit()
|
||||
cooldown = result.scalar_one()
|
||||
await self.session.refresh(cooldown)
|
||||
return cooldown
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,13 @@ class HomesRepository:
|
|||
await self.session.commit()
|
||||
return result.rowcount > 0
|
||||
|
||||
async def delete_by_player_and_name(self, player_uuid: str, name: str) -> bool:
|
||||
"""Delete home by player UUID and name."""
|
||||
stmt = delete(Home).where(Home.player_uuid == player_uuid, Home.name == name)
|
||||
result = await self.session.execute(stmt)
|
||||
await self.session.commit()
|
||||
return result.rowcount > 0
|
||||
|
||||
async def query(self, query: HomeQuery) -> tuple[List[Home], int]:
|
||||
"""Query homes with filters and pagination."""
|
||||
stmt = select(Home)
|
||||
|
|
@ -119,3 +126,9 @@ class HomesRepository:
|
|||
stmt = select(Home).where(Home.player_uuid == player_uuid).order_by(Home.name)
|
||||
result = await self.session.execute(stmt)
|
||||
return list(result.scalars().all())
|
||||
|
||||
async def count_by_player(self, player_uuid: str) -> int:
|
||||
"""Count homes for a player."""
|
||||
stmt = select(func.count(Home.id)).where(Home.player_uuid == player_uuid)
|
||||
result = await self.session.execute(stmt)
|
||||
return result.scalar()
|
||||
|
|
|
|||
|
|
@ -73,6 +73,12 @@ class HomeListResponse(BaseModel):
|
|||
total: int
|
||||
|
||||
|
||||
class HomeCountResponse(BaseModel):
|
||||
"""Home count response schema."""
|
||||
|
||||
total: int
|
||||
|
||||
|
||||
class HomeQuery(PaginationParams):
|
||||
"""Home query schema."""
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from uuid import UUID
|
|||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from hubgw.schemas.common import BaseSchema, PaginationParams
|
||||
from hubgw.schemas.common import PaginationParams
|
||||
|
||||
|
||||
class TeleportHistoryBase(BaseModel):
|
||||
|
|
@ -30,8 +30,11 @@ class TeleportHistoryCreate(TeleportHistoryBase):
|
|||
player_uuid: str
|
||||
|
||||
|
||||
class TeleportHistory(TeleportHistoryBase, BaseSchema):
|
||||
"""Teleport history schema."""
|
||||
class TeleportHistory(TeleportHistoryBase):
|
||||
"""Teleport history response schema.
|
||||
|
||||
This is an append-only audit log, so it only has created_at (no updated_at).
|
||||
"""
|
||||
|
||||
id: UUID
|
||||
player_uuid: str
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ 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)
|
||||
from hubgw.schemas.homes import (Home, HomeCountResponse, HomeGetRequest,
|
||||
HomeGetResponse, HomeListResponse,
|
||||
HomeUpsertRequest)
|
||||
|
||||
|
||||
class HomesService:
|
||||
|
|
@ -65,3 +66,19 @@ class HomesService:
|
|||
]
|
||||
|
||||
return HomeListResponse(homes=home_list, total=len(home_list))
|
||||
|
||||
async def count_homes(self, player_uuid: str) -> HomeCountResponse:
|
||||
"""Count homes for player."""
|
||||
total = await self.repo.count_by_player(player_uuid)
|
||||
return HomeCountResponse(total=total)
|
||||
|
||||
async def delete_home(self, request: HomeGetRequest) -> bool:
|
||||
"""Delete home by player UUID and name."""
|
||||
deleted = await self.repo.delete_by_player_and_name(
|
||||
request.player_uuid, request.name
|
||||
)
|
||||
if not deleted:
|
||||
raise NotFoundError(
|
||||
f"Home '{request.name}' not found for player {request.player_uuid}"
|
||||
)
|
||||
return deleted
|
||||
|
|
|
|||
Loading…
Reference in New Issue