"""YakPanel - FTP API""" from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, func from pydantic import BaseModel from app.core.database import get_db from app.core.security import get_password_hash from app.api.auth import get_current_user from app.models.user import User from app.models.ftp import Ftp from app.services.ftp_service import create_ftp_user, delete_ftp_user, update_ftp_password router = APIRouter(prefix="/ftp", tags=["ftp"]) class CreateFtpRequest(BaseModel): name: str password: str path: str pid: int = 0 ps: str = "" class UpdateFtpPasswordRequest(BaseModel): password: str @router.get("/list") async def ftp_list( current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """List FTP accounts""" result = await db.execute(select(Ftp).order_by(Ftp.id)) rows = result.scalars().all() return [{"id": r.id, "name": r.name, "path": r.path, "ps": r.ps} for r in rows] @router.post("/create") async def ftp_create( body: CreateFtpRequest, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """Create FTP account (panel + Pure-FTPd when available)""" result = await db.execute(select(Ftp).where(Ftp.name == body.name)) if result.scalar_one_or_none(): raise HTTPException(status_code=400, detail="FTP account already exists") ok, msg = create_ftp_user(body.name, body.password, body.path) if not ok: raise HTTPException(status_code=400, detail=f"FTP: {msg}") ftp = Ftp( name=body.name, password=get_password_hash(body.password), path=body.path, pid=body.pid, ps=body.ps, ) db.add(ftp) await db.commit() return {"status": True, "msg": "FTP account created", "id": ftp.id} @router.put("/{ftp_id}/password") async def ftp_update_password( ftp_id: int, body: UpdateFtpPasswordRequest, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """Update FTP account password""" result = await db.execute(select(Ftp).where(Ftp.id == ftp_id)) ftp = result.scalar_one_or_none() if not ftp: raise HTTPException(status_code=404, detail="FTP account not found") if not body.password or len(body.password) < 6: raise HTTPException(status_code=400, detail="Password must be at least 6 characters") ok, msg = update_ftp_password(ftp.name, body.password) if not ok: raise HTTPException(status_code=400, detail=f"FTP: {msg}") ftp.password = get_password_hash(body.password) await db.commit() return {"status": True, "msg": "Password updated"} @router.delete("/{ftp_id}") async def ftp_delete( ftp_id: int, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """Delete FTP account (panel + Pure-FTPd when available)""" result = await db.execute(select(Ftp).where(Ftp.id == ftp_id)) ftp = result.scalar_one_or_none() if not ftp: raise HTTPException(status_code=404, detail="FTP account not found") ok, msg = delete_ftp_user(ftp.name) if not ok: raise HTTPException(status_code=400, detail=f"FTP: {msg}") await db.delete(ftp) await db.commit() return {"status": True, "msg": "FTP account deleted"} @router.get("/count") async def ftp_count( current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """Get FTP count""" result = await db.execute(select(func.count()).select_from(Ftp)) return {"count": result.scalar() or 0}