From a63afa073df975efe709de5631b61b90783d7dc3 Mon Sep 17 00:00:00 2001 From: Zhi Date: Sun, 22 Feb 2026 09:06:37 +0000 Subject: [PATCH] feat: activity log model + API (audit trail) --- app/main.py | 44 ++++++++++++++++++++++++++++++++++++++++++ app/models/activity.py | 15 ++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 app/models/activity.py diff --git a/app/main.py b/app/main.py index 111d7b7..3bd3f25 100644 --- a/app/main.py +++ b/app/main.py @@ -253,6 +253,7 @@ def startup(): from app.core.config import Base, engine from app.models import webhook from app.models import apikey + from app.models import activity Base.metadata.create_all(bind=engine) @@ -649,3 +650,46 @@ def batch_assign(data: BatchAssign, db: Session = Depends(get_db)): db.commit() return {"updated": len(updated), "issue_ids": updated, "assignee_id": data.assignee_id} + + + +# ============ Activity Log ============ + +from app.models.activity import ActivityLog + + +class ActivityLogResponse(PydanticBaseModel): + id: int + action: str + entity_type: str + entity_id: int + user_id: int | None + details: str | None + created_at: datetime + class Config: + from_attributes = True + + +def log_activity(db: Session, action: str, entity_type: str, entity_id: int, user_id: int = None, details: str = None): + """Helper to record an activity log entry.""" + entry = ActivityLog(action=action, entity_type=entity_type, entity_id=entity_id, user_id=user_id, details=details) + db.add(entry) + db.commit() + + +@app.get("/activity", response_model=List[ActivityLogResponse]) +def list_activity( + entity_type: str = None, + entity_id: int = None, + user_id: int = None, + limit: int = 50, + db: Session = Depends(get_db) +): + query = db.query(ActivityLog) + if entity_type: + query = query.filter(ActivityLog.entity_type == entity_type) + if entity_id: + query = query.filter(ActivityLog.entity_id == entity_id) + if user_id: + query = query.filter(ActivityLog.user_id == user_id) + return query.order_by(ActivityLog.created_at.desc()).limit(limit).all() diff --git a/app/models/activity.py b/app/models/activity.py new file mode 100644 index 0000000..5d7a011 --- /dev/null +++ b/app/models/activity.py @@ -0,0 +1,15 @@ +from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey +from sqlalchemy.sql import func +from app.core.config import Base + + +class ActivityLog(Base): + __tablename__ = "activity_logs" + + id = Column(Integer, primary_key=True, index=True) + action = Column(String(50), nullable=False) # e.g. "issue.created", "comment.added" + entity_type = Column(String(50), nullable=False) # "issue", "project", "comment" + entity_id = Column(Integer, nullable=False) + user_id = Column(Integer, ForeignKey("users.id"), nullable=True) + details = Column(Text, nullable=True) # JSON string + created_at = Column(DateTime(timezone=True), server_default=func.now())