309 lines
9.4 KiB
Python
309 lines
9.4 KiB
Python
from typing import Any, List
|
|
from datetime import datetime, timedelta
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File, Form
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app import crud, models, schemas
|
|
from app.api import deps
|
|
from app.models.transaction import TransactionType
|
|
from app.models.wallet import WalletType
|
|
from app.services.file_upload import save_bot_image
|
|
from app.core.email import send_bot_purchase_confirmation
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/", response_model=List[schemas.Bot])
|
|
def read_bots(
|
|
*,
|
|
db: Session = Depends(deps.get_db),
|
|
current_user: models.User = Depends(deps.get_current_active_verified_user),
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
) -> Any:
|
|
"""
|
|
Retrieve available bots.
|
|
"""
|
|
bots = crud.bot.get_active(db, skip=skip, limit=limit)
|
|
return bots
|
|
|
|
|
|
@router.get("/{bot_id}", response_model=schemas.Bot)
|
|
def read_bot(
|
|
*,
|
|
db: Session = Depends(deps.get_db),
|
|
current_user: models.User = Depends(deps.get_current_active_verified_user),
|
|
bot_id: int,
|
|
) -> Any:
|
|
"""
|
|
Get a specific bot by ID.
|
|
"""
|
|
bot = crud.bot.get(db, id=bot_id)
|
|
if not bot:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Bot not found",
|
|
)
|
|
|
|
if not bot.is_active and current_user.role != "admin":
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Bot not found or not active",
|
|
)
|
|
|
|
return bot
|
|
|
|
|
|
@router.post("/{bot_id}/purchase", response_model=schemas.BotPurchase)
|
|
def purchase_bot(
|
|
*,
|
|
db: Session = Depends(deps.get_db),
|
|
current_user: models.User = Depends(deps.get_current_active_verified_user),
|
|
bot_id: int,
|
|
purchase_data: schemas.BotPurchaseRequest,
|
|
) -> Any:
|
|
"""
|
|
Purchase a bot.
|
|
"""
|
|
bot = crud.bot.get(db, id=bot_id)
|
|
if not bot:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Bot not found",
|
|
)
|
|
|
|
if not bot.is_active:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Bot is not active",
|
|
)
|
|
|
|
# Validate purchase amount
|
|
if purchase_data.amount < bot.min_purchase_amount:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Minimum purchase amount is {bot.min_purchase_amount} USDT",
|
|
)
|
|
|
|
if purchase_data.amount > bot.max_purchase_amount:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Maximum purchase amount is {bot.max_purchase_amount} USDT",
|
|
)
|
|
|
|
# Get user's trading wallet
|
|
trading_wallet = crud.wallet.get_by_user_and_type(
|
|
db, user_id=current_user.id, wallet_type=WalletType.TRADING
|
|
)
|
|
if not trading_wallet:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Trading wallet not found",
|
|
)
|
|
|
|
# Check if user has enough balance
|
|
if trading_wallet.balance < purchase_data.amount:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Insufficient funds in trading wallet",
|
|
)
|
|
|
|
# Calculate expected ROI
|
|
expected_roi_amount = purchase_data.amount * (bot.roi_percentage / 100)
|
|
|
|
# Calculate end time
|
|
start_time = datetime.utcnow()
|
|
end_time = start_time + timedelta(hours=bot.duration_hours)
|
|
|
|
# Create bot purchase
|
|
bot_purchase_in = schemas.BotPurchaseCreate(
|
|
user_id=current_user.id,
|
|
bot_id=bot.id,
|
|
amount=purchase_data.amount,
|
|
expected_roi_amount=expected_roi_amount,
|
|
start_time=start_time,
|
|
end_time=end_time,
|
|
status=schemas.BotPurchaseStatus.RUNNING,
|
|
)
|
|
bot_purchase = crud.bot_purchase.create(db, obj_in=bot_purchase_in)
|
|
|
|
# Deduct amount from trading wallet
|
|
crud.wallet.update_balance(db, wallet_id=trading_wallet.id, amount=purchase_data.amount, add=False)
|
|
|
|
# Create transaction record
|
|
crud.transaction.create(
|
|
db,
|
|
obj_in=schemas.TransactionCreate(
|
|
user_id=current_user.id,
|
|
wallet_id=trading_wallet.id,
|
|
amount=-purchase_data.amount,
|
|
transaction_type=TransactionType.BOT_PURCHASE,
|
|
description=f"Bot purchase - {bot.name}",
|
|
bot_purchase_id=bot_purchase.id,
|
|
),
|
|
)
|
|
|
|
# Send confirmation email
|
|
send_bot_purchase_confirmation(
|
|
email_to=current_user.email,
|
|
bot_name=bot.name,
|
|
amount=purchase_data.amount,
|
|
expected_roi=expected_roi_amount,
|
|
end_date=end_time.strftime("%Y-%m-%d %H:%M:%S UTC"),
|
|
)
|
|
|
|
return bot_purchase
|
|
|
|
|
|
@router.get("/purchased", response_model=List[schemas.BotPurchase])
|
|
def read_purchased_bots(
|
|
*,
|
|
db: Session = Depends(deps.get_db),
|
|
current_user: models.User = Depends(deps.get_current_active_verified_user),
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
) -> Any:
|
|
"""
|
|
Retrieve user's purchased bots.
|
|
"""
|
|
bot_purchases = crud.bot_purchase.get_by_user(db, user_id=current_user.id, skip=skip, limit=limit)
|
|
return bot_purchases
|
|
|
|
|
|
# Admin endpoints
|
|
@router.post("/admin", response_model=schemas.Bot)
|
|
async def create_bot(
|
|
*,
|
|
db: Session = Depends(deps.get_db),
|
|
current_user: models.User = Depends(deps.get_current_admin),
|
|
name: str = Form(...),
|
|
description: str = Form(None),
|
|
roi_percentage: float = Form(...),
|
|
duration_hours: int = Form(...),
|
|
min_purchase_amount: float = Form(...),
|
|
max_purchase_amount: float = Form(...),
|
|
is_active: bool = Form(True),
|
|
image: UploadFile = File(None),
|
|
) -> Any:
|
|
"""
|
|
Create a new bot (admin only).
|
|
"""
|
|
# Check if bot with same name already exists
|
|
existing_bot = crud.bot.get_by_name(db, name=name)
|
|
if existing_bot:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Bot with this name already exists",
|
|
)
|
|
|
|
# Create bot
|
|
bot_in = schemas.BotCreate(
|
|
name=name,
|
|
description=description,
|
|
roi_percentage=roi_percentage,
|
|
duration_hours=duration_hours,
|
|
min_purchase_amount=min_purchase_amount,
|
|
max_purchase_amount=max_purchase_amount,
|
|
is_active=is_active,
|
|
)
|
|
bot = crud.bot.create(db, obj_in=bot_in)
|
|
|
|
# Save image if provided
|
|
if image:
|
|
image_path = save_bot_image(bot.id, image)
|
|
crud.bot.update(db, db_obj=bot, obj_in={"image_path": image_path})
|
|
bot.image_path = image_path
|
|
|
|
return bot
|
|
|
|
|
|
@router.put("/admin/{bot_id}", response_model=schemas.Bot)
|
|
async def update_bot(
|
|
*,
|
|
db: Session = Depends(deps.get_db),
|
|
current_user: models.User = Depends(deps.get_current_admin),
|
|
bot_id: int,
|
|
name: str = Form(None),
|
|
description: str = Form(None),
|
|
roi_percentage: float = Form(None),
|
|
duration_hours: int = Form(None),
|
|
min_purchase_amount: float = Form(None),
|
|
max_purchase_amount: float = Form(None),
|
|
is_active: bool = Form(None),
|
|
image: UploadFile = File(None),
|
|
) -> Any:
|
|
"""
|
|
Update a bot (admin only).
|
|
"""
|
|
bot = crud.bot.get(db, id=bot_id)
|
|
if not bot:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Bot not found",
|
|
)
|
|
|
|
# Check if name is being changed and if it conflicts with existing bot
|
|
if name and name != bot.name:
|
|
existing_bot = crud.bot.get_by_name(db, name=name)
|
|
if existing_bot and existing_bot.id != bot_id:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Bot with this name already exists",
|
|
)
|
|
|
|
# Update bot data
|
|
update_data = {}
|
|
if name is not None:
|
|
update_data["name"] = name
|
|
if description is not None:
|
|
update_data["description"] = description
|
|
if roi_percentage is not None:
|
|
update_data["roi_percentage"] = roi_percentage
|
|
if duration_hours is not None:
|
|
update_data["duration_hours"] = duration_hours
|
|
if min_purchase_amount is not None:
|
|
update_data["min_purchase_amount"] = min_purchase_amount
|
|
if max_purchase_amount is not None:
|
|
update_data["max_purchase_amount"] = max_purchase_amount
|
|
if is_active is not None:
|
|
update_data["is_active"] = is_active
|
|
|
|
# Save image if provided
|
|
if image:
|
|
image_path = save_bot_image(bot.id, image)
|
|
update_data["image_path"] = image_path
|
|
|
|
bot = crud.bot.update(db, db_obj=bot, obj_in=update_data)
|
|
return bot
|
|
|
|
|
|
@router.delete("/admin/{bot_id}", response_model=schemas.Bot)
|
|
def delete_bot(
|
|
*,
|
|
db: Session = Depends(deps.get_db),
|
|
current_user: models.User = Depends(deps.get_current_admin),
|
|
bot_id: int,
|
|
) -> Any:
|
|
"""
|
|
Delete a bot (admin only).
|
|
"""
|
|
bot = crud.bot.get(db, id=bot_id)
|
|
if not bot:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Bot not found",
|
|
)
|
|
|
|
# Check if bot has any active purchases
|
|
active_purchases = crud.bot_purchase.get_by_bot(db, bot_id=bot_id)
|
|
active_purchases = [p for p in active_purchases if p.status == "running"]
|
|
if active_purchases:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Cannot delete bot with active purchases",
|
|
)
|
|
|
|
bot = crud.bot.remove(db, id=bot_id)
|
|
return bot |