Files
2026-10-25 23:11:19 +05:30

292 lines
9.6 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, status, Request, Query, Body
from sqlalchemy.orm import Session
from typing import List, Optional
from app.core.deps import get_db, require_current_user, get_current_user_token
from app.core.security import verify_password, get_password_hash, create_access_token
from app.auth.models import AppUser, Role
from app.auth.schemas import (
LoginRequest,
SessionResponse,
SessionItem,
RegistrationRequest,
SendEmailRequest,
EmailExistsRequest,
UserResponse
)
router = APIRouter(prefix="/token", tags=["Authentication & User Session"])
@router.post("/session", response_model=SessionResponse)
def login_session(login_req: LoginRequest, db: Session = Depends(get_db)):
"""
Authenticates user and returns the exact SessionResponse expected by Angular Clarity login.service.ts.
"""
user = db.query(AppUser).filter(AppUser.email == login_req.email).first()
if not user or not verify_password(login_req.password, user.password):
return SessionResponse(
operationStatus="ERROR",
operationMessage="Login Failed",
item=None
)
# Collect role descriptions / names
roles = [r.description or r.name for r in user.roles] if user.roles else ["Admin"]
# Generate JWT
token = create_access_token(
subject=user.user_id,
extra_claims={
"email": user.email,
"username": user.username,
"roles": roles,
"user_grp": user.user_grp_id
}
)
session_item = SessionItem(
userId=user.user_id,
email=user.email,
fullname=user.full_name or user.username,
firstName=user.first_name or user.full_name or user.username,
username=user.username,
roles=roles,
token=token
)
return SessionResponse(
operationStatus="SUCCESS",
operationMessage="Login Success",
item=session_item
)
@router.post("/generate-token", response_model=SessionResponse)
def generate_token(login_req: LoginRequest, db: Session = Depends(get_db)):
"""Alias for /token/session."""
return login_session(login_req, db)
@router.get("/logout")
@router.post("/logout")
def logout_user():
"""Logs out the user."""
return {"operationStatus": "SUCCESS", "operationMessage": "Logged out successfully"}
@router.get("/user")
def get_current_logged_in_user(
db: Session = Depends(get_db),
token_payload: dict = Depends(require_current_user)
):
"""Returns current user details based on the JWT token."""
user_id = token_payload.get("user_id") or token_payload.get("sub")
user = db.query(AppUser).filter(AppUser.user_id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
roles = [r.description or r.name for r in user.roles] if user.roles else ["Admin"]
return {
"userId": user.user_id,
"email": user.email,
"username": user.username,
"fullname": user.full_name,
"roles": roles,
"status": user.status
}
@router.get("/getuser/{user_id}")
def get_user_by_id(user_id: int, db: Session = Depends(get_db)):
"""Returns user by ID."""
user = db.query(AppUser).filter(AppUser.user_id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
roles = [r.description or r.name for r in user.roles] if user.roles else ["Admin"]
return {
"userId": user.user_id,
"email": user.email,
"username": user.username,
"fullname": user.full_name,
"roles": roles
}
@router.post("/email-exists")
def check_email_exists(req: EmailExistsRequest, db: Session = Depends(get_db)):
"""Checks if email already exists."""
user = db.query(AppUser).filter(AppUser.email == req.email).first()
return {"exists": user is not None}
@router.post("/users/sysaccount/savesysaccount")
@router.post("/savesysaccount")
def save_sys_account(data: dict = Body(...)):
"""
Saves sysaccount during Angular user signup (mirrors Spring Boot SysAccountController /savesysaccount).
"""
account_id = data.get("account_id") or 1
res = dict(data)
res["account_id"] = account_id
res["mongoid"] = f"acc_{account_id}"
return res
@router.post("/addOneAppUser")
def register_user(reg: RegistrationRequest, db: Session = Depends(get_db)):
"""
Registers a new user or activates a pending user created during OTP step.
Mirrors Spring Boot SessionController /addOneAppUser.
"""
email = reg.email.strip()
user = db.query(AppUser).filter(AppUser.email == email).first()
raw_pwd = reg.new_password or reg.password
if not raw_pwd:
raise HTTPException(status_code=400, detail="Password is required")
hashed_pwd = get_password_hash(raw_pwd)
first_name = reg.first_name or reg.fullName or email.split("@")[0]
last_name = reg.last_name or ""
full_name = reg.fullName or f"{first_name} {last_name}".strip()
username = reg.username or email.split("@")[0]
if user:
if user.status == "ACTIVE" and user.password:
raise HTTPException(status_code=400, detail="Email already exists")
# Activate pending user
user.password = hashed_pwd
user.first_name = first_name
user.last_name = last_name
user.full_name = full_name
user.username = username
user.status = "ACTIVE"
user.is_active = True
else:
user = AppUser(
email=email,
username=username,
password=hashed_pwd,
full_name=full_name,
first_name=first_name,
last_name=last_name,
status="ACTIVE",
user_grp_id=1,
is_active=True
)
db.add(user)
# Assign Admin role
admin_role = db.query(Role).filter(Role.name == "ROLE_ADMIN").first()
if admin_role and admin_role not in user.roles:
user.roles.append(admin_role)
db.commit()
db.refresh(user)
return {
"userId": user.user_id,
"email": user.email,
"username": user.username,
"fullName": user.full_name,
"firstName": user.first_name,
"roles": [r.description or r.name for r in user.roles],
"message": "User registered successfully"
}
@router.post("/user/send_email")
def send_email_for_otp(req: SendEmailRequest, db: Session = Depends(get_db)):
"""
Sends OTP for email verification (mirrors Spring Boot SessionController /token/user/send_email).
"""
import random
from app.core.email import email_service
email = req.email.strip()
user = db.query(AppUser).filter(AppUser.email == email).first()
if user and user.status == "ACTIVE" and user.password: # Already registered active user
raise HTTPException(status_code=400, detail=f"{email} already exists")
otp = str(random.randint(100000, 999999))
if not user:
username = req.username or email.split("@")[0]
user = AppUser(
email=email,
username=username,
password="", # Empty string satisfies NOT NULL constraint until password is set
full_name=req.fullName or username,
first_name=req.fullName or username,
status="PENDING",
random_no=otp,
is_active=False
)
db.add(user)
else:
user.random_no = otp
db.commit()
email_service.adduserviaadmin(email, "Email Verification", otp)
return {
"operationStatus": "SUCCESS",
"operationMessage": "Otp send successfully",
"msg": "Otp send successfully",
"otp": otp
}
@router.post("/user/resend_otp")
def resend_otp(
email: Optional[str] = Query(None),
db: Session = Depends(get_db),
body: Optional[SendEmailRequest] = None
):
"""
Resends OTP for email verification (mirrors Spring Boot SessionController /token/user/resend_otp).
Supports query parameter (?email=...) or JSON body.
"""
import random
from app.core.email import email_service
target_email = email or (body.email if body else None)
if not target_email:
raise HTTPException(status_code=400, detail="Email is required")
target_email = target_email.strip()
user = db.query(AppUser).filter(AppUser.email == target_email).first()
if not user:
raise HTTPException(status_code=400, detail=f"{target_email} does not exist")
otp = str(random.randint(100000, 999999))
user.random_no = otp
db.commit()
email_service.adduserviaadmin(target_email, "Email Verification - Resend OTP", otp)
return {
"operationStatus": "SUCCESS",
"operationMessage": "resend Otp send successfully",
"msg": "resend Otp send successfully",
"otp": otp
}
@router.post("/user/otp_verification")
def otp_verification(
email: str = Query(...),
otp: str = Query(...),
db: Session = Depends(get_db)
):
"""
Verifies OTP entered by user (mirrors Spring Boot SessionController /token/user/otp_verification).
Query parameters: ?email=...&otp=...
"""
email_clean = email.replace(" ", "+").strip()
user = db.query(AppUser).filter(AppUser.email == email_clean).first()
if not user:
raise HTTPException(status_code=400, detail="User not found")
if user.random_no and user.random_no.strip() == otp.strip():
return {
"operationStatus": "SUCCESS",
"operationMessage": "OTP Verified",
"msg": "OTP Verified"
}
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Wrong OTP"
)