@router.post("/{cid}/start") async def start(cid: int, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), _: None = Depends(require_csrf)): c = await _get_own(db, user, cid) if c.status == "draft": # materialize campaign_recipients on first start if not c.recipient_list_id: raise HTTPException(status_code=400, detail="Campaign has no recipient list") rl = await db.get(RecipientList, c.recipient_list_id) if not rl: raise HTTPException(status_code=400, detail="Recipient list not found") recipient_rows = (await db.execute( select(Recipient).where(Recipient.list_id == c.recipient_list_id, Recipient.is_valid == True) # noqa: E712 .order_by(Recipient.id))).scalars().all() if not recipient_rows: raise HTTPException(status_code=400, detail="No valid recipients in list") if len(recipient_rows) > cfg.MAX_RECIPIENTS_PER_CAMPAIGN: raise HTTPException(status_code=400, detail=f"Too many recipients ({len(recipient_rows)}); max {cfg.MAX_RECIPIENTS_PER_CAMPAIGN}") result = await materialize_campaign(db, c, recipient_rows) c.total_recipients = result.get("total", 0) await db.commit() res = await start_campaign(db, c) if not res.get("ok"): raise HTTPException(status_code=409, detail=res.get("reason", "Cannot start")) await log_audit(db, user.id, "campaign.start", "campaign", c.id, {"queued": res.get("queued")}) return {"status": "running", "queued": res.get("queued")} @router.post("/{cid}/pause") async def pause(cid: int, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), _: None = Depends(require_csrf)): c = await _get_own(db, user, cid) if c.status != "running": raise HTTPException(status_code=409, detail=f"Cannot pause a {c.status} campaign") res = await pause_campaign(db, c) await log_audit(db, user.id, "campaign.pause", "campaign", c.id, {}) return {"status": "paused"} @router.post("/{cid}/resume") async def resume(cid: int, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), _: None = Depends(require_csrf)): c = await _get_own(db, user, cid) if c.status != "paused": raise HTTPException(status_code=409, detail=f"Cannot resume a {c.status} campaign") res = await resume_campaign(db, c) if not res.get("ok"): raise HTTPException(status_code=409, detail=res.get("reason", "Cannot resume")) await log_audit(db, user.id, "campaign.resume", "campaign", c.id, {}) return {"status": "running", "queued": res.get("queued")}