Files
yakpanel-core/YakPanel-server/backend/app/api/firewall.py
2026-04-07 02:04:22 +05:30

84 lines
2.9 KiB
Python

"""YakPanel - Firewall API"""
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from pydantic import BaseModel
from app.core.database import get_db
from app.core.utils import exec_shell_sync
from app.api.auth import get_current_user
from app.models.user import User
from app.models.firewall import FirewallRule
router = APIRouter(prefix="/firewall", tags=["firewall"])
class CreateFirewallRuleRequest(BaseModel):
port: str
protocol: str = "tcp"
action: str = "accept"
ps: str = ""
@router.get("/list")
async def firewall_list(
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""List firewall rules"""
result = await db.execute(select(FirewallRule).order_by(FirewallRule.id))
rows = result.scalars().all()
return [{"id": r.id, "port": r.port, "protocol": r.protocol, "action": r.action, "ps": r.ps} for r in rows]
@router.post("/create")
async def firewall_create(
body: CreateFirewallRuleRequest,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""Add firewall rule (stored in panel; use Apply to UFW to sync)"""
if not body.port or len(body.port) > 32:
raise HTTPException(status_code=400, detail="Invalid port")
rule = FirewallRule(port=body.port, protocol=body.protocol, action=body.action, ps=body.ps)
db.add(rule)
await db.commit()
return {"status": True, "msg": "Rule added", "id": rule.id}
@router.delete("/{rule_id}")
async def firewall_delete(
rule_id: int,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""Delete firewall rule"""
result = await db.execute(select(FirewallRule).where(FirewallRule.id == rule_id))
rule = result.scalar_one_or_none()
if not rule:
raise HTTPException(status_code=404, detail="Rule not found")
await db.delete(rule)
await db.commit()
return {"status": True, "msg": "Rule deleted"}
@router.post("/apply")
async def firewall_apply(
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""Apply firewall rules to ufw (runs ufw allow/deny for each rule)"""
result = await db.execute(select(FirewallRule).order_by(FirewallRule.id))
rules = result.scalars().all()
errors = []
for r in rules:
port_proto = f"{r.port}/{r.protocol}"
action = "allow" if r.action == "accept" else "deny"
cmd = f"ufw {action} {port_proto}"
out, err = exec_shell_sync(cmd, timeout=10)
if err and "error" in err.lower() and "already" not in err.lower():
errors.append(f"{port_proto}: {err.strip()}")
if errors:
raise HTTPException(status_code=500, detail="; ".join(errors))
return {"status": True, "msg": "Rules applied", "count": len(rules)}