Files
popa_minecraft_launcher_api/app/api/promo.py
2025-12-20 19:56:13 +05:00

119 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from fastapi import APIRouter, HTTPException, Query, Form
from typing import List
from app.services.auth import AuthService
from app.services.promo import PromoService
from app.models.promo import PromoCreate, PromoUpdate, PromoRedeemResponse
router = APIRouter(tags=["Promo"])
promo_service = PromoService()
async def validate_admin(accessToken: str, clientToken: str):
auth = AuthService()
if not await auth.is_admin(accessToken, clientToken):
raise HTTPException(status_code=403, detail="Admin privileges required")
# --- Игровая ручка (активация) ---
@router.post("/promo/redeem", response_model=PromoRedeemResponse)
async def redeem_promo(
username: str = Form(...),
code: str = Form(...),
):
# при желании сюда можно добавить проверку accessToken/clientToken,
# как у вас в админских ручках, но это зависит от вашей auth-логики.
return await promo_service.redeem(username=username, code=code)
# --- Админские ручки ---
@router.get("/admin/promo", response_model=List[dict])
async def admin_list_promos(
accessToken: str,
clientToken: str,
limit: int = Query(50, ge=1, le=200),
skip: int = Query(0, ge=0),
):
await validate_admin(accessToken, clientToken)
return await promo_service.list(limit=limit, skip=skip)
@router.post("/admin/promo", response_model=dict)
async def admin_create_promo(
accessToken: str = Form(...),
clientToken: str = Form(...),
code: str = Form(...),
reward_coins: int = Form(...),
max_uses: str | None = Form(
default=None,
description="Сколько всего раз можно активировать промокод. Пусто = бесконечно. 1 = одноразовый."
),
is_active: bool = Form(True),
):
await validate_admin(accessToken, clientToken)
# normalize max_uses
if max_uses is None or max_uses.strip() == "":
max_uses_int = None
else:
try:
max_uses_int = int(max_uses)
except ValueError:
raise HTTPException(status_code=400, detail="max_uses must be an integer")
if max_uses_int <= 0:
# 0 или отрицательные считаем как "бесконечно"
max_uses_int = None
payload = PromoCreate(
code=code,
reward_coins=reward_coins,
max_uses=max_uses_int,
is_active=is_active,
)
return await promo_service.create(payload)
@router.put("/admin/promo/{promo_id}", response_model=dict)
async def admin_update_promo(
promo_id: str,
accessToken: str = Form(...),
clientToken: str = Form(...),
reward_coins: int | None = Form(None),
max_uses: str | None = Form(
default=None,
description="Пусто = не менять. '0' = бесконечно. '1' = одноразовый."
),
is_active: bool | None = Form(None),
):
await validate_admin(accessToken, clientToken)
max_uses_int = None
max_uses_provided = max_uses is not None # отличаем “не прислали” от “прислали пусто/0”
if max_uses_provided:
if max_uses.strip() == "":
# пусто -> бесконечно
max_uses_int = None
else:
try:
max_uses_int = int(max_uses)
except ValueError:
raise HTTPException(status_code=400, detail="max_uses must be an integer")
if max_uses_int <= 0:
max_uses_int = None
data = {"reward_coins": reward_coins, "is_active": is_active}
if max_uses_provided:
data["max_uses"] = max_uses_int
payload = PromoUpdate(**{k: v for k, v in data.items() if v is not None or k == "max_uses" and max_uses_provided})
return await promo_service.update(promo_id, payload)
@router.delete("/admin/promo/{promo_id}")
async def admin_delete_promo(
promo_id: str,
accessToken: str,
clientToken: str,
):
await validate_admin(accessToken, clientToken)
return await promo_service.delete(promo_id)