Commit Graph

37 Commits

Author SHA1 Message Date
Luca Sacchi Ricciardi
19a2c527a1 docs(progress): update T41-T43 completion status
- Mark T41, T42, T43 as completed with commit reference
- Update progress to 52% (38/74 tasks)
- Add T41-T43 context to githistory.md
- 24 tests with 100% coverage on tokens router
2026-04-07 17:03:41 +02:00
Luca Sacchi Ricciardi
5e89674b94 feat(tokens): T41-T43 implement API token management endpoints
- Add max_api_tokens_per_user config (default 5)
- Implement POST /api/tokens (T41): generate token with limit check
- Implement GET /api/tokens (T42): list active tokens, no values exposed
- Implement DELETE /api/tokens/{id} (T43): soft delete with ownership check
- Security: plaintext token shown ONLY at creation
- Security: SHA-256 hash stored in DB, never the plaintext
- Security: revoked tokens return 401 on public API
- 24 tests with 100% coverage on tokens router

Closes T41, T42, T43
2026-04-07 16:58:57 +02:00
Luca Sacchi Ricciardi
5f39460510 docs(progress): update progress for T35-T40 completion
- Public API phase completed (6/9 tasks)
- 70 new tests added, coverage maintained
- Ready for T41-T43 (token management endpoints)
2026-04-07 16:16:29 +02:00
Luca Sacchi Ricciardi
d274970358 test(public-api): T40 add comprehensive public API endpoint tests
- Schema tests: 25 tests (100% coverage)
- Rate limit tests: 18 tests (98% coverage)
- Endpoint tests: 27 tests for stats/usage/keys
- Security tests: JWT rejection, inactive tokens, missing auth
- Total: 70 tests for public API v1
2026-04-07 16:16:18 +02:00
Luca Sacchi Ricciardi
3b71ac55c3 feat(rate-limit): T39 implement rate limiting for public API
- 100 requests/hour per API token
- 30 requests/minute per IP (fallback)
- In-memory storage with auto-cleanup
- Headers: X-RateLimit-Limit, X-RateLimit-Remaining
- Returns 429 Too Many Requests when exceeded
2026-04-07 16:16:06 +02:00
Luca Sacchi Ricciardi
88b43afa7e feat(public-api): T36-T38 implement public API endpoints
- GET /api/v1/stats: aggregated stats with date range (default 30 days)
- GET /api/v1/usage: paginated usage with required date filters
- GET /api/v1/keys: key list with stats, no key values exposed
- All endpoints use API token auth and rate limiting
2026-04-07 16:15:49 +02:00
Luca Sacchi Ricciardi
3253293dd4 feat(auth): add get_current_user_from_api_token dependency
- Validates API tokens (or_api_* prefix)
- SHA-256 hash lookup in api_tokens table
- Updates last_used_at on each request
- Distinguishes from JWT tokens (401 with clear error)
2026-04-07 16:15:34 +02:00
Luca Sacchi Ricciardi
a8095f4df7 feat(schemas): T35 add Pydantic public API schemas
- PublicStatsResponse: summary + period info
- PublicUsageResponse: paginated usage items
- PublicKeyInfo: key metadata with stats (no values!)
- ApiToken schemas: create, response, create-response
- 25 unit tests, 100% coverage
2026-04-07 16:15:22 +02:00
Luca Sacchi Ricciardi
16f740f023 feat(stats): T32-T33 implement dashboard and usage endpoints
Add statistics router with two endpoints:
- GET /api/stats/dashboard: Aggregated dashboard statistics
  - Query param: days (1-365, default 30)
  - Auth required
  - Returns DashboardResponse

- GET /api/usage: Detailed usage statistics with filtering
  - Required params: start_date, end_date
  - Optional filters: api_key_id, model
  - Pagination: skip, limit (max 1000)
  - Auth required
  - Returns List[UsageStatsResponse]

Also add get_usage_stats() service function for querying
individual usage records with filtering and pagination.
2026-04-07 15:22:31 +02:00
Luca Sacchi Ricciardi
b075ae47fe feat(services): T31 implement statistics aggregation service
Add statistics aggregation service with 4 core functions:
- get_summary(): Aggregates total requests, cost, tokens with avg cost
- get_by_model(): Groups stats by model with percentage calculations
- get_by_date(): Groups stats by date for time series data
- get_dashboard_data(): Combines all stats for dashboard view

