docs: comprehensive API documentation
- Create enhanced health endpoints with /health and /health/db - Add comprehensive docstrings to all API endpoints - Add Pydantic response models with Field descriptions - Create apps/api/README.md with setup guide - Create ADR-001 for session auth decision - Create ADR-002 for async SQLAlchemy decision - Quality gates: Python syntax OK, TypeScript OK
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
# ADR-001: Session-Based Authentication with httpOnly Cookies
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
The application needs an authentication mechanism for the OAuth2 flow with Authentik. We need to decide between:
|
||||
|
||||
1. **JWT tokens** stored in localStorage (common SPA pattern)
|
||||
2. **Session cookies** with httpOnly flag
|
||||
3. **JWT tokens** in httpOnly cookies
|
||||
|
||||
### Constraints
|
||||
|
||||
- Frontend and API run on different subdomains in production (e.g., `app.example.com` and `api.example.com`)
|
||||
- Must support OAuth2 authorization code flow
|
||||
- Must work with Traefik reverse proxy
|
||||
- Must be secure against XSS attacks
|
||||
|
||||
## Decision
|
||||
|
||||
We will use **session-based authentication with HMAC-signed httpOnly cookies**.
|
||||
|
||||
### Implementation
|
||||
|
||||
1. After OAuth callback, the API creates a session token (HMAC-SHA256 signed)
|
||||
2. Token is stored in an httpOnly, Secure, SameSite cookie
|
||||
3. Frontend never sees or stores the token
|
||||
4. Cookie is sent automatically with every request via `withCredentials: true`
|
||||
5. Session is stateless - token contains user_id and expiry
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- **XSS protection**: Token is never exposed to JavaScript
|
||||
- **Simpler frontend**: No token management, refresh logic, or storage
|
||||
- **Immediate revocation**: Can invalidate sessions server-side if needed
|
||||
- **Standards compliant**: Uses well-established cookie security mechanisms
|
||||
- **Works across subdomains**: Cookie domain can be set to parent domain
|
||||
|
||||
### Negative
|
||||
|
||||
- **CSRF risk**: Requires CSRF protection for state-changing operations (mitigated by SameSite=Lax/Strict)
|
||||
- **Less flexible**: Harder to use with non-browser clients (mitigated by API key support if needed)
|
||||
- **Cookie size**: Session token adds ~100 bytes to every request
|
||||
|
||||
### Alternatives Considered
|
||||
|
||||
**JWT in localStorage**
|
||||
- Pros: Simple implementation, works with any client
|
||||
- Cons: Vulnerable to XSS, requires manual token refresh, complex frontend logic
|
||||
- Rejected due to XSS vulnerability
|
||||
|
||||
**JWT in httpOnly cookies**
|
||||
- Pros: XSS protection, standard JWT benefits
|
||||
- Cons: Complex refresh token rotation, no easy revocation, larger token size
|
||||
- Rejected in favor of simpler session cookies
|
||||
|
||||
## Date
|
||||
|
||||
2026-05-18
|
||||
|
||||
## Participants
|
||||
|
||||
- Development Team
|
||||
@@ -0,0 +1,70 @@
|
||||
# ADR-002: Async SQLAlchemy with PostgreSQL
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
We need to choose an ORM and database for the application. The application will handle concurrent requests and potentially long-running operations (git operations, Docker commands).
|
||||
|
||||
### Requirements
|
||||
|
||||
- Support concurrent API requests without blocking
|
||||
- Handle async operations (database queries + subprocess calls)
|
||||
- Type safety and autocompletion
|
||||
- Migration support
|
||||
- Good Python ecosystem support
|
||||
|
||||
## Decision
|
||||
|
||||
We will use **SQLAlchemy 2.0 with async PostgreSQL** via `asyncpg`.
|
||||
|
||||
### Implementation
|
||||
|
||||
1. **SQLAlchemy 2.0** with `AsyncSession` and declarative models
|
||||
2. **PostgreSQL** as the primary database
|
||||
3. **asyncpg** as the async driver
|
||||
4. **Alembic** for database migrations
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- **Non-blocking I/O**: Database queries don't block the event loop
|
||||
- **Scalability**: Can handle many concurrent connections
|
||||
- **Type safety**: SQLAlchemy 2.0 has excellent type hint support
|
||||
- **Ecosystem**: Large community, extensive documentation
|
||||
- **Flexibility**: Can fall back to sync operations for complex migrations
|
||||
|
||||
### Negative
|
||||
|
||||
- **Complexity**: Async SQLAlchemy has a steeper learning curve
|
||||
- **Debugging**: Harder to debug async code
|
||||
- **Migration limitations**: Some Alembic operations require sync connections
|
||||
- **Connection pool**: Requires careful configuration
|
||||
|
||||
### Alternatives Considered
|
||||
|
||||
**Prisma ORM**
|
||||
- Pros: Modern, type-safe, auto-generated client
|
||||
- Cons: Less mature Python support, custom query language
|
||||
- Rejected due to less mature ecosystem
|
||||
|
||||
**Tortoise ORM**
|
||||
- Pros: Built for async, Django-like syntax
|
||||
- Cons: Smaller community, fewer features
|
||||
- Rejected in favor of SQLAlchemy's maturity
|
||||
|
||||
**Sync SQLAlchemy with threading**
|
||||
- Pros: Simpler, well-understood
|
||||
- Cons: Thread overhead, harder to integrate with async code
|
||||
- Rejected in favor of native async support
|
||||
|
||||
## Date
|
||||
|
||||
2026-05-17
|
||||
|
||||
## Participants
|
||||
|
||||
- Development Team
|
||||
Reference in New Issue
Block a user