feat: endpoint for caddy

This commit is contained in:
deepvasoya 2025-06-09 15:03:58 +05:30
parent 165385358f
commit db20c498c2
6 changed files with 95 additions and 266 deletions

View File

@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends, Security from fastapi import APIRouter, Depends
from middleware.auth_dependency import auth_required from middleware.auth_dependency import auth_required
from fastapi.security import HTTPBearer from fastapi.security import HTTPBearer
@ -8,7 +8,7 @@ bearer_scheme = HTTPBearer(scheme_name="Bearer Authentication")
from .endpoints import clinics, doctors, calender, appointments, patients, admin, auth, s3, users, clinicDoctor, dashboard, call_transcripts, notifications,sns, stripe from .endpoints import clinics, doctors, calender, appointments, patients, admin, auth, s3, users, clinicDoctor, dashboard, call_transcripts, notifications,sns, stripe
api_router = APIRouter() api_router = APIRouter()
# api_router.include_router(twilio.router, prefix="/twilio")
api_router.include_router(clinics.router, prefix="/clinics", tags=["clinics"], dependencies=[Depends(auth_required)]) api_router.include_router(clinics.router, prefix="/clinics", tags=["clinics"], dependencies=[Depends(auth_required)])
api_router.include_router(doctors.router, prefix="/doctors", tags=["doctors"]) api_router.include_router(doctors.router, prefix="/doctors", tags=["doctors"])

View File

@ -1,4 +1,4 @@
from fastapi import APIRouter, BackgroundTasks from fastapi import APIRouter, BackgroundTasks, Request, status
from services.authService import AuthService from services.authService import AuthService
from schemas.CreateSchemas import UserCreate from schemas.CreateSchemas import UserCreate
from schemas.ApiResponse import ApiResponse from schemas.ApiResponse import ApiResponse
@ -59,3 +59,9 @@ async def verify_otp(data: AuthOTP):
data="OK", data="OK",
message="OTP verified successfully" message="OTP verified successfully"
) )
@router.get("/is-valid-domain")
async def is_valid_domain(req:Request):
host = req.headers.get("host")
is_valid = await ClinicServices().is_valid_domain(host)
return status.HTTP_200_OK if is_valid else status.HTTP_404_NOT_FOUND

View File

@ -11,19 +11,19 @@ stripe_service = StripeServices()
# async def create_checkout_session(user_id: int): # async def create_checkout_session(user_id: int):
# return await stripe_service.create_checkout_session(1) # return await stripe_service.create_checkout_session(1)
# @router.post("/create-subscription-checkout") @router.post("/create-subscription-checkout")
# async def create_subscription_checkout(): async def create_subscription_checkout():
# return await stripe_service.create_subscription_checkout( return await stripe_service.create_subscription_checkout(
# fees_to_be={ fees_to_be={
# "per_call_charges": 10, "per_call_charges": 10,
# "setup_fees": 100, "setup_fees": 100,
# "subscription_fees": 100, "subscription_fees": 100,
# "total": 210 "total": 210
# }, },
# clinic_id=1, clinic_id=1,
# account_id="acct_1RT1UFPTNqn2kWQ8", account_id="acct_1RT1UFPTNqn2kWQ8",
# customer_id="cus_SNn49FDltUcSLP" customer_id="cus_SNn49FDltUcSLP"
# ) )
@router.get("/create-stripe-account-link", dependencies=[Depends(auth_required)]) @router.get("/create-stripe-account-link", dependencies=[Depends(auth_required)])
async def create_stripe_account_link(req:Request): async def create_stripe_account_link(req:Request):

View File