Features:
- SQLAlchemy queries with ApiKey join for user filtering
- Decimal precision for all monetary values
- Period calculation and percentage breakdowns
- Top models extraction

Test: 11 unit tests covering all aggregation functions
2026-04-07 15:16:22 +02:00
Luca Sacchi Ricciardi
0df1638da8 feat(schemas): T30 add Pydantic statistics schemas
Add comprehensive Pydantic schemas for statistics management:
- UsageStatsCreate: input validation for creating usage stats
- UsageStatsResponse: orm_mode response schema
- StatsSummary: aggregated statistics with totals and averages
- StatsByModel: per-model breakdown with percentages
- StatsByDate: daily usage aggregation
- DashboardResponse: complete dashboard data structure

All schemas use Decimal for cost precision and proper validation.

Test: 16 unit tests, 100% coverage on stats.py
2026-04-07 15:04:49 +02:00
Luca Sacchi Ricciardi
761ef793a8 docs(progress): update progress for completed API keys phase
- Mark T23-T29 as completed
- Update progress to 39% (29/74 tasks)
- Add section summary for API keys

Refs: T29
2026-04-07 14:46:30 +02:00
Luca Sacchi Ricciardi
3824ce5169 feat(openrouter): T28 implement API key validation service
- Add validate_api_key() function for OpenRouter key validation
- Add get_key_info() function to retrieve key metadata
- Implement proper error handling (timeout, network errors)
- Use httpx with 10s timeout
- Export from services/__init__.py
- 92% coverage on openrouter module (13 tests)

Refs: T28
2026-04-07 14:44:15 +02:00
Luca Sacchi Ricciardi
abf7e7a532 feat(api-keys): T24-T27 implement API keys CRUD endpoints
- T24: POST /api/keys with encryption and limit validation
- T25: GET /api/keys with pagination and sorting
- T26: PUT /api/keys/{id} for partial updates
- T27: DELETE /api/keys/{id} with cascade
- Add ownership verification (403 for unauthorized access)
- API key encryption with AES-256 before storage
- Never expose API key value in responses
- 100% coverage on api_keys router (25 tests)

Refs: T24 T25 T26 T27
2026-04-07 14:41:53 +02:00
Luca Sacchi Ricciardi
2e4c1bb1e5 feat(schemas): T23 add Pydantic API key schemas
- Add ApiKeyCreate schema with OpenRouter key format validation
- Add ApiKeyUpdate schema for partial updates
- Add ApiKeyResponse schema (excludes key value for security)
- Add ApiKeyListResponse schema for pagination
- Export schemas from __init__.py
- 100% coverage on new module (23 tests)

Refs: T23
2026-04-07 14:28:03 +02:00
Luca Sacchi Ricciardi
b4fbb74113 docs: add githistory.md for authentication phase
Document commit history for T17-T22 with:
- Context and motivation for each commit
- Implementation details
- Test coverage summary
- Phase summary with metrics
2026-04-07 13:58:46 +02:00
Luca Sacchi Ricciardi
4dea358b81 test(auth): T22 add comprehensive auth endpoint tests
Add test suite for authentication with:
- 5 register tests: success, duplicate email, weak password, password mismatch, invalid email
- 4 login tests: success, invalid email, wrong password, inactive user
- 3 logout tests: success, no token, invalid token
- 3 get_current_user tests: expired token, missing sub claim, nonexistent user

Test coverage: 15 tests for auth router + 19 tests for auth schemas = 34 total
Coverage: 98%+ for auth modules

Files:
- tests/unit/routers/test_auth.py
- tests/unit/schemas/test_auth_schemas.py
2026-04-07 13:58:03 +02:00
Luca Sacchi Ricciardi
1fe5e1b031 feat(deps): T21 implement get_current_user dependency
Add authentication dependency with:
- HTTPBearer for token extraction from Authorization header
- JWT token decoding with decode_access_token()
- User ID extraction from 'sub' claim
- Database user lookup with existence check
- Active user verification
- HTTPException 401 for invalid/expired tokens or inactive users

Used as FastAPI dependency: Depends(get_current_user)

Location: src/openrouter_monitor/dependencies/auth.py
2026-04-07 13:57:56 +02:00
Luca Sacchi Ricciardi
b00dae2a58 feat(auth): T20 implement user logout endpoint
Add POST /api/auth/logout endpoint with:
- JWT stateless logout (client-side token removal)
- Requires valid authentication token
- Returns success message

Note: Token blacklisting can be added in future for enhanced security

