first commit
This commit is contained in:
commit
efa5080897
127 changed files with 18610 additions and 0 deletions
211
backend/app/routes_messaging.py
Normal file
211
backend/app/routes_messaging.py
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
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
|
||||
Loading…
Add table
Add a link
Reference in a new issue