import datetime from typing import Annotated from uuid import UUID from fastapi import APIRouter, Depends, Query, HTTPException, Request from sqlalchemy import select from sqlalchemy.orm import Session from app.core.config import settings from app.models import User, Sms, ServerStatus from app.schemas.responses import SMSResponse, SMSServerStatus, EnumServerStatus from app.api import deps router = APIRouter(prefix="/sms-sender", tags=["sms_sender"]) @router.get("/sms/to-send", response_model=list[SMSResponse]) async def list_sms_to_send( current_user: User = Depends(deps.get_current_user), session: Session = Depends(deps.get_session), max_delay: Annotated[ int | None, Query(description="the maximum delay a sms should be send with") ] = 10, ): """List sms that should be send by now""" now = datetime.datetime.now() min_sending_time = now - datetime.timedelta(minutes=max_delay) results = session.execute( select(Sms).where( (Sms.sending_time > min_sending_time) & (Sms.sending_time < now) & (Sms.send_time == None) # noqa: E711 ) ) return results.scalars().all() @router.post("/sms/send-now/{sms_id}", response_model=SMSResponse) async def send_sms_now( request: Request, sms_id: UUID, current_user: User = Depends(deps.get_current_user), session: Session = Depends(deps.get_session), ): """Update the SMS to be sent now""" sms = session.get(Sms, sms_id) if sms is None: raise HTTPException(status_code=404, detail="SMS not found") elif sms.send_time is not None: raise HTTPException(status_code=400, detail="SMS has already been sent") else: sms.send_time = datetime.datetime.now() session.commit() await update_status(request, current_user, session) return sms @router.get("/sms/not-send", response_model=list[SMSResponse]) async def list_not_sent( current_user: User = Depends(deps.get_current_user), session: Session = Depends(deps.get_session), ): """List sms that are not sent""" results = session.execute(select(Sms).where((Sms.send_time == None))) # noqa: E711 return results.scalars().all() @router.get("/sms/future", response_model=list[SMSResponse]) async def list_future_sms( current_user: User = Depends(deps.get_current_user), session: Session = Depends(deps.get_session), ): """List sms that should be sent in the future""" results = session.execute(select(Sms).where(Sms.sending_time > datetime.datetime.now())) return results.scalars().all() @router.post("/status") async def update_status( request: Request, current_user: User = Depends(deps.get_current_user), session: Session = Depends(deps.get_session), ): """Update the status of the SMS server""" status = session.query(ServerStatus).filter_by(id=1).first() if not status: status = ServerStatus(id=1) status.host = request.client.host status.updated_at = datetime.datetime.now(datetime.timezone.utc) status.user_agent = request.headers.get("user-agent", "unknown") session.add(status) session.commit() return {"message": "Status updated successfully"} INACTIVITY_THRESHOLD_SECONDS = 180 @router.get("/status", response_model=SMSServerStatus) async def get_status( current_user: User = Depends(deps.get_current_user), session: Session = Depends(deps.get_session), ): """Get the latest status of the SMS server""" status: ServerStatus | None = session.query(ServerStatus).filter_by(id=1).first() now = datetime.datetime.now(datetime.timezone.utc) if not status: return SMSServerStatus( updated_at=now, host="N/A", user_agent="N/A", message=EnumServerStatus.INVALID ) else: msg = ( EnumServerStatus.INACTIVE if (now - status.updated_at).total_seconds() > settings.INACTIVITY_SMS_SENDER_THRESHOLD_SECONDS else EnumServerStatus.ACTIVE ) return SMSServerStatus( updated_at=status.updated_at.isoformat(), host=status.host, user_agent=status.user_agent, message=msg, )