Files
v2x-server/Makefile
T
gnickensandClaude Opus 5 0526d34e42 Scaffold FastAPI + MySQL + Keycloak service with devcontainer
Sets up the project skeleton:

- FastAPI app factory with lifespan, request-id middleware, and RFC 9457
  problem+json error handlers
- Async SQLAlchemy 2.0 over MySQL (asyncmy), with a constraint naming
  convention in place before the first migration and async Alembic
- Keycloak as a pure resource server: OIDC discovery, cached JWKS with
  rotation-aware refresh, and require_roles dependencies
- Devcontainer running MySQL 8.4 and Keycloak 26.7 as compose siblings,
  with the realm (clients, roles, test users) imported on first boot
- Test suite covering the endpoints plus the token validator itself,
  exercised against a locally generated RSA keypair
- uv packaging, ruff, mypy --strict, pre-commit, Gitea CI, prod Dockerfile

Two Keycloak-in-containers traps are handled explicitly and documented in
the README: the issuer/internal-URL split (the browser sees localhost:8080,
the API sees keycloak:8080) and the audience mapper that stops Keycloak
issuing tokens with aud=account.

The devices resource is a placeholder proving the routing -> auth -> ORM ->
migration path end to end; replace it with the real domain.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-10 12:46:52 -04:00

61 lines
1.8 KiB
Makefile

.DEFAULT_GOAL := help
.PHONY: help install dev migrate revision downgrade test lint fmt typecheck check shell db up down reset kc-export
help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) \
| awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-12s\033[0m %s\n", $$1, $$2}'
install: ## Sync the virtualenv from uv.lock
uv sync --frozen
dev: ## Run the API with autoreload on :8000
uv run uvicorn v2x_server.main:app --host 0.0.0.0 --port 8000 --reload
migrate: ## Apply migrations up to head
uv run alembic upgrade head
revision: ## Autogenerate a migration: make revision m="add widgets"
@test -n "$(m)" || (echo 'Usage: make revision m="description"' && exit 1)
uv run alembic revision --autogenerate -m "$(m)"
downgrade: ## Roll back one migration
uv run alembic downgrade -1
test: ## Run the test suite
uv run pytest
lint: ## Lint (no changes written)
uv run ruff check .
uv run ruff format --check .
fmt: ## Format and autofix
uv run ruff check --fix .
uv run ruff format .
typecheck: ## Static type check
uv run mypy
check: lint typecheck test ## Everything CI runs
shell: ## Python REPL with the app importable
uv run python
db: ## Open a MySQL shell on the dev database
mysql -h mysql -u v2x -pv2xpassword v2x
up: ## Start the backing services (outside the devcontainer)
docker compose up -d
down: ## Stop the backing services
docker compose down
reset: ## Destroy all data and start clean
docker compose down -v
docker compose up -d
kc-export: ## Re-export the realm after editing it in the Keycloak console
docker compose exec keycloak /opt/keycloak/bin/kc.sh export \
--realm v2x --file /tmp/realm-v2x.json --users realm_file
docker compose cp keycloak:/tmp/realm-v2x.json ./docker/keycloak/realm-v2x.json
@echo "Exported. Review the diff before committing -- exports include volatile fields."