first commit

This commit is contained in:
debu-2 2026-10-02 11:22:40 +05:30
commit efa5080897
127 changed files with 18610 additions and 0 deletions

View 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