Test coverage: 3 tests for logout scenarios
2026-04-07 13:57:49 +02:00
Luca Sacchi Ricciardi
4633de5e43 feat(auth): T19 implement user login endpoint
Add POST /api/auth/login endpoint with:
- UserLogin schema validation
- User lookup by email
- Password verification with bcrypt
- JWT token generation
- TokenResponse with access_token, token_type, expires_in

Status: 200 OK with token on success, 401 for invalid credentials

Test coverage: 4 tests for login endpoint including inactive user handling
2026-04-07 13:57:43 +02:00
Luca Sacchi Ricciardi
714bde681c feat(auth): T18 implement user registration endpoint
Add POST /api/auth/register endpoint with:
- UserRegister schema validation
- Email uniqueness check
- Password hashing with bcrypt
- User creation in database
- UserResponse returned (excludes password)

Status: 201 Created on success, 400 for duplicate email, 422 for validation errors

Test coverage: 5 tests for register endpoint
2026-04-07 13:57:38 +02:00
Luca Sacchi Ricciardi
02473bc39e feat(schemas): T17 add Pydantic auth schemas
Add authentication schemas for user registration and login:
- UserRegister: email, password (with strength validation), password_confirm
- UserLogin: email, password
- UserResponse: id, email, created_at, is_active (orm_mode=True)
- TokenResponse: access_token, token_type, expires_in
- TokenData: user_id, exp

Includes field validators for password strength and password confirmation matching.

Test coverage: 19 tests for all schemas
2026-04-07 13:52:33 +02:00
Luca Sacchi Ricciardi
a698d09a77 feat(security): T16 finalize security services exports
- Add __init__.py with all security service exports
- Export EncryptionService, JWT utilities, Password functions, Token functions
- 70 total tests for security services
- 100% coverage on all security modules
- All imports verified working
2026-04-07 12:14:16 +02:00
Luca Sacchi Ricciardi
649ff76d6c feat(security): T15 implement API token generation
- Add generate_api_token with format 'or_api_' + 48 bytes random
- Implement hash_token with SHA-256
- Add verify_api_token with timing-safe comparison (secrets.compare_digest)
- Only hash stored in DB, plaintext shown once
- 20 comprehensive tests with 100% coverage
- Handle TypeError for non-string inputs
2026-04-07 12:12:39 +02:00
Luca Sacchi Ricciardi
781e564ea0 feat(security): T14 implement JWT utilities
- Add create_access_token with custom/default expiration
- Add decode_access_token with signature verification
- Add verify_token returning TokenData dataclass
- Support HS256 algorithm with config.SECRET_KEY
- Payload includes exp, iat, sub claims
- 19 comprehensive tests with 100% coverage
- Handle expired tokens, invalid signatures, missing claims
2026-04-07 12:10:04 +02:00
Luca Sacchi Ricciardi
54e81162df feat(security): T13 implement bcrypt password hashing
- Add password hashing with bcrypt (12 rounds)
- Implement verify_password with timing-safe comparison
- Add validate_password_strength with comprehensive rules
  - Min 12 chars, uppercase, lowercase, digit, special char
- 19 comprehensive tests with 100% coverage
- Handle TypeError for non-string inputs
2026-04-07 12:06:38 +02:00
Luca Sacchi Ricciardi
2fdd9d16fd feat(security): T12 implement AES-256 encryption service
- Add EncryptionService with AES-256-GCM via cryptography.fernet
- Implement PBKDF2HMAC key derivation with SHA256 (100k iterations)
- Deterministic salt derived from master_key for consistency
- Methods: encrypt(), decrypt() with proper error handling
- 12 comprehensive tests with 100% coverage
- Handle InvalidToken, TypeError edge cases
2026-04-07 12:03:45 +02:00
Luca Sacchi Ricciardi
66074a430d docs(progress): update progress after Database & Models phase completion
- T06-T11: 6/6 tasks completed (100% of Database & Models phase)
- Overall progress: 15% (11/74 tasks)
- All tests passing: 73 tests, 100% coverage
- Alembic migrations functional (upgrade/downgrade verified)
2026-04-07 11:16:07 +02:00
Luca Sacchi Ricciardi
abe9fc166b feat(migrations): T11 setup Alembic and initial schema migration
- Initialize Alembic with alembic init alembic
- Configure alembic.ini to use DATABASE_URL from environment
- Configure alembic/env.py to import Base and models metadata
- Generate initial migration: c92fc544a483_initial_schema
- Migration creates all 4 tables: users, api_keys, api_tokens, usage_stats
- Migration includes all indexes, constraints, and foreign keys
- Test upgrade/downgrade cycle works correctly

