feat: implement audit trail module

- Backend (FastAPI):
  - AuditLog and AuditAlert models with Alembic migration
  - AuditService with SHA-256 checksum for log integrity
  - AuditMiddleware for request metadata extraction (IP, user_agent)
  - Integrated audit logging into Task, Project, Blocker APIs
  - Query API with filtering, pagination, CSV export
  - Integrity verification endpoint
  - Sensitive operation alerts with acknowledgement

- Frontend (React + Vite):
  - Admin AuditPage with filters and export
  - ResourceHistory component for change tracking
  - Audit service for API calls

- Testing:
  - 15 tests covering service and API endpoints

- OpenSpec:
  - add-audit-trail change archived

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
beabigegg
2025-12-29 21:21:18 +08:00
parent 3470428411
commit 0ef78e13ff
24 changed files with 2431 additions and 7 deletions

View File

@@ -0,0 +1,23 @@
import uuid
from sqlalchemy import Column, String, Text, Boolean, DateTime, ForeignKey, JSON
from sqlalchemy.sql import func
from sqlalchemy.orm import relationship
from app.core.database import Base
class AuditAlert(Base):
__tablename__ = "pjctrl_audit_alerts"
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
audit_log_id = Column(String(36), ForeignKey("pjctrl_audit_logs.id", ondelete="CASCADE"), nullable=False)
alert_type = Column(String(50), nullable=False)
recipients = Column(JSON, nullable=False)
message = Column(Text, nullable=True)
is_acknowledged = Column(Boolean, default=False, nullable=False)
acknowledged_by = Column(String(36), ForeignKey("pjctrl_users.id", ondelete="SET NULL"), nullable=True)
acknowledged_at = Column(DateTime, nullable=True)
created_at = Column(DateTime, server_default=func.now(), nullable=False)
# Relationships
audit_log = relationship("AuditLog", back_populates="alerts")
acknowledger = relationship("User", foreign_keys=[acknowledged_by])