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

@@ -20,6 +20,10 @@ from app.schemas.notification import (
from app.schemas.blocker import (
BlockerCreate, BlockerResolve, BlockerResponse, BlockerListResponse
)
from app.schemas.audit import (
AuditLogResponse, AuditLogListResponse, AuditAlertResponse, AuditAlertListResponse,
IntegrityCheckRequest, IntegrityCheckResponse
)
__all__ = [
"LoginRequest",
@@ -64,4 +68,10 @@ __all__ = [
"BlockerResolve",
"BlockerResponse",
"BlockerListResponse",
"AuditLogResponse",
"AuditLogListResponse",
"AuditAlertResponse",
"AuditAlertListResponse",
"IntegrityCheckRequest",
"IntegrityCheckResponse",
]

View File

@@ -0,0 +1,61 @@
from datetime import datetime
from typing import Optional, List, Any
from pydantic import BaseModel
class AuditLogResponse(BaseModel):
id: str
event_type: str
resource_type: str
resource_id: Optional[str]
user_id: Optional[str]
action: str
changes: Optional[List[dict]]
request_metadata: Optional[dict]
sensitivity_level: str
checksum: str
created_at: datetime
user_name: Optional[str] = None
user_email: Optional[str] = None
class Config:
from_attributes = True
class AuditLogListResponse(BaseModel):
logs: List[AuditLogResponse]
total: int
offset: int
limit: int
class AuditAlertResponse(BaseModel):
id: str
audit_log_id: str
alert_type: str
recipients: List[str]
message: Optional[str]
is_acknowledged: bool
acknowledged_by: Optional[str]
acknowledged_at: Optional[datetime]
created_at: datetime
class Config:
from_attributes = True
class AuditAlertListResponse(BaseModel):
alerts: List[AuditAlertResponse]
total: int
class IntegrityCheckRequest(BaseModel):
start_date: datetime
end_date: datetime
class IntegrityCheckResponse(BaseModel):
total_checked: int
valid_count: int
invalid_count: int
invalid_records: List[str]