feat(FN-002): complete Step 3 — FastAPI Backend App Skeleton
This commit is contained in:
@@ -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()
|
||||||
@@ -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}
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
app
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user