diff --git a/Dockerfile b/Dockerfile index c76c08c..c1cf12a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,7 @@ EXPOSE 8000 # up the FastAPI workers. # NOTE: If you run this Dockerfile via "docker compose up", CMD will be overridden # by docker-compose.yaml -CMD ["gunicorn", "app.main:sio_app", \ +CMD ["gunicorn", "app.main:main_app", \ "--bind", "0.0.0.0:8000", \ "--access-logfile", "-", \ "--workers", "1", \ diff --git a/app/config.py b/app/config.py index 5b36fed..37b5984 100644 --- a/app/config.py +++ b/app/config.py @@ -14,6 +14,7 @@ class Settings(BaseSettings): enable_examples: bool = True enable_excel_online: bool = True enable_htmx: bool = True + enable_socketio: bool = True entraid_client_id: Optional[str] = None entraid_tenant_id: Optional[str] = None # Set to False if you have users from external organizations diff --git a/app/main.py b/app/main.py index d6c7a9e..1b26524 100644 --- a/app/main.py +++ b/app/main.py @@ -21,11 +21,6 @@ # App app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None) -# Routers -app.include_router(xlwings_router) -app.include_router(macros_router) -app.include_router(taskpane_router) - # CORS: Office Scripts and custom functions in Excel on the web require CORS # Using app.add_middleware won't add the CORS headers if you handle the root "Exception" # in an exception handler (it would require a specific exception type). @@ -36,7 +31,17 @@ ) # Socket.io -sio_app = socketio.ASGIApp(socketio_router.sio, cors_app) +if settings.enable_socketio: + sio_app = socketio.ASGIApp(socketio_router.sio, cors_app) + main_app = sio_app +else: + main_app = cors_app + + +# Routers +app.include_router(xlwings_router) +app.include_router(macros_router) +app.include_router(taskpane_router) # Security headers diff --git a/app/static/js/socketio-handlers.js b/app/static/js/socketio-handlers.js index 9833242..33353b4 100644 --- a/app/static/js/socketio-handlers.js +++ b/app/static/js/socketio-handlers.js @@ -9,6 +9,6 @@ try { }, }); } catch (error) { - console.error(error); + console.log("Didn't load socket.io: ", error); globalThis.socket = null; } diff --git a/app/templates/base.html b/app/templates/base.html index 4895a25..152edf0 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -22,7 +22,9 @@ {% endif %} {# Socket.io (must come before xlwings.js) #} - + {% if settings.enable_socketio %} + + {% endif %} {# xlwings.js (must come before custom-function-code) #} {# Examples #} diff --git a/docker-compose.prod.yaml b/docker-compose.prod.yaml index c5ad2b3..0d865fd 100644 --- a/docker-compose.prod.yaml +++ b/docker-compose.prod.yaml @@ -3,7 +3,7 @@ services: app: build: . command: > - gunicorn app.main:sio_app + gunicorn app.main:main_app --bind 0.0.0.0:8000 --access-logfile - --workers 1 diff --git a/docker-compose.yaml b/docker-compose.yaml index c22c066..0f38bec 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -3,7 +3,7 @@ services: app: build: . command: > - uvicorn app.main:sio_app + uvicorn app.main:main_app --host 0.0.0.0 --port 8000 --ssl-keyfile /project/certs/localhost+2-key.pem --ssl-certfile /project/certs/localhost+2.pem --reload diff --git a/function_app.py b/function_app.py new file mode 100644 index 0000000..864fe48 --- /dev/null +++ b/function_app.py @@ -0,0 +1,21 @@ +""" +Azure Functions + +Note: Azure Functions don't support streaming functions/socket.io. + +The other files required for Azure Functions are: +- host.json +- local.settings.json + +The function is always called http_app_func, see: +https://github.com/Azure-Samples/fastapi-on-azure-functions/issues/31 + +For app logs, in Azure portal go to: +Function App > My Function App. Than, under `http_app_func`, click on `Invocations and more`. +""" + +import azure.functions as func + +from app.main import main_app + +app = func.AsgiFunctionApp(app=main_app, http_auth_level=func.AuthLevel.ANONYMOUS) diff --git a/host.json b/host.json new file mode 100644 index 0000000..a3a3a7e --- /dev/null +++ b/host.json @@ -0,0 +1,22 @@ +{ + "version": "2.0", + "logging": { + "applicationInsights": { + "samplingSettings": { + "isEnabled": true, + "excludedTypes": "Request" + } + } + }, + "extensionBundle": { + "id": "Microsoft.Azure.Functions.ExtensionBundle", + "version": "[2.*, 3.0.0)" + }, + "extensions": + { + "http": + { + "routePrefix": "" + } + } +} diff --git a/local.settings.json b/local.settings.json new file mode 100644 index 0000000..fb158d7 --- /dev/null +++ b/local.settings.json @@ -0,0 +1,8 @@ +{ + "IsEncrypted": false, + "Values": { + "AzureWebJobsStorage": "", + "FUNCTIONS_WORKER_RUNTIME": "python", + "AzureWebJobsFeatureFlags": "EnableWorkerIndexing" + } +} diff --git a/requirements.in b/requirements.in index d00f903..a27b5c5 100644 --- a/requirements.in +++ b/requirements.in @@ -1,3 +1,4 @@ +azure-functions; sys_platform != 'win32' cachetools fastapi gunicorn; sys_platform != 'win32' diff --git a/requirements.txt b/requirements.txt index 846ed3e..ec33ecb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ anyio==4.3.0 # httpx # starlette # watchfiles +azure-functions==1.19.0 bidict==0.23.1 # via python-socketio cachetools==5.3.3 diff --git a/run.py b/run.py index 2fcb4d6..1654fe5 100644 --- a/run.py +++ b/run.py @@ -6,7 +6,7 @@ if __name__ == "__main__": uvicorn.run( - "app.main:sio_app", + "app.main:main_app", host="127.0.0.1", port=8000, reload=True,