first implementation

This commit is contained in:
counterweight 2025-12-20 22:18:14 +01:00
parent 1eb4641ed9
commit a56a4c076a
Signed by: counterweight
GPG key ID: 883EDBAA726BD96C
14 changed files with 898 additions and 729 deletions

185
backend/schemas.py Normal file
View file

@ -0,0 +1,185 @@
"""Pydantic schemas for API request/response models."""
from datetime import datetime
from typing import Generic, TypeVar
from pydantic import BaseModel, EmailStr
# =============================================================================
# Auth Schemas
# =============================================================================
class UserCredentials(BaseModel):
"""Base model for user email/password."""
email: EmailStr
password: str
UserCreate = UserCredentials
UserLogin = UserCredentials
class UserResponse(BaseModel):
"""Response model for authenticated user info."""
id: int
email: str
roles: list[str]
permissions: list[str]
class TokenResponse(BaseModel):
"""Response model for token-based auth (unused but kept for API completeness)."""
access_token: str
token_type: str
user: UserResponse
class RegisterWithInvite(BaseModel):
"""Request model for registration with invite."""
email: EmailStr
password: str
invite_identifier: str
# =============================================================================
# Counter Schemas
# =============================================================================
class CounterValue(BaseModel):
"""Response model for counter value."""
value: int
# =============================================================================
# Sum Schemas
# =============================================================================
class SumRequest(BaseModel):
"""Request model for sum calculation."""
a: float
b: float
class SumResponse(BaseModel):
"""Response model for sum calculation."""
a: float
b: float
result: float
# =============================================================================
# Audit Schemas
# =============================================================================
class CounterRecordResponse(BaseModel):
"""Response model for a counter audit record."""
id: int
user_email: str
value_before: int
value_after: int
created_at: datetime
class SumRecordResponse(BaseModel):
"""Response model for a sum audit record."""
id: int
user_email: str
a: float
b: float
result: float
created_at: datetime
# =============================================================================
# Pagination (Generic)
# =============================================================================
RecordT = TypeVar("RecordT", bound=BaseModel)
class PaginatedResponse(BaseModel, Generic[RecordT]):
"""Generic paginated response wrapper."""
records: list[RecordT]
total: int
page: int
per_page: int
total_pages: int
PaginatedCounterRecords = PaginatedResponse[CounterRecordResponse]
PaginatedSumRecords = PaginatedResponse[SumRecordResponse]
# =============================================================================
# Profile Schemas
# =============================================================================
class ProfileResponse(BaseModel):
"""Response model for profile data."""
contact_email: str | None
telegram: str | None
signal: str | None
nostr_npub: str | None
godfather_email: str | None = None
class ProfileUpdate(BaseModel):
"""Request model for updating profile."""
contact_email: str | None = None
telegram: str | None = None
signal: str | None = None
nostr_npub: str | None = None
# =============================================================================
# Invite Schemas
# =============================================================================
class InviteCheckResponse(BaseModel):
"""Response for invite check endpoint."""
valid: bool
status: str | None = None
error: str | None = None
class InviteCreate(BaseModel):
"""Request model for creating an invite."""
godfather_id: int
class InviteResponse(BaseModel):
"""Response model for invite data (admin view)."""
id: int
identifier: str
godfather_id: int
godfather_email: str
status: str
used_by_id: int | None
used_by_email: str | None
created_at: datetime
spent_at: datetime | None
revoked_at: datetime | None
class UserInviteResponse(BaseModel):
"""Response model for a user's invite (simpler than admin view)."""
id: int
identifier: str
status: str
used_by_email: str | None
created_at: datetime
spent_at: datetime | None
PaginatedInviteRecords = PaginatedResponse[InviteResponse]
# =============================================================================
# Admin Schemas
# =============================================================================
class AdminUserResponse(BaseModel):
"""Minimal user info for admin dropdowns."""
id: int
email: str