Alembic commands:
- alembic upgrade head
- alembic downgrade -1
- alembic revision --autogenerate -m 'message'

Tests: 13 migration tests pass
2026-04-07 11:14:45 +02:00
Luca Sacchi Ricciardi
ea198e8b0d feat(models): T07-T10 create SQLAlchemy models for User, ApiKey, UsageStats, ApiToken
- Add User model with email unique constraint and relationships
- Add ApiKey model with encrypted key storage and user relationship
- Add UsageStats model with unique constraint (api_key_id, date, model)
- Add ApiToken model with token_hash indexing
- Configure all cascade delete relationships
- Add 49 comprehensive tests with 95% coverage

Models:
- User: id, email, password_hash, created_at, updated_at, is_active
- ApiKey: id, user_id, name, key_encrypted, is_active, created_at, last_used_at
- UsageStats: id, api_key_id, date, model, requests_count, tokens_input, tokens_output, cost
- ApiToken: id, user_id, token_hash, name, created_at, last_used_at, is_active

Tests: 49 passed, coverage 95%
2026-04-07 11:09:12 +02:00
Luca Sacchi Ricciardi
60d9228d91 feat(db): T06 create database connection and session management
- Add database.py with SQLAlchemy engine and session
- Implement get_db() for FastAPI dependency injection
- Implement init_db() for table creation
- Use SQLAlchemy 2.0 declarative_base() syntax
- Add comprehensive tests with 100% coverage

Tests: 11 passed, 100% coverage
2026-04-07 10:53:13 +02:00
Luca Sacchi Ricciardi
28fde3627e feat(setup): T05 configure pytest with coverage
- Create pytest.ini with:
  - Test discovery configuration (testpaths, python_files)
  - Asyncio mode settings
  - Coverage configuration (>=90% requirement)
  - Custom markers (unit, integration, e2e, slow)
- Update conftest.py with:
  - pytest_asyncio plugin
  - Shared fixtures (project_root, src_path, temp_dir, mock_env_vars)
  - Path configuration for imports
- Add test_pytest_config.py with 12 unit tests
- All tests passing (12/12)

Refs: T05

Completes setup phase T01-T05
2026-04-07 09:55:12 +02:00
Luca Sacchi Ricciardi
aece120017 feat(setup): T04 setup configuration files
- Create config.py with Pydantic Settings (SettingsConfigDict v2)
- Add all required configuration fields with defaults
- Create .env.example template with all environment variables
- Implement get_settings() with @lru_cache for performance
- Add test_configuration.py with 13 unit tests
- All tests passing (13/13)

Refs: T04
2026-04-07 09:52:33 +02:00
Luca Sacchi Ricciardi
715536033b feat(setup): T03 create requirements.txt with dependencies
- Add requirements.txt with all core dependencies:
  - FastAPI 0.104.1, uvicorn 0.24.0
  - SQLAlchemy 2.0.23, Alembic 1.12.1
  - Pydantic 2.5.0, pydantic-settings 2.1.0
  - python-jose 3.3.0, passlib 1.7.4, cryptography 41.0.7
  - httpx 0.25.2, pytest 7.4.3, pytest-asyncio 0.21.1, pytest-cov 4.1.0
- Add test_requirements.py with 15 unit tests
- All tests passing (15/15)

Refs: T03
2026-04-07 09:48:15 +02:00
Luca Sacchi Ricciardi
3f0f77cc23 feat(setup): T02 initialize virtual environment and gitignore
- Create comprehensive .gitignore with Python, venv, DB exclusions
- Add test_virtual_env_setup.py with 6 unit tests
- Verify Python 3.13.5 compatibility (>= 3.11 required)
- All tests passing (6/6)

Refs: T02
2026-04-07 09:46:21 +02:00
Luca Sacchi Ricciardi
75f40acb17 feat(setup): T01 create project directory structure
- Create src/openrouter_monitor/ package structure
- Create models/, routers/, services/, utils/ subpackages
- Create tests/unit/ and tests/integration/ structure
- Create alembic/, docs/, scripts/ directories
- Add test_project_structure.py with 13 unit tests
- All tests passing (13/13)

Refs: T01
2026-04-07 09:44:41 +02:00
lucasacchi
849a65d4d9 Initial commit 2026-04-07 08:53:55 +02:00