# Story 2.14: Stockage Temporaire & Métadonnées Status: done ## Story As a **system**, I want **to store uploaded files temporarily with metadata logging**, so that **files can be processed and tracked efficiently while maintaining security and performance**. ## Acceptance Criteria 1. **Unique Storage**: Every uploaded file is saved to a temporary location (configured via `config.UPLOAD_DIR`) with a unique ID as the filename (FR51). 2. **Metadata Logging**: For every upload, the system logs the following metadata: - `original_filename` - `file_size` - `file_hash` (SHA256) - `timestamp` - `user_id` (if available) 3. **No Content Logging**: Ensure that NO actual file content or sensitive internal document data is logged (NFR11, NFR16). 4. **Redis Tracking**: The file path and basic metadata are stored in Redis with a TTL of 60 minutes (Story 2.14). 5. **Job Association**: The metadata is correctly associated with the `job_id` generated during the translation request. 6. **Performance**: Metadata extraction (hashing) must not significantly delay the initial response (NFR1-NFR5). ## Tasks / Subtasks - [x] **Task 1: Implement Metadata Extraction Utility** - [x] 1.1 Add `calculate_sha256` method to `utils/file_handler.py`. - [x] 1.2 Update `FileHandler.get_file_info` or create a new method to include the hash and structured metadata. - [x] **Task 2: Integrate Redis for Temporary Tracking** - [x] 2.1 Verify `TierQuotaService`'s Redis client usage for reuse. - [x] 2.2 Implement a `StorageTracker` service (or extend `ProgressTracker`) to store file paths in Redis with TTL. - [x] 2.3 Key pattern: `translation:file:{job_id}` with 60 min TTL. - [x] **Task 3: Update Translation Endpoint** - [x] 3.1 In `routes/translate_routes.py`, trigger metadata logging after successful file save. - [x] 3.2 Ensure `user_id` and `timestamp` are captured. - [x] 3.3 Ensure the `input_path` is correctly pushed to Redis tracking. - [x] **Task 4: Audit Trail & Logging** - [x] 4.1 Use `structlog` (or standard logging if `structlog` not yet integrated) to log the metadata in a structured JSON format. - [x] 4.2 Validate that NO content is logged. - [x] **Task 5: Verification & Tests** - [x] 5.1 Add unit tests for SHA256 calculation. - [x] 5.2 Add integration tests verifying Redis entry creation and TTL. - [x] 5.3 Verify log output format. ## Dev Notes ### Previous Story Intelligence (2.13) - `2-13` implemented robust file validation. - The `translate_document_v1` endpoint already generates a `job_id` and saves the file using `file_handler_util.generate_unique_filename`. - `2.13` also introduced `CORRUPTED_FILE` and `INVALID_FORMAT` error codes. ### Architecture Compliance - **Storage**: Use `config.UPLOAD_DIR` and `config.OUTPUT_DIR`. - **TTL**: `config.FILE_TTL_MINUTES` (60 min). - **Security**: SHA256 for integrity and collision avoidance in logs. - **Zero Retention**: TTL in Redis is the first step toward the automated cleanup job (`2-15`). ### Project Structure Notes - `utils/file_handler.py`: Central place for file operations. - `routes/translate_routes.py`: Orchestrates the upload flow. - `middleware/tier_quota.py`: Good example of Redis client usage (`_get_async_redis`). ### References - [Source: _bmad-output/planning-artifacts/epics.md#Story 2.14] - [Source: _bmad-output/planning-artifacts/prd.md#NFR11, NFR16] - [Source: config.py#CLEANUP_ENABLED, FILE_TTL_MINUTES] ## Dev Agent Record ### Agent Model Used Gemini CLI (Expert Agent) ### Debug Log References - SHA256 unit tests passed. - StorageTracker unit tests (including Redis mocking and logging verification) passed. - Translation endpoint integration tests passed. ### Completion Notes List - ✅ Implemented `calculate_sha256` in `FileHandler`. - ✅ Updated `get_file_info` to include SHA256 hash. - ✅ Created `StorageTracker` service using `redis.asyncio`. - ✅ Integrated `StorageTracker` into `translate_document_v1` endpoint. - ✅ Added `structlog`-compatible logging for file uploads (metadata only). - ✅ Added comprehensive unit and integration tests. ### File List - `utils/file_handler.py` - `services/storage_tracker.py` - `routes/translate_routes.py` - `tests/test_file_handler.py` - `tests/test_storage_tracker.py` - `tests/test_translation_metadata_integration.py` ## Senior Developer Review (AI) **Reviewer:** Claude (Opencode) **Date:** 2026-02-21 **Outcome:** ✅ APPROVED (with fixes applied) ### Issues Found & Fixed | # | Severity | Issue | Fix Applied | |---|----------|-------|-------------| | 1 | HIGH | `calculate_sha256` sans gestion d'erreur | Added try/except, returns `None` on failure | | 2 | HIGH | Pas de cleanup si hash échoue | Added `cleanup_file()` call on hash failure in translate_routes.py | | 3 | MEDIUM | Fichier orphelin si hash échoue | Fixed with cleanup on error | | 4 | MEDIUM | `file_hash` pouvait être `None` sans validation | Added validation + raise `CORRUPTED_FILE` error | | 5 | MEDIUM | Échec Redis silencieux | Added `_log_error("redis_not_available", ...)` and `_log_info("file_tracked_in_redis", ...)` | | 6 | MEDIUM | TTL hardcodée | Changed to `_get_default_ttl()` using `config.FILE_TTL_MINUTES` | | 7 | LOW | `print()` au lieu de logger | Changed to `logging.getLogger(__name__)` | | 8 | LOW | Test mockait le hash | Added real SHA256 calculation test + hash failure test | ### Files Modified in Review - `utils/file_handler.py` - Error handling in `calculate_sha256`, logging in `cleanup_file` - `services/storage_tracker.py` - Config-based TTL, improved logging - `routes/translate_routes.py` - Hash validation + cleanup on failure - `tests/test_file_handler.py` - Added edge case tests - `tests/test_translation_metadata_integration.py` - Added hash failure test ### AC Validation | AC | Status | Evidence | |----|--------|----------| | AC1: Unique Storage | ✅ | `file_handler_util.generate_unique_filename()` used | | AC2: Metadata Logging | ✅ | `_log_info("file_uploaded", ...)` with all fields | | AC3: No Content Logging | ✅ | Only metadata logged, no file content | | AC4: Redis Tracking | ✅ | `storage_tracker.track_file()` with TTL | | AC5: Job Association | ✅ | `job_id` passed to `track_file()` | | AC6: Performance | ⚠️ | Hash is synchronous (noted for future async improvement) | ### Change Log - 2026-02-21: Code review completed, 8 issues found and fixed. Status → done.