Quality cleanup refactoring

Made-with: Cursor
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-04-13 06:21:26 +03:00
parent 8f7deb3fca
commit 4eaf218f09
33 changed files with 957 additions and 207 deletions
@@ -0,0 +1,92 @@
# List of Changes
**Run**: 01-quality-cleanup
**Mode**: automatic
**Source**: self-discovered
**Date**: 2026-04-13
## Summary
Address thread safety issues, replace unsafe manual cryptographic padding with library implementations, remove dead code, and fix minor configurability/error-handling gaps.
## Changes
### C01: Thread-safe ApiClient singleton
- **File(s)**: main.py
- **Problem**: `get_api_client()` checks `if api_client is None` and assigns without a lock. Concurrent requests can create duplicate instances, with the second overwriting the first.
- **Change**: Protect the singleton initialization with a `threading.Lock` using the double-checked locking pattern.
- **Rationale**: FastAPI serves concurrent requests via threads; the global singleton must be safe under concurrency.
- **Risk**: low
- **Dependencies**: None
### C02: Encapsulate unlock state
- **File(s)**: main.py
- **Problem**: `unlock_state` and `unlock_error` are module-level globals mutated via a lock. This pattern scatters state management across the module and makes the state machine hard to reason about.
- **Change**: Move unlock state and error into a small state holder class that encapsulates the lock and provides thread-safe read/write methods.
- **Rationale**: Single Responsibility — state management belongs in one place, not spread across endpoint handlers and background tasks.
- **Risk**: low
- **Dependencies**: None
### C03: Use library PKCS7 unpadder in Security.decrypt_to
- **File(s)**: security.pyx
- **Problem**: `decrypt_to` manually reads the last byte to determine padding length. It does not validate that all N trailing bytes equal N (PKCS7 requirement). Corrupted ciphertext silently produces garbage. The `padding.PKCS7(128).unpadder()` is already imported but unused for decryption.
- **Change**: Replace manual unpadding (lines 38-44) with the library's `padding.PKCS7(128).unpadder()`, matching the encrypt path.
- **Rationale**: The library validates all padding bytes and raises on corruption instead of silently returning garbage.
- **Risk**: medium — changes decryption output on invalid input (correctly raises instead of silently passing)
- **Dependencies**: None
### C04: Use library PKCS7 unpadder in decrypt_archive
- **File(s)**: binary_split.py
- **Problem**: `decrypt_archive` manually reads the last byte of the decrypted file to strip padding (lines 46-53). Same incomplete PKCS7 validation as C03.
- **Change**: After decryption, use the `cryptography` library's PKCS7 unpadder to strip padding instead of manual byte inspection and file truncation.
- **Rationale**: Same as C03 — proper validation, raises on corruption.
- **Risk**: medium — same reasoning as C03
- **Dependencies**: None
### C05: Configurable log file path
- **File(s)**: constants.pyx
- **Problem**: Log file sink is hardcoded to `"Logs/log_loader_{time:YYYYMMDD}.txt"`. No environment variable override. Creates unexpected directories outside Docker.
- **Change**: Read the log directory from an environment variable (e.g., `LOG_DIR`) with the current value as default.
- **Rationale**: Configurability across development and production environments.
- **Risk**: low
- **Dependencies**: None
### C06: Log os.remove failure instead of swallowing
- **File(s)**: main.py
- **Problem**: `os.remove(tar_path)` catches `OSError` and passes silently (lines 143-146). Hides information about filesystem issues.
- **Change**: Log the exception at warning level instead of silently passing.
- **Rationale**: Per project rules, errors should not be silently suppressed.
- **Risk**: low
- **Dependencies**: None
### C07: Remove dead methods from ApiClient
- **File(s)**: api_client.pyx, api_client.pxd
- **Problem**: 5 methods are defined and declared but never called from any source file: `get_user`, `list_files`, `check_resource`, `upload_to_cdn`, `download_from_cdn`.
- **Change**: Delete the 5 orphan method definitions from `api_client.pyx` and their declarations from `api_client.pxd`.
- **Rationale**: Dead code misleads readers and breaks when its dependencies evolve. Git history preserves it.
- **Risk**: low
- **Dependencies**: None
### C08: Remove dead constants
- **File(s)**: constants.pyx, constants.pxd
- **Problem**: 5 constants in `constants.pyx` are never referenced outside their own file: `CONFIG_FILE`, `QUEUE_CONFIG_FILENAME`, `AI_ONNX_MODEL_FILE`, `MODELS_FOLDER`, `ALIGNMENT_WIDTH`. Additionally, 3 variables are declared in `constants.pxd` with no definition in `constants.pyx`: `QUEUE_MAXSIZE`, `COMMANDS_QUEUE`, `ANNOTATIONS_QUEUE`.
- **Change**: Delete the 5 unused constant definitions from `constants.pyx` and all 8 orphan declarations from `constants.pxd` (keeping only `CDN_CONFIG`, `SMALL_SIZE_KB`, `log`, `logerror`).
- **Rationale**: Dead code rots. Orphan `.pxd` declarations with no backing definition are especially misleading.
- **Risk**: low
- **Dependencies**: None
### C09: CDN upload failure silently ignored
- **File(s)**: api_client.pyx
- **Problem**: `upload_big_small_resource` (line 203) calls `self.cdn_manager.upload()` which returns `False` on failure, but the return value is never checked. A failed CDN upload means the big part is only saved locally — the resource cannot be downloaded from other devices.
- **Change**: Check the return value of `cdn_manager.upload` and raise on failure, matching the pattern in the (currently orphan) `upload_to_cdn` method.
- **Rationale**: Silent upload failure leads to incomplete resources that cannot be downloaded from CDN on other devices.
- **Risk**: medium — previously-silent failures will now raise exceptions
- **Dependencies**: None
### C10: API upload failure silently swallowed
- **File(s)**: api_client.pyx
- **Problem**: `upload_file` (line 91-102) catches all exceptions and only logs them. The caller (`upload_big_small_resource` at line 206) never knows the API upload failed. The small part is silently lost.
- **Change**: Let the exception propagate to the caller instead of catching and logging. The caller's error handling (main.py endpoint) will convert it to an appropriate HTTP error.
- **Rationale**: The upload endpoint should report failure, not return `{"status": "ok"}` when the small part was never uploaded.
- **Risk**: medium — previously-silent failures will now raise exceptions
- **Dependencies**: None