- FastAPI backend with JWT auth, roles (admin/moderator/user) - PDF upload (up to 500MB) with streaming, PyMuPDF text extraction - ChromaDB vectorization per page with metadata - LiteLLM AI question extraction from PDF (not generation) - Image extraction from PDF pages, graceful fallback - Quiz modes: timed (countdown timer) + learning (answers shown inline) - Page-by-page question navigation with dot navigator - TTS endpoint using LiteLLM (Google Vertex / OpenAI voices) - Admin dashboard: AI model management per task, user role management - Moderator role: upload PDFs, create sections, generate quizzes - Spaced repetition reminders via SMTP email (SM-2 intervals) - APScheduler daily reminder jobs - Celery + Redis for background PDF processing - React frontend with all pages - Docker Compose deployment (nginx + backend + celery + redis) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
49 lines
1.6 KiB
Python
49 lines
1.6 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.database import get_db
|
|
from app.models.user import User
|
|
from app.schemas.auth import UserCreate, UserResponse, Token, LoginRequest
|
|
from app.utils.auth import get_password_hash, verify_password, create_access_token, get_current_user
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.post("/register", response_model=Token)
|
|
def register(user_data: UserCreate, db: Session = Depends(get_db)):
|
|
existing = db.query(User).filter(User.email == user_data.email).first()
|
|
if existing:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Email already registered",
|
|
)
|
|
|
|
user = User(
|
|
email=user_data.email,
|
|
hashed_password=get_password_hash(user_data.password),
|
|
name=user_data.name,
|
|
)
|
|
db.add(user)
|
|
db.commit()
|
|
db.refresh(user)
|
|
|
|
access_token = create_access_token(data={"sub": user.email})
|
|
return Token(access_token=access_token)
|
|
|
|
|
|
@router.post("/login", response_model=Token)
|
|
def login(login_data: LoginRequest, db: Session = Depends(get_db)):
|
|
user = db.query(User).filter(User.email == login_data.email).first()
|
|
if not user or not verify_password(login_data.password, user.hashed_password):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Invalid email or password",
|
|
)
|
|
|
|
access_token = create_access_token(data={"sub": user.email})
|
|
return Token(access_token=access_token)
|
|
|
|
|
|
@router.get("/me", response_model=UserResponse)
|
|
def get_me(current_user: User = Depends(get_current_user)):
|
|
return current_user
|