Files
ai-rpg/app/api/presets.py

126 lines
4.0 KiB
Python
Raw Permalink Normal View History

2026-06-20 19:13:05 +03:00
"""Presets API — CRUD for world presets."""
from __future__ import annotations
import uuid
from fastapi import APIRouter, Depends, HTTPException, Response, status
from sqlalchemy import or_, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.deps import get_current_user
from app.db import get_db
from app.models import User, WorldPreset
from app.schemas import PresetCreateRequest, PresetFull, PresetSummary
router = APIRouter(prefix="/api/presets", tags=["presets"])
@router.get("", response_model=dict)
async def list_presets(
db: AsyncSession = Depends(get_db),
user: User = Depends(get_current_user),
) -> dict:
"""List public presets + current user's presets."""
rows = (
await db.execute(
select(WorldPreset).where(
or_(
WorldPreset.is_public.is_(True),
WorldPreset.owner_id == user.id,
)
).order_by(WorldPreset.created_at.desc())
)
).scalars().all()
return {"items": [PresetSummary.model_validate(r).model_dump() for r in rows]}
@router.post("", response_model=PresetFull, status_code=status.HTTP_201_CREATED)
async def create_preset(
body: PresetCreateRequest,
db: AsyncSession = Depends(get_db),
user: User = Depends(get_current_user),
) -> WorldPreset:
if not user.is_admin:
raise HTTPException(403, "admin_only")
preset = WorldPreset(
owner_id=user.id,
name=body.name,
description=body.description,
language=body.language,
rules=body.rules,
time_schema=body.time_schema,
schemas=body.schemas,
environment_schema=body.environment_schema,
environment_initial=body.environment_initial,
is_public=body.is_public,
status="ready",
)
db.add(preset)
await db.commit()
await db.refresh(preset)
return preset
@router.get("/{preset_id}", response_model=PresetFull)
async def get_preset(
preset_id: uuid.UUID,
db: AsyncSession = Depends(get_db),
user: User = Depends(get_current_user),
) -> WorldPreset:
preset = (
await db.execute(select(WorldPreset).where(WorldPreset.id == preset_id))
).scalar_one_or_none()
if preset is None:
raise HTTPException(404, "not_found")
if not preset.is_public and preset.owner_id != user.id and not user.is_admin:
raise HTTPException(403, "not_accessible")
return preset
@router.patch("/{preset_id}", response_model=PresetFull)
async def update_preset(
preset_id: uuid.UUID,
body: PresetCreateRequest,
db: AsyncSession = Depends(get_db),
user: User = Depends(get_current_user),
) -> WorldPreset:
preset = (
await db.execute(select(WorldPreset).where(WorldPreset.id == preset_id))
).scalar_one_or_none()
if preset is None:
raise HTTPException(404, "not_found")
if preset.owner_id != user.id and not user.is_admin:
raise HTTPException(403, "not_owner")
preset.name = body.name
preset.description = body.description
preset.language = body.language
preset.rules = body.rules
preset.time_schema = body.time_schema
preset.schemas = body.schemas
preset.environment_schema = body.environment_schema
preset.environment_initial = body.environment_initial
preset.is_public = body.is_public
preset.version += 1
await db.commit()
await db.refresh(preset)
return preset
@router.delete("/{preset_id}", status_code=status.HTTP_204_NO_CONTENT, response_class=Response)
async def delete_preset(
preset_id: uuid.UUID,
db: AsyncSession = Depends(get_db),
user: User = Depends(get_current_user),
) -> Response:
preset = (
await db.execute(select(WorldPreset).where(WorldPreset.id == preset_id))
).scalar_one_or_none()
if preset is None:
raise HTTPException(404, "not_found")
if preset.owner_id != user.id and not user.is_admin:
raise HTTPException(403, "not_owner")
preset.status = "archived"
await db.commit()
return Response(status_code=status.HTTP_204_NO_CONTENT)