adding base project

This commit is contained in:
Azmat-7860
2026-10-25 23:11:19 +05:30
commit 5993c15b70
23 changed files with 1297 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Table
from sqlalchemy.orm import relationship
from datetime import datetime
from app.core.database import Base
# Association table for User <-> Role (many to many)
user_roles = Table(
"app_user_roles",
Base.metadata,
Column("user_id", Integer, ForeignKey("app_users.user_id"), primary_key=True),
Column("role_id", Integer, ForeignKey("roles.id"), primary_key=True)
)
class Role(Base):
__tablename__ = "roles"
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
name = Column(String(100), unique=True, index=True, nullable=False)
description = Column(String(255), nullable=True)
class AppUser(Base):
__tablename__ = "app_users"
user_id = Column(Integer, primary_key=True, index=True, autoincrement=True)
email = Column(String(255), unique=True, index=True, nullable=False)
username = Column(String(100), unique=True, index=True, nullable=False)
password = Column(String(255), nullable=True) # Can be null before initial verification
full_name = Column(String(255), nullable=True)
first_name = Column(String(100), nullable=True)
last_name = Column(String(100), nullable=True)
status = Column(String(50), default="ACTIVE")
user_grp_id = Column(Integer, default=1)
is_active = Column(Boolean, default=True)
random_no = Column(String(50), nullable=True) # OTP storage matching Spring Boot
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
roles = relationship("Role", secondary=user_roles, lazy="joined")
+291
View File
@@ -0,0 +1,291 @@
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"
)
+56
View File
@@ -0,0 +1,56 @@
from pydantic import BaseModel, EmailStr
from typing import Optional, List, Any
class LoginRequest(BaseModel):
email: str
password: str
class SessionItem(BaseModel):
userId: int
email: str
fullname: Optional[str] = ""
firstName: Optional[str] = ""
username: Optional[str] = ""
roles: List[str] = []
token: str
class SessionResponse(BaseModel):
operationStatus: str # "SUCCESS" or "ERROR"
operationMessage: str # "Login Success" or "Login Failed"
item: Optional[SessionItem] = None
class RegistrationRequest(BaseModel):
email: str
password: Optional[str] = None
new_password: Optional[str] = None
confirm_password: Optional[str] = None
fullName: Optional[str] = None
first_name: Optional[str] = None
last_name: Optional[str] = None
username: Optional[str] = None
account_id: Optional[Any] = None
mob_no: Optional[Any] = None
gender: Optional[str] = None
date_of_birth: Optional[str] = None
how_you_know_aboutus: Optional[str] = None
accesstype: Optional[str] = None
class SendEmailRequest(BaseModel):
email: str
password: Optional[str] = None
fullName: Optional[str] = None
username: Optional[str] = None
account_id: Optional[Any] = None
recaptcha: Optional[Any] = None
class EmailExistsRequest(BaseModel):
email: str
class UserResponse(BaseModel):
userId: int
email: str
username: str
fullName: Optional[str] = None
firstName: Optional[str] = None
roles: List[str] = []
status: Optional[str] = "ACTIVE"