Fix: Consolidate divergent _since_unix implementations (T-09)
Consolidate the two divergent implementations of _since_unix from ban_service.py and history_service.py into a single shared utility function in time_utils.py. Changes: - Move _since_unix to app/utils/time_utils.py with consistent time.time() approach - Move TIME_RANGE_SLACK_SECONDS constant to app/utils/constants.py - Update ban_service.py to import since_unix from time_utils - Update history_service.py to import since_unix from time_utils - Both services now use the same window boundary calculation with 60-second slack - Add comprehensive tests for the shared since_unix function - Document timestamp handling rationale in Backend-Development.md This ensures dashboard and history queries return consistent row counts for the same time range by using the same timestamp calculation and slack window across all services. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -69,6 +69,39 @@ from fail2ban.client.csocket import CSSocket # noqa: E402
|
||||
- `print()` for logging — use `structlog`.
|
||||
- `json.loads` / `json.dumps` on Pydantic models — use `.model_dump()` / `.model_validate()`.
|
||||
|
||||
### Timestamp Handling
|
||||
|
||||
Timestamp consistency is critical for accurate ban history queries across the dashboard and history endpoints. Follow these rules:
|
||||
|
||||
**Rule 1: Use consistent UTC timestamps**
|
||||
- All timestamps in the database are stored as Unix epochs (seconds since 1970-01-01 UTC).
|
||||
- fail2ban stores timestamps using `time.time()`, which is always UTC epoch seconds.
|
||||
- When querying fail2ban's SQLite database by timestamp, use `app.utils.time_utils.since_unix()` (not manual datetime calculations).
|
||||
|
||||
**Rule 2: Time-range windows include a 60-second slack**
|
||||
- The `since_unix()` function includes a 60-second slack window (`TIME_RANGE_SLACK_SECONDS` in `app.utils.constants`).
|
||||
- This slack accommodates:
|
||||
- Clock drift between the local system and fail2ban.
|
||||
- Test seeding delays when timestamps are manually set to exact boundaries.
|
||||
- The slack ensures that dashboard and history queries return consistent row counts for the same time range.
|
||||
|
||||
**Rule 3: Never duplicate timestamp calculation logic**
|
||||
- All services that query by time range must import and use `since_unix()`.
|
||||
- Do not recalculate timestamps locally using `datetime` or `time` modules in service code.
|
||||
- If you need a timestamp for a time range, use `since_unix()`.
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from app.utils.time_utils import since_unix
|
||||
|
||||
# Get all bans from the last 24 hours (with 60-second slack)
|
||||
since_ts: int = since_unix("24h")
|
||||
rows = await db.execute(
|
||||
"SELECT * FROM bans WHERE timeofban >= ?",
|
||||
(since_ts,)
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Project Structure
|
||||
|
||||
Reference in New Issue
Block a user