Major changes across backend, frontend, infrastructure: - Provider system with model selection (Google, DeepL, OpenAI, Ollama, Google Cloud) - Admin panel: user management, pricing, settings - Glossary system with CSV import/export - Subscription and tier quota management - Security hardening (rate limiting, API key auth, path traversal fixes) - Docker compose for dev, prod, and IONOS deployment - Alembic migrations for new tables - Frontend: dashboard, pricing page, landing page, i18n (en/fr) - Test suite and verification scripts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
143 lines
6.3 KiB
Markdown
143 lines
6.3 KiB
Markdown
# 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.
|