From 80f04a5c451f1d58fb683191a3685058d0af6dab Mon Sep 17 00:00:00 2001 From: Fusion Date: Thu, 14 May 2026 01:31:58 +0200 Subject: [PATCH] =?UTF-8?q?feat(FN-002):=20complete=20Step=203=20=E2=80=94?= =?UTF-8?q?=20FastAPI=20Backend=20App=20Skeleton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/app/__init__.py | 0 apps/api/app/config.py | 28 ++++++++++++++ apps/api/app/main.py | 22 +++++++++++ apps/api/headquarter_api.egg-info/PKG-INFO | 13 +++++++ apps/api/headquarter_api.egg-info/SOURCES.txt | 10 +++++ .../dependency_links.txt | 1 + .../api/headquarter_api.egg-info/requires.txt | 9 +++++ .../headquarter_api.egg-info/top_level.txt | 1 + apps/api/package.json | 12 ++++++ apps/api/pyproject.toml | 38 +++++++++++++++++++ apps/api/tests/__init__.py | 0 apps/api/tests/test_health.py | 14 +++++++ 12 files changed, 148 insertions(+) create mode 100644 apps/api/app/__init__.py create mode 100644 apps/api/app/config.py create mode 100644 apps/api/app/main.py create mode 100644 apps/api/headquarter_api.egg-info/PKG-INFO create mode 100644 apps/api/headquarter_api.egg-info/SOURCES.txt create mode 100644 apps/api/headquarter_api.egg-info/dependency_links.txt create mode 100644 apps/api/headquarter_api.egg-info/requires.txt create mode 100644 apps/api/headquarter_api.egg-info/top_level.txt create mode 100644 apps/api/package.json create mode 100644 apps/api/pyproject.toml create mode 100644 apps/api/tests/__init__.py create mode 100644 apps/api/tests/test_health.py diff --git a/apps/api/app/__init__.py b/apps/api/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/api/app/config.py b/apps/api/app/config.py new file mode 100644 index 0000000..02afc3c --- /dev/null +++ b/apps/api/app/config.py @@ -0,0 +1,28 @@ +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class Settings(BaseSettings): + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + extra="ignore", + ) + + app_name: str = "Headquarter API" + debug: bool = False + api_v1_prefix: str = "/api/v1" + + # Authentik OIDC placeholders (to be wired in FN-004) + authentik_issuer_url: str = "" + authentik_client_id: str = "" + authentik_client_secret: str = "" + + # Database (to be wired in FN-004) + database_url: str = "postgresql://postgres:postgres@localhost:5432/headquarter" + + # Deployment + root_domain: str = "localhost" + tool_subdomain_pattern: str = "{tool}-{project}-{user}.tools.{root_domain}" + + +settings = Settings() diff --git a/apps/api/app/main.py b/apps/api/app/main.py new file mode 100644 index 0000000..ae550c4 --- /dev/null +++ b/apps/api/app/main.py @@ -0,0 +1,22 @@ +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware + +from app.config import settings + +app = FastAPI( + title=settings.app_name, + debug=settings.debug, +) + +app.add_middleware( + CORSMiddleware, + allow_origins=["http://localhost:5173"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +@app.get("/health") +def health() -> dict[str, str]: + return {"status": "ok", "service": settings.app_name} diff --git a/apps/api/headquarter_api.egg-info/PKG-INFO b/apps/api/headquarter_api.egg-info/PKG-INFO new file mode 100644 index 0000000..ff539f4 --- /dev/null +++ b/apps/api/headquarter_api.egg-info/PKG-INFO @@ -0,0 +1,13 @@ +Metadata-Version: 2.4 +Name: headquarter-api +Version: 0.0.1 +Summary: Headquarter FastAPI backend +Requires-Python: >=3.11 +Requires-Dist: fastapi>=0.115.0 +Requires-Dist: uvicorn[standard]>=0.34.0 +Requires-Dist: pydantic-settings>=2.8.0 +Provides-Extra: dev +Requires-Dist: pytest>=8.3.0; extra == "dev" +Requires-Dist: httpx>=0.28.0; extra == "dev" +Requires-Dist: ruff>=0.11.0; extra == "dev" +Requires-Dist: mypy>=1.15.0; extra == "dev" diff --git a/apps/api/headquarter_api.egg-info/SOURCES.txt b/apps/api/headquarter_api.egg-info/SOURCES.txt new file mode 100644 index 0000000..7591d30 --- /dev/null +++ b/apps/api/headquarter_api.egg-info/SOURCES.txt @@ -0,0 +1,10 @@ +pyproject.toml +app/__init__.py +app/config.py +app/main.py +headquarter_api.egg-info/PKG-INFO +headquarter_api.egg-info/SOURCES.txt +headquarter_api.egg-info/dependency_links.txt +headquarter_api.egg-info/requires.txt +headquarter_api.egg-info/top_level.txt +tests/test_health.py \ No newline at end of file diff --git a/apps/api/headquarter_api.egg-info/dependency_links.txt b/apps/api/headquarter_api.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/apps/api/headquarter_api.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/apps/api/headquarter_api.egg-info/requires.txt b/apps/api/headquarter_api.egg-info/requires.txt new file mode 100644 index 0000000..723d1de --- /dev/null +++ b/apps/api/headquarter_api.egg-info/requires.txt @@ -0,0 +1,9 @@ +fastapi>=0.115.0 +uvicorn[standard]>=0.34.0 +pydantic-settings>=2.8.0 + +[dev] +pytest>=8.3.0 +httpx>=0.28.0 +ruff>=0.11.0 +mypy>=1.15.0 diff --git a/apps/api/headquarter_api.egg-info/top_level.txt b/apps/api/headquarter_api.egg-info/top_level.txt new file mode 100644 index 0000000..b80f0bd --- /dev/null +++ b/apps/api/headquarter_api.egg-info/top_level.txt @@ -0,0 +1 @@ +app diff --git a/apps/api/package.json b/apps/api/package.json new file mode 100644 index 0000000..d9d80da --- /dev/null +++ b/apps/api/package.json @@ -0,0 +1,12 @@ +{ + "name": "@headquarter/api", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": ".venv/bin/uvicorn app.main:app --reload --port 8000", + "build": "echo 'Python build step skipped (no compilation required)'", + "lint": ".venv/bin/ruff check app tests", + "test": ".venv/bin/pytest", + "typecheck": ".venv/bin/mypy app tests" + } +} diff --git a/apps/api/pyproject.toml b/apps/api/pyproject.toml new file mode 100644 index 0000000..a19d610 --- /dev/null +++ b/apps/api/pyproject.toml @@ -0,0 +1,38 @@ +[project] +name = "headquarter-api" +version = "0.0.1" +description = "Headquarter FastAPI backend" +requires-python = ">=3.11" +dependencies = [ + "fastapi>=0.115.0", + "uvicorn[standard]>=0.34.0", + "pydantic-settings>=2.8.0", +] + +[project.optional-dependencies] +dev = [ + "pytest>=8.3.0", + "httpx>=0.28.0", + "ruff>=0.11.0", + "mypy>=1.15.0", +] + +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[tool.setuptools] +packages = ["app"] + +[tool.ruff] +line-length = 100 +target-version = "py311" + +[tool.ruff.lint] +select = ["E", "F", "I", "N", "W", "UP"] + +[tool.mypy] +python_version = "3.11" +strict = true +warn_return_any = true +warn_unused_configs = true diff --git a/apps/api/tests/__init__.py b/apps/api/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/api/tests/test_health.py b/apps/api/tests/test_health.py new file mode 100644 index 0000000..966e62c --- /dev/null +++ b/apps/api/tests/test_health.py @@ -0,0 +1,14 @@ +from fastapi.testclient import TestClient + +from app.config import settings +from app.main import app + +client = TestClient(app) + + +def test_health_returns_ok() -> None: + response = client.get("/health") + assert response.status_code == 200 + data = response.json() + assert data["status"] == "ok" + assert data["service"] == settings.app_name