feat: initialize aivideo project
This commit is contained in:
24
backend/app/modules/auth/repository.py
Normal file
24
backend/app/modules/auth/repository.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from sqlalchemy import or_, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models.entities import User
|
||||
|
||||
|
||||
class AuthRepository:
|
||||
def __init__(self, db: Session) -> None:
|
||||
self.db = db
|
||||
|
||||
def get_user_by_account(self, account: str) -> User | None:
|
||||
return self.db.scalar(
|
||||
select(User).where(
|
||||
or_(
|
||||
User.email == account,
|
||||
User.mobile == account,
|
||||
User.username == account,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def get_user_by_public_id(self, public_id: str) -> User | None:
|
||||
return self.db.scalar(select(User).where(User.public_id == public_id))
|
||||
|
||||
55
backend/app/modules/auth/router.py
Normal file
55
backend/app/modules/auth/router.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from fastapi import APIRouter, Cookie, Depends, Request, Response
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.common.db.session import get_db
|
||||
from app.common.responses.api_response import success_response
|
||||
from app.common.security.deps import get_current_user
|
||||
from app.models.entities import User
|
||||
from app.modules.auth.schema import LoginRequest, RegisterRequest
|
||||
from app.modules.auth.service import AuthService
|
||||
|
||||
|
||||
router = APIRouter(prefix="/api/v1/auth", tags=["auth"])
|
||||
|
||||
|
||||
@router.post("/register")
|
||||
def register(
|
||||
payload: RegisterRequest,
|
||||
request: Request,
|
||||
response: Response,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
data = AuthService(db).register(payload, request, response)
|
||||
return {"code": 0, "message": "ok", "data": data}
|
||||
|
||||
|
||||
@router.post("/login")
|
||||
def login(
|
||||
payload: LoginRequest,
|
||||
request: Request,
|
||||
response: Response,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
data = AuthService(db).login(payload, request, response)
|
||||
return {"code": 0, "message": "ok", "data": data}
|
||||
|
||||
|
||||
@router.post("/refresh")
|
||||
def refresh(
|
||||
response: Response,
|
||||
db: Session = Depends(get_db),
|
||||
user_refresh_token: str | None = Cookie(default=None),
|
||||
):
|
||||
data = AuthService(db).refresh(user_refresh_token, response)
|
||||
return {"code": 0, "message": "ok", "data": data}
|
||||
|
||||
|
||||
@router.post("/logout")
|
||||
def logout(response: Response, db: Session = Depends(get_db)):
|
||||
AuthService(db).logout(response)
|
||||
return {"code": 0, "message": "ok", "data": {"success": True}}
|
||||
|
||||
|
||||
@router.get("/me")
|
||||
def me(current_user: User = Depends(get_current_user)):
|
||||
return success_response(AuthService.serialize_user(current_user))
|
||||
13
backend/app/modules/auth/schema.py
Normal file
13
backend/app/modules/auth/schema.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from pydantic import BaseModel, EmailStr, Field
|
||||
|
||||
|
||||
class RegisterRequest(BaseModel):
|
||||
account: EmailStr
|
||||
password: str = Field(min_length=8, max_length=64)
|
||||
invite_code: str | None = None
|
||||
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
account: str
|
||||
password: str = Field(min_length=8, max_length=64)
|
||||
|
||||
123
backend/app/modules/auth/service.py
Normal file
123
backend/app/modules/auth/service.py
Normal file
@@ -0,0 +1,123 @@
|
||||
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
|
||||
Reference in New Issue
Block a user