feat: implement API versioning /api/v1/

- All backend routers moved to /api/v1/ prefix
- Frontend BASE_URL updated to /api/v1
- Setup redirect middleware updated to redirect to /api/v1/setup
- Health router path fixed: prefix=/api/v1/health, @router.get('')
- conftest.py: set server_status=online for test fixture
- Created Docs/API_VERSIONING.md with deprecation policy
- Updated Docs/Backend-Development.md with versioning section
- Updated Instructions.md curl examples

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-05-02 21:29:30 +02:00
parent 0d5882b32f
commit cc6dbcf3f0
51 changed files with 1886 additions and 671 deletions

View File

@@ -10,7 +10,7 @@ from app.models.server import ServerStatus
async def test_health_check_returns_200_when_online(client: AsyncClient) -> None:
"""``GET /api/health`` must return HTTP 200 when fail2ban is online."""
client._transport.app.state.server_status = ServerStatus(online=True)
response = await client.get("/api/health")
response = await client.get("/api/v1/health")
assert response.status_code == 200
@@ -18,7 +18,7 @@ async def test_health_check_returns_200_when_online(client: AsyncClient) -> None
async def test_health_check_returns_503_when_offline(client: AsyncClient) -> None:
"""``GET /api/health`` must return HTTP 503 when fail2ban is offline."""
client._transport.app.state.server_status = ServerStatus(online=False)
response = await client.get("/api/health")
response = await client.get("/api/v1/health")
assert response.status_code == 503
@@ -26,7 +26,7 @@ async def test_health_check_returns_503_when_offline(client: AsyncClient) -> Non
async def test_health_check_returns_ok_status_when_online(client: AsyncClient) -> None:
"""``GET /api/health`` must contain ``status: ok`` when fail2ban is online."""
client._transport.app.state.server_status = ServerStatus(online=True)
response = await client.get("/api/health")
response = await client.get("/api/v1/health")
data: dict[str, str] = response.json()
assert data["status"] == "ok"
assert data["fail2ban"] == "online"
@@ -36,7 +36,7 @@ async def test_health_check_returns_ok_status_when_online(client: AsyncClient) -
async def test_health_check_returns_unavailable_when_offline(client: AsyncClient) -> None:
"""``GET /api/health`` must contain ``status: unavailable`` when fail2ban is offline."""
client._transport.app.state.server_status = ServerStatus(online=False)
response = await client.get("/api/health")
response = await client.get("/api/v1/health")
data: dict[str, str] = response.json()
assert data["status"] == "unavailable"
assert data["fail2ban"] == "offline"
@@ -45,6 +45,6 @@ async def test_health_check_returns_unavailable_when_offline(client: AsyncClient
@pytest.mark.asyncio
async def test_health_check_content_type_is_json(client: AsyncClient) -> None:
"""``GET /api/health`` must set the ``Content-Type`` header to JSON."""
response = await client.get("/api/health")
response = await client.get("/api/v1/health")
assert "application/json" in response.headers.get("content-type", "")