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