@ -1,183 +0,0 @@
# import asyncio
# import json
# import logging
# import os
# from fastapi import APIRouter, Request, WebSocket, status
# from twilio.twiml.voice_response import VoiceResponse, Connect
# from twilio.rest import Client
# from fastapi import WebSocket, Request, Response
# from enum import Enum
# from typing import Optional
# from services.bot import run_bot
# from services.call_state import CallState
# logger = logging.getLogger(__name__)
# router = APIRouter()
# BASE_WS_URL = "wss://13.229.247.61:8000/api/twilio"
# BASE_URL = "http://13.229.247.61:8000/api/twilio"
# DTMF_SWITCH_KEY = "*" # Key to switch between models
# LOG_EVENT_TYPES = [
# "error",
# "response.content.done",
# "rate_limits.updated",
# "response.done",
# "input_audio_buffer.committed",
# "input_audio_buffer.speech_stopped",
# "input_audio_buffer.speech_started",
# "session.created",
# ]
# SHOW_TIMING_MATH = False
# MENU_OPTIONS = """
# Press 1 for Model 1.
# Press 2 for Model 2.
# Press 3 for Model 3.
# Press 4 for Model 4.
# Press 5 for Model 5.
# Press 0 to repeat the options.
# """
# call_state = CallState
# # Define model options as enum for type safety
# class ModelOption(int, Enum):
# MODEL_1 = 1
# MODEL_2 = 2
# MODEL_3 = 3
# MODEL_4 = 4
# MODEL_5 = 5
# @router.post("/call")
# async def get_call():
# SID = os.getenv("TWILIO_SID")
# AUTH_TOKEN = os.getenv("TWILIO_AUTH")
# client = Client(SID, AUTH_TOKEN)
# call = client.calls.create(
# from_="+14149466486",
# to="+917984372159",
# record=True,
# url=f"{BASE_URL}/receive",
# )
# return status.HTTP_200_OK
# # @router.websocket("/media-stream/{id}")
# # async def handle_media_stream(websocket: WebSocket, id: int):
# # """Handle WebSocket connections between Twilio and OpenAI."""
# # print(f"Client connected with id: {id}")
# # await websocket.accept()
# # start_data = websocket.iter_text()
# # await start_data.__anext__()
# # call_data = json.loads(await start_data.__anext__())
# # print(call_data, flush=True)
# # stream_sid = call_data["start"]["streamSid"]
# # print("WebSocket connection accepted")
# # await run_bot(websocket, stream_sid, False, option=id)
# @router.websocket("/media-stream/{id}")
# async def handle_media_stream(websocket: WebSocket, id: int):
# """Handle WebSocket connections between Twilio and OpenAI."""
# logger.info(f"Client connected with id: {id}")
# print(f"Client connected with id: {id}")
# await websocket.accept()
# start_data = websocket.iter_text()
# await start_data.__anext__()
# call_data = json.loads(await start_data.__anext__())
# print(call_data, flush=True)
# logger.info(call_data)
# stream_sid = call_data["start"]["streamSid"]
# print("WebSocket connection accepted")
# logger.info("WebSocket connection accepted")
# await run_bot(websocket, stream_sid, False, option=id)
# # @router.post("/receive")
# # async def receive_call(req: Request):
# # print("----------------- received call -----------------")
# # resp = VoiceResponse()
# # connect = Connect()
# # connect.stream(
# # url=f"wss://allegedly-known-wasp.ngrok-free.app/api/twilio/media-stream"
# # )
# # resp.append(connect)
# # return Response(content=str(resp), media_type="application/xml")
# @router.post("/receive")
# async def receive_call(req: Request):
# print("----------------- received call -----------------")
# resp = VoiceResponse()
# # Gather DTMF input
# gather = resp.gather(
# num_digits=1, action="/api/twilio/handle-key", method="POST", timeout=10
# )
# # Initial greeting and menu options
# gather.say(MENU_OPTIONS)
# # If no input received, redirect back to the main menu
# resp.redirect("/api/twilio/receive")
# return Response(content=str(resp), media_type="application/xml")
# @router.post("/handle-key")
# async def handle_key_press(req: Request):
# """Process the key pressed by the caller and connect to the appropriate model."""
# try:
# form_data = await req.form()
# digits_pressed = form_data.get("Digits", "")
# print(f"User pressed: {digits_pressed}")
# resp = VoiceResponse()
# if digits_pressed == "0":
# # Repeat options
# resp.redirect("/api/twilio/receive")
# elif digits_pressed in "12345":
# # Valid model selection
# model_id = int(digits_pressed)
# resp.say(f"You have selected model {model_id}.")
# # Connect to WebSocket
# connect = Connect()
# connect.stream(url=f"{BASE_WS_URL}/media-stream/{model_id}")
# resp.append(connect)
# else:
# # Invalid selection
# resp.say("Invalid selection. Please try again.")
# resp.redirect("/api/twilio/receive")
# return Response(content=str(resp), media_type="application/xml")
# except Exception as e:
# print(f"Error handling key press: {str(e)}")
# resp = VoiceResponse()
# resp.say(
# "We encountered a problem processing your request. Please try again later."
# )
# return Response(content=str(resp), media_type="application/xml")
# @router.post("/error")
# async def read_item(req: Request):
# print(await req.body())
# print(await req.form())
# logger.error(await req.body())
# return status.HTTP_200_OK

View File

