Refactor API
This commit is contained in:
parent
9c813cdfbe
commit
8aa4828239
21 changed files with 731 additions and 363 deletions
136
API/routers/user.py
Normal file
136
API/routers/user.py
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
from datetime import datetime
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy import select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from schemas.user import UserCreate, UserResponse, UserLogin, UserTokenRevoke
|
||||
from models.server import Server
|
||||
from models.user import User
|
||||
from models.config import Config
|
||||
from models.token import AuthToken
|
||||
from utils.database import get_db
|
||||
from utils.auth import get_password_hash, create_token, verify_password, get_current_user, hash_token
|
||||
from utils.xui import XUIClient
|
||||
|
||||
router = APIRouter(prefix="/user")
|
||||
|
||||
@router.post("/signup", status_code=201)
|
||||
async def signup(body: UserCreate, db: AsyncSession = Depends(get_db)):
|
||||
promo_code = (body.promo_code or "").strip()
|
||||
result = await db.execute(
|
||||
select(Server).where(
|
||||
Server.deleted_at.is_(None),
|
||||
Server.code == promo_code
|
||||
)
|
||||
)
|
||||
server = result.scalar_one_or_none()
|
||||
if not server:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Promo-code is not valid"
|
||||
)
|
||||
existing = await db.execute(
|
||||
select(User).where(User.email == body.email)
|
||||
)
|
||||
if existing.scalar_one_or_none():
|
||||
raise HTTPException(
|
||||
status_code=409,
|
||||
detail="Email is busy"
|
||||
)
|
||||
new_user = User(
|
||||
email=body.email,
|
||||
pass_hash= get_password_hash(body.password),
|
||||
server_id=server.id
|
||||
)
|
||||
db.add(new_user)
|
||||
await db.commit()
|
||||
await db.refresh(new_user)
|
||||
access_token = await create_token(user_id=new_user.id, db=db)
|
||||
return UserResponse(
|
||||
id=new_user.id,
|
||||
email=new_user.email,
|
||||
created_at=new_user.created_at,
|
||||
access_token=access_token
|
||||
)
|
||||
|
||||
@router.post("/login", status_code=200)
|
||||
async def login(body: UserLogin, db: AsyncSession = Depends(get_db)):
|
||||
result = await db.execute(
|
||||
select(User).where(User.email == body.email)
|
||||
)
|
||||
user = result.scalar_one_or_none()
|
||||
if (not user or user.deleted_at is not None) or (not verify_password(body.password, user.pass_hash)):
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="Incorrect email or password"
|
||||
)
|
||||
access_token = await create_token(user_id=user.id, db=db)
|
||||
return UserResponse(
|
||||
id=user.id,
|
||||
email=user.email,
|
||||
created_at=user.created_at,
|
||||
access_token=access_token
|
||||
)
|
||||
|
||||
@router.post("/logout", status_code=200)
|
||||
async def revoke_token(body: UserTokenRevoke, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
|
||||
token_to_revoke = body.token_to_revoke.strip()
|
||||
token_hash = hash_token(token_to_revoke)
|
||||
result = await db.execute(
|
||||
select(AuthToken).where(
|
||||
AuthToken.token_hash == token_hash,
|
||||
AuthToken.user_id == current_user.id
|
||||
)
|
||||
)
|
||||
token_record = result.scalar_one_or_none()
|
||||
if not token_record:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Token not found"
|
||||
)
|
||||
if token_record.revoked:
|
||||
return {"message": "Token already revoked"}
|
||||
token_record.revoked = True
|
||||
token_record.revoked_at = datetime.utcnow()
|
||||
await db.commit()
|
||||
return {"message": "Token successfully revoked"}
|
||||
|
||||
@router.delete("/delete", status_code=200)
|
||||
async def delete_user(current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
|
||||
if current_user.deleted_at is not None:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="User is already deleted"
|
||||
)
|
||||
try:
|
||||
await db.refresh(current_user, ["server"])
|
||||
server = current_user.server
|
||||
if server and server.inbound_id:
|
||||
xui = await XUIClient.from_server(server)
|
||||
result = await db.execute(
|
||||
select(Config).where(
|
||||
Config.user_id == current_user.id,
|
||||
Config.deleted_at.is_(None)
|
||||
)
|
||||
)
|
||||
user_configs = result.scalars().all()
|
||||
for cfg in user_configs:
|
||||
client_email_xui = f"{current_user.email}-{cfg.name}"
|
||||
try:
|
||||
await xui.delete_client(server.inbound_id, client_email_xui)
|
||||
except:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
current_user.deleted_at = datetime.utcnow()
|
||||
await db.execute(
|
||||
update(AuthToken)
|
||||
.where(AuthToken.user_id == current_user.id)
|
||||
.values(revoked=True, revoked_at=datetime.utcnow())
|
||||
)
|
||||
await db.execute(
|
||||
update(Config)
|
||||
.where(Config.user_id == current_user.id, Config.deleted_at.is_(None))
|
||||
.values(deleted_at=datetime.utcnow())
|
||||
)
|
||||
await db.commit()
|
||||
return {"message": "User has been successfully deleted"}
|
||||
Loading…
Add table
Add a link
Reference in a new issue