File size: 5,467 Bytes
9fe25e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
from typing import Optional
from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from backend.app.core.security import get_password_hash, verify_password, create_access_token
from backend.app.repositories.users import UserRepository
from backend.app.schemas.auth import UserRegister, UserLogin
from backend.app.models.users import User

class AuthService:
    def __init__(self, db: Session):
        self.db = db
        self.user_repo = UserRepository(db)

    def signup(self, data: UserRegister) -> User:
        """Register a new customer or worker."""
        # Check if phone already exists
        existing_user = self.user_repo.get_by_phone(data.phone)
        if existing_user:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="A user with this phone number is already registered."
            )

        # Hash password if provided
        hashed_password = None
        if data.password:
            hashed_password = get_password_hash(data.password)

        if data.role == "worker":
            # For workers, ensure rate and skill are provided
            if not data.skill or data.rate is None:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail="Workers must provide a skill and a daily rate."
                )
            
            # Create worker and worker profile
            user = self.user_repo.create_worker(
                name=data.name,
                phone=data.phone,
                skill=data.skill,
                rate=data.rate,
                city=data.city
            )
            # Set hashed password if provided
            if hashed_password:
                user.password_hash = hashed_password
                self.db.commit()
        elif data.role in ("customer", "mediator", "admin"):
            # Create customer/mediator/admin
            user = self.user_repo.create_customer(
                name=data.name,
                phone=data.phone,
                city=data.city
            )
            # Override default role if not customer
            user.role = data.role
            if hashed_password:
                user.password_hash = hashed_password
            self.db.commit()
        else:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Invalid registration role: {data.role}"
            )
            
        return user

    def login(self, data: UserLogin) -> dict:
        """Authenticate user and return a JWT access token."""
        user = self.user_repo.get_by_phone(data.phone)
        
        # Bypass for demo OTP 123456
        if data.otp == "123456":
            if not user:
                # Dynamically create the user for demo convenience if they don't exist
                if data.role == "worker":
                    user = self.user_repo.create_worker(
                        name="Ramesh Kumar",
                        phone=data.phone,
                        skill="Mason",
                        rate=650
                    )
                else:
                    user = self.user_repo.create_customer(
                        name="Harsh",
                        phone=data.phone
                    )
                    user.role = data.role
                    self.db.commit()
            else:
                # Update role if user log in with a different role in the demo selector
                user.role = data.role
                self.db.commit()
        else:
            # If not using demo OTP, check password/credentials
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid credentials. Use demo OTP 123456."
            )

        if not user.is_active:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="User account is suspended. Contact admin."
            )

        # Create access token
        token = create_access_token(subject=user.phone)
        return {
            "access_token": token,
            "token_type": "bearer",
            "role": user.role
        }

    def get_current_user_by_token(self, token: str) -> User:
        """Decode JWT token and get the user."""
        from jose import jwt, JWTError
        from backend.app.core.config import settings
        
        try:
            payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
            phone: str = payload.get("sub")
            if phone is None:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Invalid credentials token signature."
                )
        except JWTError:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid token. Please log in again."
            )
            
        user = self.user_repo.get_by_phone(phone)
        if not user:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="User not found."
            )
        if not user.is_active:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Account is suspended."
            )
        return user