@ -1,8 +1,8 @@
from database import get_db from database import get_db
from sqlalchemy.orm import Session, joinedload from sqlalchemy.orm import Session, joinedload
from schemas.UpdateSchemas import ClinicStatusUpdate, ClinicUpdate from schemas.UpdateSchemas import ClinicUpdate
from schemas.ResponseSchemas import Clinic, ClinicOfferResponse from schemas.ResponseSchemas import Clinic, ClinicOfferResponse
from typing import List, Literal, Optional, Union from typing import Literal, Optional, Union
from exceptions import ResourceNotFoundException, ValidationException from exceptions import ResourceNotFoundException, ValidationException
from enums.enums import ClinicStatus, UserType from enums.enums import ClinicStatus, UserType
from exceptions.unauthorized_exception import UnauthorizedException from exceptions.unauthorized_exception import UnauthorizedException
@ -18,6 +18,7 @@ from loguru import logger
from sqlalchemy import func from sqlalchemy import func
from exceptions.db_exceptions import DBExceptionHandler from exceptions.db_exceptions import DBExceptionHandler
class ClinicServices: class ClinicServices:
def __init__(self): def __init__(self):
self.db: Session = next(get_db()) self.db: Session = next(get_db())
@ -424,3 +425,18 @@ class ClinicServices:
DBExceptionHandler.handle_exception(e, context="deleting clinic offer") DBExceptionHandler.handle_exception(e, context="deleting clinic offer")
finally: finally:
self.db.close() self.db.close()
async def is_valid_domain(self, domain:str):
subdomain = domain.split(".")[0]
# allow main domain, backend and admin domains
if(subdomain in ["toBeDomain", "backend", "admin"]):
return True
# check if any clinic has domain
clinic_domain = self.db.query(Clinic).filter(Clinic.domain == subdomain).first()
if clinic_domain:
return True
return False

View File

@ -193,6 +193,7 @@ class StripeServices:
finally: finally:
self.db.close() self.db.close()
# NOTE: in case when checkout session expired or not created
async def create_payment_session(self, user): async def create_payment_session(self, user):
try: try:
if user["userType"] != UserType.CLINIC_ADMIN: if user["userType"] != UserType.CLINIC_ADMIN:
@ -252,46 +253,34 @@ class StripeServices:
finally: finally:
self.db.close() self.db.close()
async def create_checkout_session(self, user_id: int): # NOTE: This is not used
try: # async def create_checkout_session(self, user_id: int):
checkout_session = await stripe.checkout.Session.create_async( # try:
payment_method_types=["card"], # checkout_session = await stripe.checkout.Session.create_async(
line_items=[ # payment_method_types=["card"],
{ # line_items=[
"price_data": { # {
"currency": "aud", # "price_data": {
"product_data": { # "currency": "aud",
"name": "Willio Voice Subscription", # "product_data": {
}, # "name": "Willio Voice Subscription",
"unit_amount": 5000, # },
}, # "unit_amount": 5000,
"quantity": 1, # },
} # "quantity": 1,
], # }
expand=["payment_intent"], # ],
mode="payment", # expand=["payment_intent"],
payment_intent_data={"metadata": {"order_id": "1"}}, # mode="payment",
success_url=f"{self.redirect_url}auth/waiting", # payment_intent_data={"metadata": {"order_id": "1"}},
cancel_url=f"{self.redirect_url}auth/waiting", # success_url=f"{self.redirect_url}auth/waiting",
metadata={"user_id": user_id}, # cancel_url=f"{self.redirect_url}auth/waiting",
) # metadata={"user_id": user_id},
return checkout_session # )
except stripe.error.StripeError as e: # return checkout_session
self.logger.error(f"Error creating checkout session: {e}") # except stripe.error.StripeError as e:
raise # self.logger.error(f"Error creating checkout session: {e}")
# raise
async def create_setup_fees(self, customer_id: str, amount: int):
try:
setup_intent = await stripe.InvoiceItem.create_async(
customer=customer_id,
amount=amount,
currency="aud",
description="Setup Fees",
)
return setup_intent
except stripe.error.StripeError as e:
self.logger.error(f"Error creating setup intent: {e}")
raise
async def create_subscription_checkout( async def create_subscription_checkout(
self, fees_to_be: dict, clinic_id: int, account_id: str, customer_id: str self, fees_to_be: dict, clinic_id: int, account_id: str, customer_id: str
@ -313,6 +302,7 @@ class StripeServices:
), # Convert to cents ), # Convert to cents
"recurring": { "recurring": {
"interval": "year", "interval": "year",
"interval_count": 3, #NOTE: max 3 years supported by stripe
}, },
}, },
"quantity": 1, "quantity": 1,