Dns validation for https (#49)

* Add DNS-01 challenge support for Let's Encrypt

Co-authored-by: mathieumoran1 <[email protected]>

* Refactor letsencrypt script to use /dev/tty for interactive prompts

Co-authored-by: mathieumoran1 <[email protected]>

* feat: Add optional DNS hook mode for certonly

Co-authored-by: mathieumoran1 <[email protected]>

* Refactor: Improve MongoDB connection and add certifi

Co-authored-by: mathieumoran1 <[email protected]>

* Checkpoint before follow-up message

Co-authored-by: mathieumoran1 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
This commit was merged in pull request #49.
This commit is contained in:
Mathieu Moran
2025-12-22 04:16:45 -05:00
committed by GitHub
co-authored by Cursor Agent
parent 812590d262
commit 5325576dc4
6 changed files with 315 additions and 26 deletions
+83 -8
View File
@@ -1,6 +1,8 @@
import mimetypes
import os
import posixpath
import ssl
from functools import lru_cache
from datetime import datetime, timezone
from pathlib import Path
from typing import Generator, Optional, Tuple
@@ -13,6 +15,7 @@ from fastapi import FastAPI, Header, HTTPException, Request, Response
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, StreamingResponse
from pymongo import MongoClient
from pymongo.errors import PyMongoError
def _env_str(name: str, default: str = "") -> str:
@@ -49,6 +52,12 @@ def _mongo_db_name() -> str:
except Exception:
return ""
def _env_int(name: str, default: int) -> int:
try:
return int(_env_str(name, str(default)))
except Exception:
return default
def _video_files_dir() -> Path:
# Read env at runtime (important on Windows/uvicorn worker reloads).
@@ -143,7 +152,51 @@ def _mongo_client() -> MongoClient:
uri = _mongo_uri()
if not uri:
raise HTTPException(status_code=500, detail="MONGO_URI is not configured")
return MongoClient(uri)
kwargs = {
# Fail fast instead of hanging for ~30s on each request.
"serverSelectionTimeoutMS": _env_int("MONGO_SERVER_SELECTION_TIMEOUT_MS", 8000),
"connectTimeoutMS": _env_int("MONGO_CONNECT_TIMEOUT_MS", 8000),
"socketTimeoutMS": _env_int("MONGO_SOCKET_TIMEOUT_MS", 20000),
}
# On some hosts, the system CA store is missing/broken or overridden by env vars,
# causing TLS handshake failures to MongoDB Atlas. Prefer certifi's CA bundle.
ca_file = _env_str("MONGO_TLS_CA_FILE")
if ca_file:
kwargs["tlsCAFile"] = ca_file
else:
try:
import certifi # type: ignore
kwargs["tlsCAFile"] = certifi.where()
except Exception:
# If certifi isn't available, fall back to system trust.
pass
# Some hosts / middleboxes break TLS 1.3 handshakes to Atlas and produce
# `TLSV1_ALERT_INTERNAL_ERROR`. Forcing TLS 1.2 can fix it.
if _env_str("MONGO_TLS_FORCE_TLS12", "0") in ("1", "true", "yes", "on"):
cafile = kwargs.pop("tlsCAFile", None)
ctx = ssl.create_default_context(cafile=cafile)
try:
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
ctx.maximum_version = ssl.TLSVersion.TLSv1_2
except Exception:
# Older Python/OpenSSL may not support TLSVersion fields.
pass
kwargs["ssl_context"] = ctx
# Optional escape hatch (NOT recommended): allow invalid certs for debugging only.
if _env_str("MONGO_TLS_INSECURE", "0") in ("1", "true", "yes", "on"):
kwargs["tlsAllowInvalidCertificates"] = True
return MongoClient(uri, **kwargs)
@lru_cache(maxsize=1)
def _mongo_client_cached() -> MongoClient:
# Reuse one client (connection pool) across requests.
return _mongo_client()
app = FastAPI()
@@ -211,15 +264,26 @@ def stream_video(
except Exception:
raise HTTPException(status_code=400, detail="Invalid movie id")
with _mongo_client() as client:
db_name = _mongo_db_name()
if not db_name:
raise HTTPException(
status_code=500,
detail="Mongo DB name not configured. Add it to MONGO_URI (…/dbname) or set MONGO_DB_NAME.",
)
db_name = _mongo_db_name()
if not db_name:
raise HTTPException(
status_code=500,
detail="Mongo DB name not configured. Add it to MONGO_URI (…/dbname) or set MONGO_DB_NAME.",
)
try:
client = _mongo_client_cached()
db = client[db_name]
movie = db["movies"].find_one({"_id": oid}, {"video_file": 1})
except PyMongoError as e:
raise HTTPException(
status_code=502,
detail=(
"Failed to query MongoDB (TLS/connection issue). "
"On some hosts you may need a CA bundle; try installing certifi or set MONGO_TLS_CA_FILE. "
f"Original error: {type(e).__name__}: {e}"
),
)
video_file = (movie or {}).get("video_file") or ""
if not isinstance(video_file, str) or not video_file.strip():
@@ -287,5 +351,16 @@ def healthz():
"time": datetime.now(timezone.utc).isoformat(),
"video_files_dir": str(_video_files_dir()),
"mongo_db_name": _mongo_db_name() or None,
"openssl": ssl.OPENSSL_VERSION,
}
@app.on_event("shutdown")
def _shutdown():
# Close the cached Mongo client cleanly.
try:
client = _mongo_client_cached()
client.close()
except Exception:
pass