Spaces:
Sleeping
Sleeping
File size: 3,777 Bytes
35d761c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | import uuid
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from passlib.context import CryptContext
from pydantic import BaseModel
from app.services.coa_initializer import initialize_startup_coa
from app.api.auth import get_current_user_claims
from app.db.database import get_db
from app.db.models import User, Account
from app.models.schemas import UserProfileResponse, UserProfileUpsert, RegisterRequest, LoginRequest
router = APIRouter()
# Password Hashing Engine
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
@router.post("/register")
def register_user(req: RegisterRequest, db: Session = Depends(get_db)):
existing_user = db.query(User).filter(User.email == req.email.lower()).first()
if existing_user:
raise HTTPException(status_code=400, detail="Email already registered")
# 2. Hash the password and create the user
user_id = str(uuid.uuid4())
hashed_password = pwd_context.hash(req.password)
new_user = User(
user_id=user_id,
email=req.email.lower(),
hashed_password=hashed_password,
organization_name=req.organization_name,
country=req.country,
business_type=req.business_type,
monthly_transactions=req.monthly_transactions
)
db.add(new_user)
db.flush() # Secure the ID
# 3. Build the Double-Entry Accounting Buckets Instantly!
initialize_startup_coa(db, user_id=user_id)
db.commit()
return {"status": "success", "user_id": user_id, "email": new_user.email}
@router.post("/login")
def login_user(req: LoginRequest, db: Session = Depends(get_db)):
"""Verifies a user's password and returns their profile to NextAuth."""
user = db.query(User).filter(User.email == req.email.lower()).first()
if not user or not user.hashed_password:
raise HTTPException(status_code=401, detail="Invalid email or password")
if not pwd_context.verify(req.password, user.hashed_password):
raise HTTPException(status_code=401, detail="Invalid email or password")
return {
"id": user.user_id,
"email": user.email,
"organization_name": user.organization_name
}
@router.put("/me", response_model=UserProfileResponse)
def upsert_current_user(
profile: UserProfileUpsert,
db: Session = Depends(get_db),
claims: dict = Depends(get_current_user_claims),
):
user_id = claims.get("sub")
email = claims.get("email")
if not user_id or not email:
raise HTTPException(status_code=400, detail="Token is missing required profile data.")
user = db.query(User).filter(User.user_id == user_id).first()
if user:
user.organization_name = profile.organization_name
user.email = email
else:
user = User(
user_id=user_id,
organization_name=profile.organization_name,
email=email,
)
db.add(user)
db.flush()
initialize_startup_coa(db, user_id=user_id)
db.commit()
db.refresh(user)
return user
@router.get("/accounts")
def list_user_accounts(
db: Session = Depends(get_db),
claims: dict = Depends(get_current_user_claims)
):
user_id = claims.get("sub")
if not user_id:
raise HTTPException(status_code=401, detail="Unauthorized")
accounts = db.query(Account).filter(
Account.user_id == user_id,
Account.account_type.in_(["ASSET", "LIABILITY"])
).all()
return {
"status": "success",
"data": [
{
"id": acc.id,
"name": acc.name,
"type": acc.account_type,
"description": acc.description
}
for acc in accounts
]
}
|