From ae63817172d340561d5904b5bb4b0ada84be5f94 Mon Sep 17 00:00:00 2001 From: twothatit Date: Sat, 7 Feb 2026 21:41:43 +0100 Subject: [PATCH] bugfix --- app/routers/customers.py | 25 ++++++++++++++++++++----- app/routers/deployments.py | 36 ++++++++++++++++++++++-------------- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/app/routers/customers.py b/app/routers/customers.py index 2d6004f..8a8a53a 100644 --- a/app/routers/customers.py +++ b/app/routers/customers.py @@ -1,5 +1,6 @@ """Customer CRUD API endpoints with automatic deployment on create.""" +import asyncio import logging from datetime import datetime from typing import Optional @@ -7,7 +8,7 @@ from typing import Optional from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Query, status from sqlalchemy.orm import Session -from app.database import get_db +from app.database import SessionLocal, get_db from app.dependencies import get_current_user from app.models import Customer, Deployment, DeploymentLog, User from app.services import netbird_service @@ -59,11 +60,22 @@ async def create_customer( logger.info("Customer %d (%s) created by %s.", customer.id, customer.subdomain, current_user.username) - # Deploy in background - result = await netbird_service.deploy_customer(db, customer.id) + # Deploy in background so the HTTP response returns immediately. + # We create a dedicated DB session for the background task because + # the request session will be closed once the response is sent. + async def _deploy_in_background(customer_id: int) -> None: + bg_db = SessionLocal() + try: + await netbird_service.deploy_customer(bg_db, customer_id) + except Exception: + logger.exception("Background deployment failed for customer %d", customer_id) + finally: + bg_db.close() + + background_tasks.add_task(_deploy_in_background, customer.id) response = customer.to_dict() - response["deployment"] = result + response["deployment"] = {"deployment_status": "deploying"} return response @@ -221,7 +233,10 @@ async def delete_customer( ) # Undeploy first (containers, NPM, files) - await netbird_service.undeploy_customer(db, customer_id) + try: + await netbird_service.undeploy_customer(db, customer_id) + except Exception: + logger.exception("Undeploy error for customer %d (continuing with delete)", customer_id) # Delete customer record (cascades to deployment + logs) db.delete(customer) diff --git a/app/routers/deployments.py b/app/routers/deployments.py index 69d1c0f..1397910 100644 --- a/app/routers/deployments.py +++ b/app/routers/deployments.py @@ -2,10 +2,10 @@ import logging -from fastapi import APIRouter, Depends, HTTPException, status +from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status from sqlalchemy.orm import Session -from app.database import get_db +from app.database import SessionLocal, get_db from app.dependencies import get_current_user from app.models import Customer, Deployment, User from app.services import docker_service, netbird_service @@ -17,35 +17,43 @@ router = APIRouter() @router.post("/{customer_id}/deploy") async def manual_deploy( customer_id: int, + background_tasks: BackgroundTasks, current_user: User = Depends(get_current_user), db: Session = Depends(get_db), ): """Manually trigger deployment for a customer. Use this to re-deploy a customer whose previous deployment failed. + Runs in background and returns immediately. Args: customer_id: Customer ID. Returns: - Deployment result dict. + Acknowledgement dict. """ customer = db.query(Customer).filter(Customer.id == customer_id).first() if not customer: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Customer not found.") - # Remove existing deployment if present - existing = db.query(Deployment).filter(Deployment.customer_id == customer_id).first() - if existing: - await netbird_service.undeploy_customer(db, customer_id) + customer.status = "deploying" + db.commit() - result = await netbird_service.deploy_customer(db, customer_id) - if not result.get("success"): - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=result.get("error", "Deployment failed."), - ) - return result + async def _deploy_bg(cid: int) -> None: + bg_db = SessionLocal() + try: + # Remove existing deployment if present + existing = bg_db.query(Deployment).filter(Deployment.customer_id == cid).first() + if existing: + await netbird_service.undeploy_customer(bg_db, cid) + await netbird_service.deploy_customer(bg_db, cid) + except Exception: + logger.exception("Background re-deploy failed for customer %d", cid) + finally: + bg_db.close() + + background_tasks.add_task(_deploy_bg, customer_id) + return {"message": "Deployment started in background.", "status": "deploying"} @router.post("/{customer_id}/start")