import json import subprocess import logging import ipaddress #import api_utils.carddav_util as carddav_util #from .api_utils import carddav_util from enum import Enum from typing import Any, Dict, List, Optional # import wave import os import shutil from pathlib import Path from tempfile import NamedTemporaryFile from typing import Callable import base64 from datetime import datetime, timedelta from api_utils import api_utils, coerce_methods from collections import OrderedDict from pydantic import BaseModel, Field from fastapi import Depends, FastAPI, HTTPException, File, UploadFile from fastapi.encoders import jsonable_encoder from fastapi.openapi.docs import get_swagger_ui_html from fastapi.openapi.utils import get_openapi from starlette.status import HTTP_403_FORBIDDEN from starlette.responses import RedirectResponse, Response, JSONResponse from starlette.requests import Request from starlette.middleware.cors import CORSMiddleware from starlette.responses import FileResponse from starlette.types import ASGIApp, Message, Receive, Scope, Send from models.cellular_hardware import cellularHardware from models.cellular_hardwares import cellularHardwares from models.call import call, post_call from models.calls import calls from models.httpresponse import httpResponse400, httpResponse200, httpResponse500 from fastapi_login import LoginManager from core.security import manager, NotAuthenticatedException from routes import auth as _auth auth_router = _auth.router from routes import user as _user user_router = _user.router #from routes.posts import router as posts_router from routes import majornet as _majornet majornet_router = _majornet.router from routes import presence as _presence presence_router = _presence.router from routes import contacts as _contacts contacts_router = _contacts.router from routes import reslevis as _reslevis reslevis_router = _reslevis.router #security from fastapi import FastAPI, Security from fastapi.security import OAuth2AuthorizationCodeBearer AUTH_URL = "https://192.168.1.3:10002/realms/API.Server.local/protocol/openid-connect/auth" TOKEN_URL = "https://192.168.1.3:10002/realms/API.Server.local/protocol/openid-connect/token" oauth2 = OAuth2AuthorizationCodeBearer( authorizationUrl=AUTH_URL, tokenUrl=TOKEN_URL, scopes={"items:read": "Read items", "items:write": "Write items"}, ) log = logging.getLogger(__name__) # pylint: disable=invalid-name DEBUG = True app = FastAPI(title="MajorNet API", redoc_url=None, docs_url=None, openapi_url=None) ####DEV ##app = FastAPI(title=PROJECT_NAME) app.debug = True logging.basicConfig(filename='/tmp/app.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s') logging.warning('This will /tmp/app.log') ALLOWED_HOSTS = ["*"] app.add_middleware( CORSMiddleware, allow_origins=ALLOWED_HOSTS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.exception_handler(NotAuthenticatedException) def auth_exception_handler(request: Request, exc: NotAuthenticatedException): """ Redirect the user to the login page if not logged in """ return RedirectResponse(url='/login') app.include_router(auth_router) app.include_router(user_router) #app.include_router(posts_router) app.include_router(presence_router) app.include_router(majornet_router) app.include_router(contacts_router) app.include_router(reslevis_router, prefix="/reslevis", tags=["reslevis"]) @app.get("/") async def root(): #return {"url": "/docs"} return get_swagger_ui_html(openapi_url="/openapi.json", title="docs") @app.get("/openapi.json/", tags=["Documentation"]) async def get_open_api_endpoint(): #async def get_open_api_endpoint(current_user: User = Depends(get_current_active_user)): return JSONResponse(get_openapi(title="MajorNet APIs", version="1.0", routes=app.routes)) @app.get("/docs/", tags=["Documentation"]) #async def get_documentation(current_user: User = Depends(get_current_active_user)): async def get_documentation(): if DEBUG: print("SONO IN /DOCS") return get_swagger_ui_html(openapi_url="/openapi.json", title="docs") @app.post("/majortel/call/", tags=["Majortel"]) async def route_call(active_user=Depends(manager),callerNumber=None, calledNumber=None): try: if DEBUG: print("Entro nel TRY") # Check if the callerNumber sent is an ip address and retrieve the associated number ipaddress.ip_address(callerNumber) callerNumberRetrieved = os.popen('asterisk -rx "sip show peers" | grep ' + callerNumber).read() callerNumberRetrieved = callerNumberRetrieved.split("/") callerNumber = callerNumberRetrieved[0] except: if DEBUG: print("EXCEPT") mode = "callFromTo" from_ = callerNumber to_ = calledNumber if DEBUG: print("DATI ARRIVATI: ") if DEBUG: print(callerNumber) if DEBUG: print(calledNumber) subprocess.Popen(['perl', '/usr/local/bin/ast/voice.pl', mode, from_, to_], stdout=subprocess.PIPE) return @app.post("/majortel/ring/", tags=["Majortel"]) async def route_ring(active_user=Depends(manager),calledNumber=None, calledId=None, ringTime=None): try: if DEBUG: print("Entro nel TRY") # Check if the callerNumber sent is an ip address and retrieve the associated number ipaddress.ip_address(calledNumber) calledNumberRetrieved = os.popen('asterisk -rx "sip show peers" | grep ' + calledNumber).read() calledNumberRetrieved = calledNumberRetrieved.split("/") calledNumber = calledNumberRetrieved[0] except: if DEBUG: print("EXCEPT") mode = "ringAlert" to_ = calledNumber calledId_ = calledId ringTime_ = ringTime if DEBUG: print("DATI ARRIVATI: ") if DEBUG: print(calledNumber) if DEBUG: print(calledId) if DEBUG: print(ringTime) subprocess.Popen(['perl', '/usr/local/bin/ast/voice.pl', mode, to_, calledId_, ringTime_], stdout=subprocess.PIPE) return @app.get("/majortel/hardware/", response_model=cellularHardwares, tags=["Majortel"]) #async def majortel_hardware_get(current_user: User = Depends(get_current_active_user)): async def majortel_hardware_get(): gsm_temp_list = "GSM span1: Provisioned, Up, Active" response = {"CellularHardwares": []} hardware_dict = {} myCmd = os.popen('asterisk -rx "gsm show spans"').read() myCmd = myCmd.split("\n") # cancello l'ultimo item della lista poichè, risultando vuoto dopo lo split, # genera errore "index out of range" nei successivi accessi alla lista if DEBUG: print("spans: ") if DEBUG: print(myCmd) myCmd = os.popen('asterisk -rx "dongle show devices"').read() myCmd = myCmd.split("\n") # cancello il primo item della lista poichè contiene la legenda del myCmd[0] del myCmd[-1] # costruisco la response for device in myCmd: device = device.split() hardware_id = device[0] current_device = os.popen('asterisk -rx "dongle show device state ' + hardware_id + '"').read() current_device = current_device.split("\n") # cancello il primo e gli ultimi item della lista poichè, risultando vuoti dopo lo split, # generano errore "index out of range" nei successivi accessi alla lista del current_device[0] del current_device[-1] del current_device[-1] # costruisco un dizionario a partire dall'output della system call for row in current_device: row = row.split(":") row[0] = row[0].strip() row[1] = row[1].strip() hardware_dict[row[0]] = row[1] hardware_id = hardware_dict["Device"] status = hardware_dict["State"] signal = hardware_dict["RSSI"] signal = int(signal[0:2]) operator = hardware_dict["Provider Name"] device_obj = {"id": hardware_id, "description": "description", "status": status, "signal_level": signal, "registered_number": "To Do", "operator": operator} response["CellularHardwares"].append(device_obj) return response @app.get("/majortel/hardware/{item_id}", response_model=cellularHardware, tags=["Majortel"], responses={400: {"model": httpResponse400}}) #async def majortel_hardware_id_get(item_id: str, current_user: User = Depends(get_current_active_user)): async def majortel_hardware_id_get(item_id: str,active_user=Depends(manager)): hardware_response = {} hardware_dict = {} current_device = os.popen('asterisk -rx "dongle show device state ' + item_id + '"').read() current_device = current_device.split("\n") # cancello il primo e gli ultimi item della lista poichè, risultando vuoti dopo lo split, # generano errore "index out of range" nei successivi accessi alla lista del current_device[0] if (current_device[0]): del current_device[-1] del current_device[-1] # costruisco un dizionario a partire dall'output della system call for row in current_device: row = row.split(":") row[0] = row[0].strip() row[1] = row[1].strip() hardware_dict[row[0]] = row[1] hardware_id = hardware_dict["Device"] status = hardware_dict["State"] signal = hardware_dict["RSSI"] signal = int(signal[0:2]) operator = hardware_dict["Provider Name"] hardware_response = {"id": hardware_id, "description": "description", "status": status, "signal_level": signal, "registered_number": "To Do", "operator": operator} return hardware_response else: return JSONResponse(status_code=404, content={"message": "Device not found"}) @app.get("/majortel/calls/", response_model=calls, tags=["Majortel"]) #async def majortel_calls_get(current_user: User = Depends(get_current_active_user)): async def majortel_calls_get(active_user=Depends(manager)): response = {"Calls": []} p = subprocess.Popen(['perl', '/opt/api_project_python/addCallQueue.pl', "get_calls"], stdout=subprocess.PIPE) calls = str(p.stdout.read()) calls = calls.split("'") response1 = calls calls = calls[1].split("||") # cancello l'ultimo item della lista poichè, risultando vuoto dopo lo split, # genera errore "index out of range" nei successivi accessi alla lista del calls[-1] for call in calls: call = call.split("|") call_id = call[0] status = call[1] history = call[2] ntel = call[3] ackid = call[4] call_obj = {"id": call_id, "status": status, "history": history, "called_number": ntel, "ack_id": ackid} response["Calls"].append(call_obj) return response @app.get("/majortel/calls/{call_id}/", response_model=call, tags=["Majortel"]) async def majortel_calls_id_get(call_id,active_user=Depends(manager)): p = subprocess.Popen(['perl', '/var/opt/FastAPI/addCallQueue.pl', "call_id", call_id], stdout=subprocess.PIPE) call = str(p.stdout.read()) call = call.split("|") call_id = call[0].split("'")[1] status = call[1] # history = call[2] ntel = call[3] ackid = call[4] ackid = int(ackid.split("'")[0]) response = {"id": call_id, "status": status, "called_number": ntel, "ack_id": ackid} return response # response_model=post_call, @app.post("/majortel/calls", response_model=post_call, tags=["Majortel"]) async def majortel_calls_post(hw_id, ack_id, called_number, text_message="codice di riscontro ", timeout=30, retry=1, file: UploadFile = File(None),active_user=Depends(manager)): # controllo se l'hw_id corrisponde ai devices connessi current_device = os.popen('asterisk -rx "dongle show device state ' + hw_id + '"').read() current_device = current_device.split("\n") del current_device[0] if (current_device[0]): curr_time = datetime.now() call_id = str(curr_time.strftime('%y')) + str(curr_time.strftime('%m')) + str(curr_time.strftime('%d')) + str( curr_time.strftime('%H')) + str(curr_time.strftime('%M')) + str(curr_time.strftime('%S')) + str( curr_time.strftime('%f')) file_path = "" if (file): nomefile = file.filename extension = nomefile.split(".")[1] if (extension == "wav"): file_path = "/data/service/voip.majornet/ivr/" + call_id renamed_file = file_path + ".wav" with open(renamed_file, "wb") as f: shutil.copyfileobj(file.file, f) f.close() # p = subprocess.Popen(['(cd /tmp/audioCalls;/usr/local/bin/sox_wav2gsm)'],stdout=subprocess.PIPE) # os.popen('/usr/local/bin/sox_wav2gsm 1>/dev/null 2>/dev/null') subprocess.Popen( ['perl', '/opt/api_project_python/addCallQueue.pl', "voicegateway", "digiovine@afasystems.it", call_id, called_number, ack_id, text_message, "retry message here", timeout, retry, "yes", "notification_to", "notification_bcc", "setup", file_path, "fromemailssss", hw_id], stdout=subprocess.PIPE) response = {"id": call_id} return response else: return JSONResponse(status_code=404, content={"message": "Device not found"}) @app.delete("/majortel/calls/", tags=["Majortel"], responses={200: {"model": httpResponse200}, 400: {"model": httpResponse400}, 500: {"model": httpResponse500}}) #async def majortel_calls_id_delete(call_id, current_user: User = Depends(get_current_active_user)): async def majortel_calls_id_delete(call_id,active_user=Depends(manager)): p = subprocess.Popen(['perl', '/opt/api_project_python/addCallQueue.pl', "delete_call", call_id], stdout=subprocess.PIPE) response = str(p.stdout.read()) response = response.split("'")[1] if (response == "Success"): return JSONResponse(status_code=200, content={"message": "Success"}) elif (response == "Not found"): return JSONResponse(status_code=404, content={"message": "Call not found"}) else: return JSONResponse(status_code=500, content={"message": "Server error"})