feat(data-safety): scheduled DB backups + audit trail + verified actor attribution

- db-backup sidecar: pg_dump -Fc of procurement+casdoor every
  BACKUP_INTERVAL_SECONDS into BACKUP_DIR, retention via BACKUP_KEEP_DAYS
- audit_log table (migration.sql + runtime ensure_audit_table) with
  model + transactional helper (utils/audit.record) — wired into user
  create/role-change and inventory transaction creation
- get_actor: quote_events.actor now stores the VERIFIED user's display
  name instead of the raw bearer token
This commit is contained in:
MMD
2026-09-18 22:23:34 +03:30
parent e17d1f0883
commit e5d6c77c3c
12 changed files with 187 additions and 14 deletions
+11
View File
@@ -43,6 +43,7 @@ from app.utils.errors import (
UnauthorizedError,
ValidationError,
)
from app.utils.audit import record as audit_record
from app.utils.pagination import paginate, sort_query
from app.utils.serialization import to_iso
@@ -494,6 +495,16 @@ async def create_transaction(
if dest.project_id != scope.project_id:
raise ForbiddenError("انبار مقصد متعلق به پروژه شما نیست")
txs = await apply_transaction(db, payload, performed_by=scope.actor)
await audit_record(
db,
actor=scope.actor,
action="inventory.transaction_created",
entity="inventory",
entity_id=str(txs[0].inventory_id) if txs else None,
details={"type": payload.transaction_type, "quantity": str(payload.quantity),
"warehouse_id": str(payload.warehouse_id),
"tx_ids": [str(tx.id) for tx in txs]},
)
return {"data": [transaction_to_dict(tx) for tx in txs]}