mirror of
https://github.com/azaion/loader.git
synced 2026-04-22 08:46:33 +00:00
Add E2E tests, fix bugs
Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
# HTTP API
|
||||
|
||||
## 1. High-Level Overview
|
||||
|
||||
**Purpose**: FastAPI application that exposes HTTP endpoints for health monitoring, user authentication, encrypted resource loading/uploading, and a background Docker image unlock workflow.
|
||||
|
||||
**Architectural Pattern**: Thin controller — delegates all business logic to Resource Management (03) and binary_split.
|
||||
|
||||
**Upstream dependencies**: Core Models (01) — UnlockState enum; Resource Management (03) — ApiClient, binary_split functions
|
||||
|
||||
**Downstream consumers**: None — this is the system entry point, consumed by external HTTP clients.
|
||||
|
||||
## 2. Internal Interfaces
|
||||
|
||||
### Interface: Module-level Functions
|
||||
|
||||
| Function | Input | Output | Description |
|
||||
|-------------------|---------------------------------|----------------|---------------------------------|
|
||||
| `get_api_client` | — | ApiClient | Lazy singleton accessor |
|
||||
| `_run_unlock` | `str email, str password` | — | Background task: full unlock flow |
|
||||
|
||||
## 3. External API Specification
|
||||
|
||||
| Endpoint | Method | Auth | Rate Limit | Description |
|
||||
|--------------------|--------|----------|------------|------------------------------------------|
|
||||
| `/health` | GET | Public | — | Liveness probe |
|
||||
| `/status` | GET | Public | — | Auth status + model cache dir |
|
||||
| `/login` | POST | Public | — | Set user credentials |
|
||||
| `/load/{filename}` | POST | Implicit | — | Download + decrypt resource |
|
||||
| `/upload/{filename}`| POST | Implicit | — | Encrypt + upload resource (big/small) |
|
||||
| `/unlock` | POST | Public | — | Start background Docker unlock |
|
||||
| `/unlock/status` | GET | Public | — | Poll unlock workflow progress |
|
||||
|
||||
"Implicit" auth = credentials must have been set via `/login` first; enforced by ApiClient's auto-login on token absence.
|
||||
|
||||
### Request/Response Schemas
|
||||
|
||||
**POST /login**
|
||||
```json
|
||||
// Request
|
||||
{"email": "user@example.com", "password": "secret"}
|
||||
// Response 200
|
||||
{"status": "ok"}
|
||||
// Response 401
|
||||
{"detail": "error message"}
|
||||
```
|
||||
|
||||
**POST /load/{filename}**
|
||||
```json
|
||||
// Request
|
||||
{"filename": "model.bin", "folder": "models"}
|
||||
// Response 200 — binary octet-stream
|
||||
// Response 500
|
||||
{"detail": "error message"}
|
||||
```
|
||||
|
||||
**POST /upload/{filename}**
|
||||
```
|
||||
// Request — multipart/form-data
|
||||
data: <file>
|
||||
folder: "models" (form field, default "models")
|
||||
// Response 200
|
||||
{"status": "ok"}
|
||||
```
|
||||
|
||||
**POST /unlock**
|
||||
```json
|
||||
// Request
|
||||
{"email": "user@example.com", "password": "secret"}
|
||||
// Response 200
|
||||
{"state": "authenticating"}
|
||||
// Response 404
|
||||
{"detail": "Encrypted archive not found"}
|
||||
```
|
||||
|
||||
**GET /unlock/status**
|
||||
```json
|
||||
// Response 200
|
||||
{"state": "decrypting", "error": null}
|
||||
```
|
||||
|
||||
## 4. Data Access Patterns
|
||||
|
||||
### Caching Strategy
|
||||
|
||||
| Data | Cache Type | TTL | Invalidation |
|
||||
|---------------|---------------------|---------------|---------------------|
|
||||
| ApiClient | In-memory singleton | Process life | Never |
|
||||
| unlock_state | Module global | Until next unlock | State machine transition |
|
||||
|
||||
## 5. Implementation Details
|
||||
|
||||
**State Management**: Module-level globals (`api_client`, `unlock_state`, `unlock_error`) protected by `threading.Lock` for unlock state mutations.
|
||||
|
||||
**Key Dependencies**:
|
||||
|
||||
| Library | Version | Purpose |
|
||||
|----------------|---------|------------------------------|
|
||||
| fastapi | latest | HTTP framework |
|
||||
| uvicorn | latest | ASGI server |
|
||||
| pydantic | (via fastapi) | Request/response models |
|
||||
| python-multipart| latest | File upload support |
|
||||
|
||||
**Error Handling Strategy**:
|
||||
- `/login` — catches all exceptions, returns 401
|
||||
- `/load`, `/upload` — catches all exceptions, returns 500
|
||||
- `/unlock` — checks preconditions (archive exists, not already in progress), then delegates to background task
|
||||
- Background task (`_run_unlock`) catches all exceptions, sets `unlock_state = error` with error message
|
||||
|
||||
## 6. Extensions and Helpers
|
||||
|
||||
None.
|
||||
|
||||
## 7. Caveats & Edge Cases
|
||||
|
||||
**Known limitations**:
|
||||
- No authentication middleware — endpoints rely on prior `/login` call having set credentials on the singleton
|
||||
- `get_api_client()` uses a global without locking — race on first concurrent access
|
||||
- `/load/{filename}` has a path parameter `filename` but also takes `req.filename` from the body — the path param is unused
|
||||
- `_run_unlock` silently ignores `OSError` when removing tar file (acceptable cleanup behavior)
|
||||
|
||||
**Potential race conditions**:
|
||||
- `unlock_state` mutations are lock-protected, but `api_client` singleton creation is not
|
||||
- Concurrent `/unlock` calls: the lock check prevents duplicate starts, but there's a small TOCTOU window between the check and the `background_tasks.add_task` call
|
||||
|
||||
**Performance bottlenecks**:
|
||||
- `/load` and `/upload` are synchronous — large files block the worker thread
|
||||
- `_run_unlock` runs as a background task (single thread) — only one unlock can run at a time
|
||||
|
||||
## 8. Dependency Graph
|
||||
|
||||
**Must be implemented after**: Core Models (01), Resource Management (03)
|
||||
|
||||
**Can be implemented in parallel with**: —
|
||||
|
||||
**Blocks**: — (entry point)
|
||||
|
||||
## 9. Logging Strategy
|
||||
|
||||
No direct logging in this component — all logging is handled by downstream components via `constants.log()` / `constants.logerror()`.
|
||||
|
||||
**Log format**: N/A (delegates)
|
||||
|
||||
**Log storage**: N/A (delegates)
|
||||
Reference in New Issue
Block a user