first commit
This commit is contained in:
0
app/__init__.py
Normal file
0
app/__init__.py
Normal file
22
app/config.py
Normal file
22
app/config.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
class Settings(BaseSettings):
|
||||
# Load settings from the .env file
|
||||
model_config = SettingsConfigDict(env_file="../.env", env_file_encoding='utf-8')
|
||||
|
||||
# VoIP.ms Credentials
|
||||
voipms_api_username: str
|
||||
voipms_api_password: str
|
||||
|
||||
# Target DID and Destinations
|
||||
target_did: str
|
||||
target_sip_account: str
|
||||
target_cellphone_1: str
|
||||
target_cellphone_2: str
|
||||
|
||||
# VoIP.ms API endpoint
|
||||
voipms_api_url: str = "https://voip.ms/api/v1/rest.php"
|
||||
|
||||
|
||||
# Create a single instance of the settings to be used throughout the app
|
||||
settings = Settings()
|
||||
34
app/database.py
Normal file
34
app/database.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy.engine import URL
|
||||
from config import load_config
|
||||
|
||||
|
||||
ApplicationConfig = load_config()
|
||||
|
||||
|
||||
url = URL.create(
|
||||
drivername="postgresql",
|
||||
username=ApplicationConfig.POSTGRES_USERNAME,
|
||||
password=ApplicationConfig.POSTGRES_PW,
|
||||
host=ApplicationConfig.POSTGRES_SERVER,
|
||||
database=ApplicationConfig.POSTGRES_DBNAME00,
|
||||
port=ApplicationConfig.POSTGRES_PORT
|
||||
)
|
||||
|
||||
engine = create_engine(url)
|
||||
|
||||
Session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
session = Session()
|
||||
|
||||
Base = declarative_base()
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
|
||||
def get_db():
|
||||
db = Session()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
82
app/main.py
Normal file
82
app/main.py
Normal file
@@ -0,0 +1,82 @@
|
||||
from fastapi import FastAPI, HTTPException, status
|
||||
from .config import settings
|
||||
from .voipms_client import update_did_routing
|
||||
from .database import Session
|
||||
from .models import Call
|
||||
|
||||
app = FastAPI(
|
||||
title="EAMCO VoIP.ms Controller",
|
||||
description="An API to manage routing for a VoIP.ms DID.",
|
||||
version="1.0.0",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@app.get("/", tags=["General"])
|
||||
def read_root():
|
||||
"""A simple root endpoint to confirm the API is running."""
|
||||
return {"message": f"Welcome to the VoIP.ms API for DID: {settings.target_did}"}
|
||||
|
||||
|
||||
@app.post("/route/sip", status_code=status.HTTP_200_OK, tags=["DID Routing"])
|
||||
def route_to_sip_account():
|
||||
"""
|
||||
Routes the target DID to the pre-configured SIP account.
|
||||
"""
|
||||
routing_string = f"sip:{settings.target_sip_account}"
|
||||
try:
|
||||
result = update_did_routing(did=settings.target_did, routing=routing_string)
|
||||
target_phone = routing_string.split(':')[1]
|
||||
db = Session()
|
||||
db.add(Call(current_phone=target_phone))
|
||||
db.commit()
|
||||
db.close()
|
||||
return {
|
||||
"message": f"Successfully routed DID {settings.target_did} to SIP account {settings.target_sip_account}",
|
||||
"voipms_response": result
|
||||
}
|
||||
except HTTPException as e:
|
||||
# Re-raise the exception to let FastAPI handle the response
|
||||
raise e
|
||||
|
||||
|
||||
@app.post("/route/cellphone1", status_code=status.HTTP_200_OK, tags=["DID Routing"])
|
||||
def route_to_cellphone_1():
|
||||
"""
|
||||
Routes the target DID to the pre-configured Cellphone #1.
|
||||
"""
|
||||
routing_string = f"fwd:{settings.target_cellphone_1}"
|
||||
try:
|
||||
result = update_did_routing(did=settings.target_did, routing=routing_string)
|
||||
target_phone = routing_string.split(':')[1]
|
||||
db = Session()
|
||||
db.add(Call(current_phone=target_phone))
|
||||
db.commit()
|
||||
db.close()
|
||||
return {
|
||||
"message": f"Successfully routed DID {settings.target_did} to Cellphone #1 ({settings.target_cellphone_1})",
|
||||
"voipms_response": result
|
||||
}
|
||||
except HTTPException as e:
|
||||
raise e
|
||||
|
||||
|
||||
@app.post("/route/cellphone2", status_code=status.HTTP_200_OK, tags=["DID Routing"])
|
||||
def route_to_cellphone_2():
|
||||
"""
|
||||
Routes the target DID to the pre-configured Cellphone #2.
|
||||
"""
|
||||
routing_string = f"fwd:{settings.target_cellphone_2}"
|
||||
try:
|
||||
result = update_did_routing(did=settings.target_did, routing=routing_string)
|
||||
target_phone = routing_string.split(':')[1]
|
||||
db = Session()
|
||||
db.add(Call(current_phone=target_phone))
|
||||
db.commit()
|
||||
db.close()
|
||||
return {
|
||||
"message": f"Successfully routed DID {settings.target_did} to Cellphone #2 ({settings.target_cellphone_2})",
|
||||
"voipms_response": result
|
||||
}
|
||||
except HTTPException as e:
|
||||
raise e
|
||||
41
app/models.py
Normal file
41
app/models.py
Normal file
@@ -0,0 +1,41 @@
|
||||
## File: your_app/models.py
|
||||
|
||||
from sqlalchemy import Column, Integer, String, Float, DateTime, Boolean, ForeignKey, Numeric
|
||||
from .database import Base
|
||||
import datetime
|
||||
|
||||
class Customer(Base):
|
||||
__tablename__ = "customer_customer"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
|
||||
# --- ADD THIS COLUMN ---
|
||||
# This stores the master profile ID from Authorize.Net's CIM.
|
||||
auth_net_profile_id = Column(String(100))
|
||||
|
||||
# --- YOUR EXISTING COLUMNS ---
|
||||
account_number = Column(String(25))
|
||||
customer_last_name = Column(String(250))
|
||||
customer_first_name = Column(String(250))
|
||||
customer_town = Column(String(140))
|
||||
customer_state = Column(Integer)
|
||||
customer_zip = Column(String(25))
|
||||
customer_first_call = Column(DateTime)
|
||||
customer_email = Column(String(500))
|
||||
customer_automatic = Column(Integer)
|
||||
customer_phone_number = Column(String(25))
|
||||
customer_home_type = Column(Integer)
|
||||
customer_apt = Column(String(140))
|
||||
customer_address = Column(String(1000))
|
||||
company_id = Column(Integer)
|
||||
customer_latitude = Column(String(250))
|
||||
customer_longitude = Column(String(250))
|
||||
correct_address = Column(Boolean)
|
||||
|
||||
# --- ADD THIS ENTIRE NEW MODEL ---
|
||||
class Call(Base):
|
||||
__tablename__ = "call_call"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
current_phone = Column(String(500))
|
||||
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
56
app/voipms_client.py
Normal file
56
app/voipms_client.py
Normal file
@@ -0,0 +1,56 @@
|
||||
import requests
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
from .config import settings
|
||||
|
||||
def update_did_routing(did: str, routing: str):
|
||||
"""
|
||||
Calls the VoIP.ms API to update the routing for a specific DID.
|
||||
|
||||
Args:
|
||||
did (str): The phone number (DID) to update.
|
||||
routing (str): The new routing string (e.g., 'sip:user@server' or 'fwd:15551234567').
|
||||
|
||||
Raises:
|
||||
HTTPException: If the API call fails or returns an error.
|
||||
|
||||
Returns:
|
||||
dict: The JSON response from the VoIP.ms API on success.
|
||||
"""
|
||||
params = {
|
||||
"api_username": settings.voipms_api_username,
|
||||
"api_password": settings.voipms_api_password,
|
||||
"method": "setDIDInfo",
|
||||
"did": did,
|
||||
"routing": routing,
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.get(settings.voipms_api_url, params=params)
|
||||
response.raise_for_status() # Raises an HTTPError for bad responses (4xx or 5xx)
|
||||
|
||||
data = response.json()
|
||||
|
||||
# VoIP.ms API has its own status field in the JSON response
|
||||
if data.get("status") != "success":
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"VoIP.ms API Error: {data.get('status')}"
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
# Handle network errors, timeouts, etc.
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
detail=f"Failed to connect to VoIP.ms API: {e}"
|
||||
)
|
||||
except Exception as e:
|
||||
# Catch any other exceptions, including the ones we raised manually
|
||||
if isinstance(e, HTTPException):
|
||||
raise e # Re-raise if it's already an HTTPException
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"An unexpected error occurred: {e}"
|
||||
)
|
||||
Reference in New Issue
Block a user