# 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.
