1+ """
2+ Authentication Routes for the Physical AI Book Platform
3+
4+ This module defines the API endpoints for user authentication and profile management,
5+ including registration, login, logout, and profile operations.
6+ """
7+
8+ from fastapi import APIRouter , Depends , HTTPException , status , Request
9+ from fastapi .security import HTTPBearer , HTTPAuthorizationCredentials
10+ from sqlalchemy .orm import Session
11+ from typing import Optional
12+ from database import get_db
13+ from .models import User
14+ from .services import auth_service
15+ from pydantic import BaseModel
16+ import json
17+
18+
19+ router = APIRouter (prefix = "/auth" , tags = ["authentication" ])
20+
21+ # Define request/response models
22+ class RegisterRequest (BaseModel ):
23+ email : str
24+ password : str
25+ software_experience : str
26+ hardware_experience : str
27+ background_preference : str
28+
29+ class LoginRequest (BaseModel ):
30+ email : str
31+ password : str
32+
33+ class UpdateProfileRequest (BaseModel ):
34+ software_experience : Optional [str ] = None
35+ hardware_experience : Optional [str ] = None
36+ background_preference : Optional [str ] = None
37+
38+
39+ # HTTP Bearer token for authentication
40+ security = HTTPBearer ()
41+
42+
43+ def get_current_user_id (credentials : HTTPAuthorizationCredentials = Depends (security ), db : Session = Depends (get_db )):
44+ """
45+ Get the current user ID from the authorization token.
46+ """
47+ token = credentials .credentials
48+ user_id = auth_service .verify_session_token (token )
49+
50+ if not user_id :
51+ raise HTTPException (
52+ status_code = status .HTTP_401_UNAUTHORIZED ,
53+ detail = "Invalid or expired token" ,
54+ headers = {"WWW-Authenticate" : "Bearer" },
55+ )
56+
57+ # Verify user still exists in database
58+ user = db .query (User ).filter (User .id == user_id , User .is_active == True ).first ()
59+ if not user :
60+ raise HTTPException (
61+ status_code = status .HTTP_401_UNAUTHORIZED ,
62+ detail = "User no longer exists" ,
63+ headers = {"WWW-Authenticate" : "Bearer" },
64+ )
65+
66+ return user_id
67+
68+
69+ @router .post ("/register" , status_code = status .HTTP_201_CREATED )
70+ async def register (request : RegisterRequest , db : Session = Depends (get_db )):
71+ """
72+ Register a new user with profile information.
73+
74+ Creates a new user account and associated profile with background information.
75+ """
76+ result = auth_service .register_user (
77+ db = db ,
78+ email = request .email ,
79+ password = request .password ,
80+ software_experience = request .software_experience ,
81+ hardware_experience = request .hardware_experience ,
82+ background_preference = request .background_preference
83+ )
84+
85+ if not result ["success" ]:
86+ raise HTTPException (
87+ status_code = status .HTTP_400_BAD_REQUEST ,
88+ detail = result ["error" ]
89+ )
90+
91+ # Create session token for auto-login
92+ token = auth_service .create_session_token (result ["user" ]["id" ])
93+
94+ return {
95+ "success" : True ,
96+ "message" : "User registered successfully" ,
97+ "user" : result ["user" ],
98+ "profile" : result ["profile" ],
99+ "token" : token
100+ }
101+
102+
103+ @router .post ("/login" )
104+ async def login (request : LoginRequest , db : Session = Depends (get_db )):
105+ """
106+ Authenticate user and create session.
107+
108+ Authenticates user credentials and returns a session token.
109+ """
110+ result = auth_service .authenticate_user (
111+ db = db ,
112+ email = request .email ,
113+ password = request .password
114+ )
115+
116+ if not result ["success" ]:
117+ raise HTTPException (
118+ status_code = status .HTTP_401_UNAUTHORIZED ,
119+ detail = result ["error" ]
120+ )
121+
122+ # Create session token
123+ token = auth_service .create_session_token (result ["user" ]["id" ])
124+
125+ return {
126+ "success" : True ,
127+ "message" : "Login successful" ,
128+ "user" : result ["user" ],
129+ "profile" : result ["profile" ],
130+ "token" : token
131+ }
132+
133+
134+ @router .post ("/logout" )
135+ async def logout (credentials : HTTPAuthorizationCredentials = Depends (security )):
136+ """
137+ Logout user and destroy session.
138+
139+ In a stateless JWT system, this is typically handled on the client side.
140+ This endpoint can be used to notify the server of logout.
141+ """
142+ # In a real system, you might add the token to a blacklist
143+ return {
144+ "success" : True ,
145+ "message" : "Logout successful"
146+ }
147+
148+
149+ @router .get ("/profile" )
150+ async def get_profile (current_user_id : str = Depends (get_current_user_id ), db : Session = Depends (get_db )):
151+ """
152+ Get current user's profile.
153+
154+ Retrieves the profile information for the authenticated user.
155+ """
156+ result = auth_service .get_user_profile (db = db , user_id = current_user_id )
157+
158+ if not result ["success" ]:
159+ raise HTTPException (
160+ status_code = status .HTTP_400_BAD_REQUEST ,
161+ detail = result ["error" ]
162+ )
163+
164+ return {
165+ "success" : True ,
166+ "profile" : result ["profile" ]
167+ }
168+
169+
170+ @router .put ("/profile" )
171+ async def update_profile (
172+ request : UpdateProfileRequest ,
173+ current_user_id : str = Depends (get_current_user_id ),
174+ db : Session = Depends (get_db )
175+ ):
176+ """
177+ Update current user's profile.
178+
179+ Updates the profile information for the authenticated user.
180+ """
181+ result = auth_service .update_user_profile (
182+ db = db ,
183+ user_id = current_user_id ,
184+ software_experience = request .software_experience ,
185+ hardware_experience = request .hardware_experience ,
186+ background_preference = request .background_preference
187+ )
188+
189+ if not result ["success" ]:
190+ raise HTTPException (
191+ status_code = status .HTTP_400_BAD_REQUEST ,
192+ detail = result ["error" ]
193+ )
194+
195+ return {
196+ "success" : True ,
197+ "profile" : result ["profile" ]
198+ }
0 commit comments