Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ COPY . .
EXPOSE 8000

# Default command — overridden by worker/beat in docker-compose
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
CMD ["sh", "-c", "uvicorn app.main:app --host 0.0.0.0 --port ${PORT:-8000}"]
20 changes: 20 additions & 0 deletions backend/app/core/config.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
"""Application configuration loaded from environment variables."""

from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit

from pydantic_settings import BaseSettings
from pydantic import model_validator
from functools import lru_cache


def _set_ssl_parameter(url: str, parameter: str) -> str:
"""Require TLS without duplicating driver-specific connection options."""
parts = urlsplit(url)
query = dict(parse_qsl(parts.query, keep_blank_values=True))
query.pop("ssl", None)
query.pop("sslmode", None)
query[parameter] = "require"
return urlunsplit(parts._replace(query=urlencode(query)))


class Settings(BaseSettings):
# ── App ───────────────────────────────────────────────────
APP_NAME: str = "ORBITA-ATSAD"
Expand Down Expand Up @@ -67,6 +79,14 @@ def normalise_settings(self) -> "Settings":
).replace(
"postgresql://", "postgresql+psycopg2://", 1
)

# Render and many managed PostgreSQL providers reject non-TLS clients.
# asyncpg and psycopg2 use different names for the same requirement.
if self.ENVIRONMENT.lower() == "production":
self.DATABASE_URL = _set_ssl_parameter(self.DATABASE_URL, "ssl")
self.DATABASE_URL_SYNC = _set_ssl_parameter(
self.DATABASE_URL_SYNC, "sslmode"
)
return self

@property
Expand Down
14 changes: 7 additions & 7 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# ── Web Framework ─────────────────────────────────────────────
fastapi==0.136.0
uvicorn[standard]==0.34.0
python-multipart==0.0.22
python-multipart==0.0.27

# ── Database ──────────────────────────────────────────────────
sqlalchemy[asyncio]==2.0.36
Expand Down Expand Up @@ -31,9 +31,9 @@ jplephem==2.22

# ── HTTP Client (for external data sources) ──────────────────
httpx==0.28.1
urllib3==2.6.3
urllib3==2.7.0
requests==2.33.1
aiohttp==3.13.5
aiohttp==3.14.0

# ── Real-Time & Background Workers (Phase 2) ──────────────────
celery[redis]==5.4.0
Expand All @@ -42,22 +42,22 @@ websockets==14.2

# ── AI Agents (Phase 3) ───────────────────────────────────────
langchain==1.2.15
langchain-core==1.3.0
langchain-core==1.3.3
langchain-openai==1.1.16
langchain-anthropic==1.4.1
nltk==3.9.4

# ── Utilities ─────────────────────────────────────────────────
python-dateutil==2.9.0.post0
orjson==3.11.6
ujson==5.11.0
ujson==5.12.1
filelock==3.29.0
werkzeug==3.1.8
pillow==12.1.1
pillow==12.2.0
matplotlib==3.10.0
pygments==2.20.0
seaborn==0.13.2
reportlab==4.3.1
# ── Machine Learning ──────────────────────────────────────────
torch==2.9.0
torch==2.12.1
pandas==2.2.3
27 changes: 27 additions & 0 deletions backend/tests/test_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from app.core.config import Settings


def test_production_database_urls_require_driver_specific_tls() -> None:
settings = Settings(
ENVIRONMENT="production",
DATABASE_URL="postgresql://user:pass@example.com/db?sslmode=require",
DATABASE_URL_SYNC="postgresql://user:pass@example.com/db?ssl=require",
)

assert settings.DATABASE_URL == (
"postgresql+asyncpg://user:pass@example.com/db?ssl=require"
)
assert settings.DATABASE_URL_SYNC == (
"postgresql+psycopg2://user:pass@example.com/db?sslmode=require"
)


def test_development_database_urls_do_not_force_tls() -> None:
settings = Settings(
ENVIRONMENT="development",
DATABASE_URL="postgresql://user:pass@localhost/db",
DATABASE_URL_SYNC="postgresql://user:pass@localhost/db",
)

assert settings.DATABASE_URL.endswith("@localhost/db")
assert settings.DATABASE_URL_SYNC.endswith("@localhost/db")
Loading
Loading