211 lines
6.8 KiB
Python
211 lines
6.8 KiB
Python
from uuid import UUID
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from pydantic import BaseModel, Field
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session, joinedload
|
|
|
|
from .auth import current_user
|
|
from .database import get_db
|
|
from .message_send_service import SendMessageError, send_template_messages
|
|
from .models import SenderConfig, TemplateConfig, UserAccount
|
|
from .sender_validators import (
|
|
validate_dlt_entity_id,
|
|
validate_purpose,
|
|
validate_sender_id,
|
|
validate_sender_type,
|
|
)
|
|
from .template_validators import (
|
|
validate_dlt_template_id,
|
|
validate_template_name,
|
|
validate_template_text,
|
|
)
|
|
|
|
router = APIRouter(prefix="/api/v1/messaging", tags=["messaging"])
|
|
|
|
|
|
class CreateSenderRequest(BaseModel):
|
|
sender_id: str = Field(min_length=3, max_length=11)
|
|
sender_type: str
|
|
purpose: str = Field(min_length=10, max_length=500)
|
|
dlt_entity_id: str = Field(min_length=12, max_length=25)
|
|
|
|
|
|
class CreateTemplateRequest(BaseModel):
|
|
sender_config_id: str
|
|
template_name: str = Field(min_length=3, max_length=40)
|
|
template_text: str = Field(min_length=10, max_length=1000)
|
|
dlt_template_id: str = Field(min_length=12, max_length=25)
|
|
|
|
|
|
def serialize_sender(sender: SenderConfig) -> dict:
|
|
return {
|
|
"id": str(sender.id),
|
|
"sender_id": sender.sender_id,
|
|
"sender_type": sender.sender_type,
|
|
"purpose": sender.purpose,
|
|
"dlt_entity_id": sender.dlt_entity_id,
|
|
"status": sender.status,
|
|
"review_note": sender.review_note,
|
|
"created_at": sender.created_at,
|
|
"reviewed_at": sender.reviewed_at,
|
|
}
|
|
|
|
|
|
def serialize_template(template: TemplateConfig) -> dict:
|
|
return {
|
|
"id": str(template.id),
|
|
"sender_config_id": str(template.sender_config_id),
|
|
"sender_id": template.sender_config.sender_id,
|
|
"template_name": template.template_name,
|
|
"template_text": template.template_text,
|
|
"dlt_template_id": template.dlt_template_id,
|
|
"status": template.status,
|
|
"review_note": template.review_note,
|
|
"created_at": template.created_at,
|
|
"reviewed_at": template.reviewed_at,
|
|
}
|
|
|
|
|
|
@router.get("/senders")
|
|
def list_senders(
|
|
db: Session = Depends(get_db),
|
|
user: UserAccount = Depends(current_user),
|
|
) -> list[dict]:
|
|
senders = db.scalars(
|
|
select(SenderConfig)
|
|
.where(SenderConfig.user_id == user.id)
|
|
.order_by(SenderConfig.created_at.desc())
|
|
).all()
|
|
return [serialize_sender(sender) for sender in senders]
|
|
|
|
|
|
@router.post("/senders", status_code=status.HTTP_201_CREATED)
|
|
def create_sender(
|
|
payload: CreateSenderRequest,
|
|
db: Session = Depends(get_db),
|
|
user: UserAccount = Depends(current_user),
|
|
) -> dict:
|
|
try:
|
|
sender_id = validate_sender_id(payload.sender_id)
|
|
sender_type = validate_sender_type(payload.sender_type)
|
|
purpose = validate_purpose(payload.purpose)
|
|
dlt_entity_id = validate_dlt_entity_id(payload.dlt_entity_id)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(exc)) from exc
|
|
|
|
existing = db.scalar(
|
|
select(SenderConfig).where(
|
|
SenderConfig.user_id == user.id,
|
|
SenderConfig.sender_id == sender_id,
|
|
)
|
|
)
|
|
if existing:
|
|
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Sender ID already exists")
|
|
|
|
sender = SenderConfig(
|
|
user_id=user.id,
|
|
sender_id=sender_id,
|
|
sender_type=sender_type,
|
|
purpose=purpose,
|
|
dlt_entity_id=dlt_entity_id,
|
|
status="pending",
|
|
)
|
|
db.add(sender)
|
|
db.commit()
|
|
db.refresh(sender)
|
|
return serialize_sender(sender)
|
|
|
|
|
|
@router.get("/templates")
|
|
def list_templates(
|
|
db: Session = Depends(get_db),
|
|
user: UserAccount = Depends(current_user),
|
|
) -> list[dict]:
|
|
templates = db.scalars(
|
|
select(TemplateConfig)
|
|
.options(joinedload(TemplateConfig.sender_config))
|
|
.where(TemplateConfig.user_id == user.id)
|
|
.order_by(TemplateConfig.created_at.desc())
|
|
).unique().all()
|
|
return [serialize_template(template) for template in templates]
|
|
|
|
|
|
@router.post("/templates", status_code=status.HTTP_201_CREATED)
|
|
def create_template(
|
|
payload: CreateTemplateRequest,
|
|
db: Session = Depends(get_db),
|
|
user: UserAccount = Depends(current_user),
|
|
) -> dict:
|
|
try:
|
|
template_name = validate_template_name(payload.template_name)
|
|
template_text = validate_template_text(payload.template_text)
|
|
dlt_template_id = validate_dlt_template_id(payload.dlt_template_id)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(exc)) from exc
|
|
|
|
sender = db.scalar(
|
|
select(SenderConfig).where(
|
|
SenderConfig.id == payload.sender_config_id,
|
|
SenderConfig.user_id == user.id,
|
|
)
|
|
)
|
|
if not sender:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Sender ID not found")
|
|
if sender.status != "approved":
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail="Template can only be linked to an approved sender ID",
|
|
)
|
|
|
|
existing = db.scalar(
|
|
select(TemplateConfig).where(
|
|
TemplateConfig.user_id == user.id,
|
|
TemplateConfig.template_name == template_name,
|
|
)
|
|
)
|
|
if existing:
|
|
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Template name already exists")
|
|
|
|
template = TemplateConfig(
|
|
user_id=user.id,
|
|
sender_config_id=sender.id,
|
|
template_name=template_name,
|
|
template_text=template_text,
|
|
dlt_template_id=dlt_template_id,
|
|
status="pending",
|
|
)
|
|
db.add(template)
|
|
db.commit()
|
|
db.refresh(template)
|
|
db.refresh(sender)
|
|
template.sender_config = sender
|
|
return serialize_template(template)
|
|
|
|
|
|
class ComposeSendRequest(BaseModel):
|
|
sender_config_id: UUID
|
|
template_id: UUID
|
|
numbers: str = Field(min_length=10, max_length=5000)
|
|
message: str = Field(min_length=1, max_length=1000)
|
|
campaign_name: str | None = Field(default=None, max_length=120)
|
|
|
|
|
|
@router.post("/send")
|
|
def compose_send_message(
|
|
payload: ComposeSendRequest,
|
|
db: Session = Depends(get_db),
|
|
user: UserAccount = Depends(current_user),
|
|
) -> dict:
|
|
try:
|
|
return send_template_messages(
|
|
db,
|
|
user,
|
|
sender_config_id=payload.sender_config_id,
|
|
template_id=payload.template_id,
|
|
message=payload.message,
|
|
recipients=[part.strip() for part in payload.numbers.split(",") if part.strip()],
|
|
campaign_name=payload.campaign_name,
|
|
)
|
|
except SendMessageError as exc:
|
|
raise HTTPException(status_code=exc.status_code, detail=exc.detail) from exc
|