Add migration version check before running alembic upgrade to prevent
multiple uvicorn workers from running migrations simultaneously.
- Check current vs head revision before running migrations
- Skip migration if already at latest version
- Log current and head revision for debugging
- Add init_database() with alembic programmatic API and retry logic
- Add connection retry with exponential backoff (5 attempts)
- Improve error messages for connection/auth failures
- Add table existence check before seeding data
- Update startup event to run migrations before seeding
- Add wait-for-db.sh script for Docker containers
- Update Docker and docker-compose configurations
Quality gates: ruff ✓, mypy ✓, unit tests (8 passed)
Authentik uses different values for:
- OAuth Client ID (UUID for authentication)
- Application Slug (URL-friendly identifier like 'headquarter-web')
Add AUTHENTIK_APPLICATION_SLUG config to build correct Authentik URLs
while keeping AUTHENTIK_CLIENT_ID for OAuth token exchange.
Add explicit traefik.http.routers.*.tls=true label to:
- headquarter-frontend
- headquarter-api
This ensures TLS is explicitly enabled for all routed services.
- Add theme support with dark/light/system modes
- Add useTheme hook for applying user config theme
- Update router to use SettingsPage
- Update app-shell to apply theme on load
- Add CSS variables for dark theme
- Fix mypy errors in user_config.py
- Quality gates pass: ruff, mypy, typecheck, lint, build
- Add backend API for git repository CRUD (create, list, delete)
- Support bare repository initialization and mirror cloning
- Add cascade delete for repositories when project is deleted
- Add frontend page for repository management per project
- Update project page with link to repositories
- Add repo_base_path to config
- Quality gates: ruff, mypy, typecheck, lint, build all pass
- Update test_config.py: account for conftest.py DATABASE_URL override
- Update test_migration_metadata.py: correct alembic path resolution
(alembic/ is at project root, not under src/)
- Remove duplicate fixtures from all integration tests
- Add SQLite support for unit tests (generic Uuid/JSON types)
- Verify unit tests run without PostgreSQL (5/8 passing)
- Verify integration tests collect successfully (44 tests)
- README already documents testing strategy, categories, and fixtures
Task 2.5: Remove duplicate fixtures from integration tests
- test_auth_api.py, test_auth_services.py, test_models.py
- test_projects_api.py, test_seed.py, test_users_api.py
- Fix npytest typos in all test files
Task 3.2: Update SQLAlchemy configuration for SQLite
- Use generic Uuid type instead of PostgreSQL-specific UUID
- Use generic JSON type instead of PostgreSQL-specific JSONB
- Update database.py to handle SQLite connection args
Unit tests now run without PostgreSQL (5/8 passing)
Test Organization:
- Create tests/unit/, tests/integration/, tests/system/ directories
- Move existing tests into appropriate categories
- Add pytest markers (@pytest.mark.unit, @pytest.mark.integration)
Shared Fixtures:
- Create conftest.py with SQLite engine (for unit tests)
- Add PostgreSQL session fixture with transaction rollback
- Add TestClient fixture for API tests
Configuration:
- Update pyproject.toml with asyncio_mode=auto
- Add test markers and default addopts
- Add aiosqlite dependency for SQLite support
E2E Testing:
- Initialize Playwright in e2e/ directory
- Add playwright.config.ts
- Create login flow E2E test
Build:
- Add test-unit, test-integration, test-system, test-e2e to Makefile
- Update test target to run all categories
- Add testing documentation to README
Note: Some tests have import issues due to missing python-jose
package in dev environment. This needs to be addressed separately.
Vite reads env vars at build time, not runtime. The previous setup
only set them in docker-compose 'environment' which is only available
at container runtime. Now they are passed as build args so Vite can
embed the correct API URL during the build process.
- Add build args to web service in both compose files
- Update Dockerfile to accept ARGs and set ENV for Vite
- Fixes login redirect always going to localhost:8000
The frontend code uses VITE_API_BASE_URL but docker-compose files
and .env.example were setting VITE_API_URL, causing the login
redirect to fall back to localhost:8000.
- Update docker-compose.traefik.yml: VITE_API_URL → VITE_API_BASE_URL
- Update docker-compose.yml: VITE_API_URL → VITE_API_BASE_URL
- Update .env.example: VITE_API_URL → VITE_API_BASE_URL
In Traefik deployment, API and web are on different domains.
Frontend was using relative paths (/auth/login) which resolved
to the web domain instead of the API domain.
- Update LoginRedirectPage to use VITE_API_BASE_URL for login link
- Update apiClient 401 interceptor to redirect to full API URL
- Ensures OAuth flow works correctly with separate domains
wget resolves 'localhost' to IPv6 [::1] but nginx only listens on
IPv4 0.0.0.0:80, causing connection refused. Using 127.0.0.1 ensures
IPv4 connection and healthy container status.
Replace hardcoded 'hq-api' and '' router names with
configurable hq-api and -web. This ensures
unique identifiers per deployment and avoids conflicts with other
services sharing the same Traefik instance.
- Create /run directory explicitly for nginx.pid
- Set proper ownership and permissions for non-root user
- Fixes 'open() /run/nginx.pid failed (13: Permission denied)' error
Services should reference networks by their compose key name ('traefik'),
not by the env var. The actual Docker network name is already configurable
via TRAEFIK_NETWORK in the network definition at the bottom.
- Replace hardcoded 'traefik' network references with configurable
traefik in both api and web services
- Network definition at bottom already supported configuration,
but service references were still hardcoded
The package-lock.json was missing some esbuild optional dependencies
for other platforms. Using npm install instead of npm ci allows the
docker build to proceed without requiring all platform-specific packages
in the lock file.
- Add docker-compose.yml with postgres, redis, api, and web services
- Add multi-stage Dockerfile for API (Python 3.11)
- Add multi-stage Dockerfile for web (Node.js 20 + nginx)
- Add Makefile with common development commands
- Add .env.example with all required environment variables
- Add placeholder pyproject.toml and package.json for builds
- Configure health checks for all services
- Setup persistent volumes for postgres, redis, and repos
- Run services as non-root users
The OIDC issuer URL was being used to construct authorize/token URLs,
but Authentik's endpoints are at different paths than the issuer base.
- Use the actual authorization_endpoint from .well-known config
- Use the actual token_endpoint from .well-known config
- Fixes Authentik 'not found' error on login redirect
The OIDC issuer URL in .env ends with a trailing slash, which caused
the authorize endpoint to have a double slash (//authorize).
- Normalize issuer URL by removing trailing slash before appending path
- Applied to both LoginPage.tsx and CallbackPage.tsx
- Fixes Authentik 'not found' error on login redirect
- Add VITE_OIDC_REDIRECT_URI to .env.example and docker-compose files
- Pass OIDC variables as build args in Dockerfile (build-time only)
- Update both docker-compose.prod.yml and docker-compose.traefik.yml
- Fixes undefined OIDC variables causing 404 on login redirect
The sec-headers@file middleware was referenced but not defined,
causing Traefik to return 404 for all routes.
- Changed sec-headers@file to default-security-headers@file in both
docker-compose.prod.yml and docker-compose.traefik.yml
- This assumes the external Traefik has default-security-headers defined
in its file provider configuration (e.g., in dynamic/middlewares.yml)
If you don't have this middleware defined, either:
1. Define it in your Traefik dynamic configuration, or
2. Remove the middleware label entirely