improve project structure for api

This commit is contained in:
lilly 2026-05-03 20:57:13 +02:00
commit 7fa9867a38
Signed by: lilly
SSH key fingerprint: SHA256:y9T5GFw2A20WVklhetIxG1+kcg/Ce0shnQmbu1LQ37g
7 changed files with 42 additions and 11 deletions

9
.envrc.dist Normal file
View file

@ -0,0 +1,9 @@
# integrate this into your own .envrc file with `source_env .envrc.dist`
watch_file api/pyproject.toml \
api/uv.lock
export UV_PROJECT=$(realpath ./api)
uv venv --allow-existing
uv sync

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
.envrc
.direnv/
**/__pycache__
api/dist

View file

@ -9,3 +9,13 @@ Project structure:
├── api # Python application interacting with HomeMatic and providing the API.
└── app # Web UI
```
## API Development
Most things should automatically be set up with the included [direnv script](./.envrc.dist) but if you don't use that, take the following steps:
1. Go to the api/ directory
2. Run `uv venv` to create a python virtual environment
3. Install all dependencies of the dooris project into that virtual environment with `uv sync`
4. Run a development server `uv run uvicorn dooris_api:app --reload`

View file

@ -8,3 +8,12 @@ dependencies = [
"simple-openid-connect>=2.4.0",
"uvicorn>=0.46.0",
]
classifiers = ["Private :: Do Not Upload"]
[project.scripts]
"dooris-api" = "dooris_api:main"
[build-system]
requires = ["flit_core >=3.11,<5"]
build-backend = "flit_core.buildapi"

View file

@ -0,0 +1,8 @@
from dooris_api.app import app
def main():
import uvicorn
config = uvicorn.Config(app, port=8000, log_level="info")
server = uvicorn.Server(config)
server.run()

55
api/src/dooris_api/app.py Normal file
View file

@ -0,0 +1,55 @@
#!/usr/bin/env python3
from typing import Optional
from fastapi import FastAPI, Request, Response
from fastapi.responses import RedirectResponse
from pydantic import BaseModel
from contextlib import asynccontextmanager
from simple_openid_connect.client import OpenidClient
from simple_openid_connect.data import TokenSuccessResponse, IdToken
@asynccontextmanager
async def lifespan(app: FastAPI):
app.extra["oidc_client"] = OpenidClient.from_issuer_url(
url="https://id.hamburg.ccc.de/realms/test/",
authentication_redirect_uri="http://localhost:8000/auth/login-callback",
client_id="dooris",
client_secret="dp9HhnvUhAtKm3pRnxfGA7q8Nwrd1td8",
)
yield
app = FastAPI(lifespan=lifespan)
class UserInfo(BaseModel):
name: str
class UserStatus(BaseModel):
is_logged_in: bool
user_info: Optional[UserInfo]
@app.get("/api/user-info/")
async def get_user_info() -> UserStatus:
return { "bla": "blub" }
@app.get("/auth/login")
async def login_init(req: Request):
oidc_client = req.app.extra["oidc_client"] # type: OpenidClient
return RedirectResponse(oidc_client.authorization_code_flow.start_authentication())
@app.get("/auth/login-callback")
async def login_callback(req: Request, resp: Response):
oidc_client = req.app.extra["oidc_client"] # type: OpenidClient
auth_result = oidc_client.authorization_code_flow.handle_authentication_result(current_url=req.url)
if isinstance(auth_result, TokenSuccessResponse):
resp.set_cookie("access_token", auth_result.access_token, httponly=True)
resp.set_cookie("id_token", auth_result.id_token)
return {"authenticated": True}
else:
return {"authenticated": False}

2
api/uv.lock generated
View file

@ -233,7 +233,7 @@ wheels = [
[[package]]
name = "dooris-api"
version = "0.1.0"
source = { virtual = "." }
source = { editable = "." }
dependencies = [
{ name = "fastapi" },
{ name = "simple-openid-connect" },