114 lines
3.6 KiB
Python
114 lines
3.6 KiB
Python
"""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}
|