-
-
Notifications
You must be signed in to change notification settings - Fork 342
Closed
Description
I am not exactly sure why this is happening, but for some reason the endpoint /docs always redirects to the docs specified in default docker-images/app/main.py file π€·ββοΈ.
All other endpoints are working fine (serviced by my FastAPI instance) - including /.
My project structure is shown below.
.
βββ Dockerfile
βββ docker-compose.yml
βββ docker-compose.traefik.yml
βββ requirements.txt
βββ .env
βββ app/
βββ api/
βββ __init__.py
βββ main.py
Here is my main.py file
from api.settings import APP_SECRET_KEY, HASHED_DOCS_PASSWORD
import hashlib
import secrets
from fastapi import FastAPI, Depends, HTTPException, status, APIRouter
from fastapi.openapi.docs import get_swagger_ui_html
from fastapi.openapi.utils import get_openapi
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from starlette.middleware.sessions import SessionMiddleware
from api.database import db
from api.dependencies import router as token_router
from api.routes import auth, bot, campaign, client, contact, sequence
# Create an instance of HTTPBasic, which will be used for basic authentication of the docs
security = HTTPBasic()
app = FastAPI(
docs_url=None,
redoc_url=None,
openapi_url=None,
title="Chase API",
version="MVP",
)
app.add_middleware(SessionMiddleware, secret_key=str(APP_SECRET_KEY))
def authenticate_docs_access(credentials: HTTPBasicCredentials = Depends(security)) -> bool:
"""
Authenticates access to the FastAPI documentation.
Args:
credentials (HTTPBasicCredentials): The HTTP Basic credentials.
Returns:
bool: True if the username is "admin" and the hashed password matches the one saved in the
environment file, otherwise an HTTP 401 (unauthorized) response is sent.
"""
correct_username = secrets.compare_digest(credentials.username, "admin")
correct_password = secrets.compare_digest(
hashlib.sha256(credentials.password.encode()
).hexdigest(), str(HASHED_DOCS_PASSWORD)
)
if not (correct_username and correct_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password.",
headers={"WWW-Authenticate": "Basic"},
)
return True
@app.get("/docs")
async def get_documentation(auth: bool = Depends(authenticate_docs_access)):
"""
Provides access to the Swagger-based API documentation UI.
### Headers
- `Authorization`: Basic authentication credentials to verify access to documentation.
### Query Parameters
- `auth` (`bool`): Indicates if the user has authenticated access to the documentation.
### Notes
- Access to the Swagger UI is conditional upon successful basic authentication.
"""
return get_swagger_ui_html(openapi_url="/openapi.json", title="docs")
@app.get("/openapi.json")
async def openapi(auth: bool = Depends(authenticate_docs_access)):
"""
Serves the OpenAPI schema for the FastAPI application.
### Headers
- `Authorization`: Basic authentication credentials required for access.
### Query Parameters
- `auth` (`bool`): Determines if the user has permission to view the OpenAPI schema.
"""
return get_openapi(title=app.title, version=app.version, routes=app.routes)
@app.on_event("startup")
async def startup():
"""
Event handler that connects to the database on app startup.
"""
await db.connect()
@app.on_event("shutdown")
async def shutdown():
"""
Event handler that disconnects from the database on app shutdown.
"""
await db.disconnect()
# Include the routers for each endpoint
app.include_router(token_router)
app.include_router(auth.router)
app.include_router(bot.router)
app.include_router(campaign.router)
app.include_router(client.router)
app.include_router(contact.router)
app.include_router(sequence.router)
And below is my docker-compose.yml
I have specified a MODULE_NAME and VARIABLE_NAME to reflect my file structure.
version: "3"
services:
web:
environment:
- MODULE_NAME=api.main
- MAX_WORKERS=2
- VARIABLE_NAME=app
build: ./
restart: always
labels:
# === TLS ===
# Enable Traefik for this specific "backend" service
- traefik.enable=true
# Define the port inside of the Docker service to use
- traefik.http.services.app.loadbalancer.server.port=80
# Make Traefik use this domain in HTTP
- traefik.http.routers.app-http.entrypoints=http
- traefik.http.routers.app-http.rule=Host(`localhost`)
# Use the traefik-public network (declared below)
- traefik.docker.network=traefik-public
# Make Traefik use this domain in HTTPS
- traefik.http.routers.app-https.entrypoints=https
- traefik.http.routers.app-https.rule=Host(`localhost`)
- traefik.http.routers.app-https.tls=true
# Use the "le" (Let's Encrypt) resolver
- traefik.http.routers.app-https.tls.certresolver=le
# https-redirect middleware to redirect HTTP to HTTPS
- traefik.http.middlewares.https-redirect.redirectscheme.scheme=https
- traefik.http.middlewares.https-redirect.redirectscheme.permanent=true
# Middleware to redirect HTTP to HTTPS
- traefik.http.routers.app-http.middlewares=https-redirect
networks:
# Use the public network created to be shared between Traefik and
# any other service that needs to be publically available with HTTPS
- traefik-public
extra_hosts:
# Allows a direct connection from inside a docker container to the local
# machine on Linux based systems.
- "host.docker.internal:host-gateway"
networks:
traefik-public:
external: true
Perhaps even stranger, the docs ARE protected π€― as in they use it is protected behind the HTTPBasicCredentials I have specified.
No sure how to resolve this.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
