Files
PoleDanceApp/backend/app/routers/users.py
Dianaka123 1c5719ac85 Initial commit: Pole Dance Championships App
Full-stack mobile app for pole dance championship management.

Backend: FastAPI + SQLAlchemy 2 (async) + SQLite (dev) / PostgreSQL (prod)
- JWT auth with refresh token rotation
- Championship CRUD with Instagram Graph API sync (APScheduler)
- Registration flow with status management
- Participant list publish with Expo push notifications
- Alembic migrations, pytest test suite

Mobile: React Native + Expo (TypeScript)
- Auth gate: pending approval screen for new members
- Championships list & detail screens
- Registration form with status tracking
- React Query + Zustand + React Navigation v6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 22:47:10 +03:00

102 lines
3.2 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.crud import crud_user
from app.database import get_db
from app.dependencies import get_admin, get_current_user
from app.models.user import User
from app.schemas.user import PushTokenUpdate, UserCreate, UserOut
from app.services import notification_service
router = APIRouter(prefix="/users", tags=["users"])
@router.get("", response_model=list[UserOut])
async def list_users(
status: str | None = None,
role: str | None = None,
skip: int = 0,
limit: int = 50,
_admin: User = Depends(get_admin),
db: AsyncSession = Depends(get_db),
):
return await crud_user.list_all(db, status=status, role=role, skip=skip, limit=limit)
@router.get("/{user_id}", response_model=UserOut)
async def get_user(
user_id: str,
_admin: User = Depends(get_admin),
db: AsyncSession = Depends(get_db),
):
user = await crud_user.get(db, user_id)
if not user:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
return user
@router.post("", response_model=UserOut, status_code=status.HTTP_201_CREATED)
async def create_user(
body: UserCreate,
_admin: User = Depends(get_admin),
db: AsyncSession = Depends(get_db),
):
if await crud_user.get_by_email(db, body.email):
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Email already registered")
return await crud_user.create(
db,
email=body.email,
password=body.password,
full_name=body.full_name,
phone=body.phone,
role=body.role,
status="approved",
)
@router.patch("/{user_id}/approve", response_model=UserOut)
async def approve_user(
user_id: str,
admin: User = Depends(get_admin),
db: AsyncSession = Depends(get_db),
):
user = await crud_user.get(db, user_id)
if not user:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
user = await crud_user.set_status(db, user, "approved")
await notification_service.send_push_notification(
db=db,
user=user,
title="Welcome!",
body="Your account has been approved. You can now access the app.",
notif_type="account_approved",
)
return user
@router.patch("/{user_id}/reject", response_model=UserOut)
async def reject_user(
user_id: str,
_admin: User = Depends(get_admin),
db: AsyncSession = Depends(get_db),
):
user = await crud_user.get(db, user_id)
if not user:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
return await crud_user.set_status(db, user, "rejected")
@router.patch("/{user_id}/push-token", status_code=status.HTTP_204_NO_CONTENT)
async def update_push_token(
user_id: str,
body: PushTokenUpdate,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
if str(current_user.id) != user_id and current_user.role != "admin":
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
user = await crud_user.get(db, user_id)
if not user:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
await crud_user.set_push_token(db, user, body.expo_push_token)