Files
aivideo/backend/app/modules/auth/service.py

124 lines
4.6 KiB
Python

from __future__ import annotations
from datetime import datetime
from fastapi import Request, Response
from sqlalchemy.orm import Session
from app.common.errors.app_error import AuthenticationError, ConflictAppError
from app.common.security.jwt import (
clear_auth_cookies,
create_access_token,
create_refresh_token,
decode_refresh_token,
set_auth_cookies,
)
from app.common.security.password import hash_password, verify_password
from app.common.utils.id_gen import new_public_id
from app.models.entities import InviteCode, InviteRelation, User, Wallet
from app.modules.auth.repository import AuthRepository
from app.modules.wallets.service import WalletService
class AuthService:
def __init__(self, db: Session) -> None:
self.db = db
self.repository = AuthRepository(db)
self.wallet_service = WalletService(db)
def register(self, payload, request: Request, response: Response) -> dict:
if self.repository.get_user_by_account(payload.account):
raise ConflictAppError("account already exists", code=10010)
user = User(
public_id=new_public_id("usr"),
email=payload.account,
password_hash=hash_password(payload.password),
nickname=payload.account.split("@")[0],
status=1,
register_ip=request.client.host if request.client else "",
last_login_ip=request.client.host if request.client else "",
last_login_at=datetime.utcnow(),
)
self.db.add(user)
self.db.flush()
self.db.add(Wallet(user_id=user.id))
self.db.flush()
self._bind_invite_relation(user.id, payload.invite_code, request)
self.wallet_service.try_issue_signup_reward(user.id)
self.db.commit()
self.db.refresh(user)
self._issue_tokens(user.public_id, response)
return self.serialize_user(user)
def login(self, payload, request: Request, response: Response) -> dict:
user = self.repository.get_user_by_account(payload.account)
if not user or not verify_password(payload.password, user.password_hash):
raise AuthenticationError("invalid credentials")
user.last_login_at = datetime.utcnow()
user.last_login_ip = request.client.host if request.client else ""
self.db.commit()
self._issue_tokens(user.public_id, response)
return self.serialize_user(user)
def refresh(self, refresh_token: str | None, response: Response) -> dict:
if not refresh_token:
raise AuthenticationError()
try:
payload = decode_refresh_token(refresh_token)
except Exception as exc: # noqa: BLE001
raise AuthenticationError() from exc
if payload.get("scope") != "user":
raise AuthenticationError()
user = self.repository.get_user_by_public_id(payload["sub"])
if not user:
raise AuthenticationError()
self._issue_tokens(user.public_id, response)
return self.serialize_user(user)
def logout(self, response: Response) -> None:
clear_auth_cookies(response, prefix="user")
@staticmethod
def serialize_user(user: User) -> dict:
return {
"publicId": user.public_id,
"username": user.username or "",
"nickname": user.nickname,
"avatarUrl": user.avatar_url,
"email": user.email or "",
"mobile": user.mobile or "",
"status": user.status,
}
def _issue_tokens(self, public_id: str, response: Response) -> None:
access_token = create_access_token(public_id, scope="user")
refresh_token = create_refresh_token(public_id, scope="user")
set_auth_cookies(response, access_token, refresh_token, prefix="user")
def _bind_invite_relation(
self,
invitee_user_id: int,
invite_code_value: str | None,
request: Request,
) -> None:
if not invite_code_value:
return
invite_code = self.db.query(InviteCode).filter(
InviteCode.invite_code == invite_code_value,
InviteCode.status == 1,
).first()
if not invite_code:
return
self.db.add(
InviteRelation(
inviter_user_id=invite_code.user_id,
invitee_user_id=invitee_user_id,
invite_code_id=invite_code.id,
reward_status="pending",
reward_points=0,
register_ip=request.client.host if request.client else "",
)
)
invite_code.used_count += 1