dataset_operation_log_service.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. """
  2. Dataset Operation Log Service
  3. Records all user operations on datasets for audit and compliance
  4. """
  5. from datetime import datetime
  6. from enum import Enum
  7. from typing import Any, Optional
  8. from flask import request
  9. from sqlalchemy import and_, or_
  10. from extensions.ext_database import db
  11. from models.model import OperationLog
  12. class DatasetOperationType(str, Enum):
  13. """Dataset operation types"""
  14. # Dataset operations
  15. DATASET_CREATE = "dataset.create"
  16. DATASET_UPDATE = "dataset.update"
  17. DATASET_DELETE = "dataset.delete"
  18. # Document operations
  19. DOCUMENT_CREATE = "document.create"
  20. DOCUMENT_UPDATE = "document.update"
  21. DOCUMENT_DELETE = "document.delete"
  22. DOCUMENT_BATCH_DELETE = "document.batch_delete"
  23. DOCUMENT_RENAME = "document.rename"
  24. # Document status operations
  25. DOCUMENT_ENABLE = "document.enable"
  26. DOCUMENT_DISABLE = "document.disable"
  27. DOCUMENT_ARCHIVE = "document.archive"
  28. DOCUMENT_UNARCHIVE = "document.unarchive"
  29. DOCUMENT_BATCH_ENABLE = "document.batch_enable"
  30. DOCUMENT_BATCH_DISABLE = "document.batch_disable"
  31. DOCUMENT_BATCH_ARCHIVE = "document.batch_archive"
  32. DOCUMENT_BATCH_UNARCHIVE = "document.batch_unarchive"
  33. # Document schedule operations
  34. DOCUMENT_SCHEDULE_CREATE = "document.schedule.create"
  35. DOCUMENT_SCHEDULE_CANCEL = "document.schedule.cancel"
  36. DOCUMENT_SCHEDULE_EXECUTE = "document.schedule.execute"
  37. # Segment operations
  38. SEGMENT_CREATE = "segment.create"
  39. SEGMENT_UPDATE = "segment.update"
  40. SEGMENT_DELETE = "segment.delete"
  41. SEGMENT_BATCH_DELETE = "segment.batch_delete"
  42. SEGMENT_ENABLE = "segment.enable"
  43. SEGMENT_DISABLE = "segment.disable"
  44. # Metadata operations
  45. METADATA_CREATE = "metadata.create"
  46. METADATA_UPDATE = "metadata.update"
  47. METADATA_DELETE = "metadata.delete"
  48. class DatasetOperationLogService:
  49. """Service for logging dataset operations"""
  50. @staticmethod
  51. def log_operation(
  52. tenant_id: str,
  53. account_id: str,
  54. action: DatasetOperationType,
  55. dataset_id: str,
  56. resource_type: str, # 'dataset', 'document', 'segment'
  57. resource_id: str,
  58. changes: Optional[dict] = None,
  59. metadata: Optional[dict] = None,
  60. ) -> OperationLog:
  61. """
  62. Record a dataset operation
  63. Args:
  64. tenant_id: Tenant ID
  65. account_id: Account ID of the operator
  66. action: Operation type
  67. dataset_id: Dataset ID
  68. resource_type: Resource type
  69. resource_id: Resource ID
  70. changes: Change details (e.g., {"old": {...}, "new": {...}})
  71. metadata: Additional metadata
  72. Returns:
  73. Created OperationLog instance
  74. """
  75. content = {
  76. "dataset_id": dataset_id,
  77. "resource_type": resource_type,
  78. "resource_id": resource_id,
  79. "action": action.value,
  80. "timestamp": datetime.utcnow().isoformat(),
  81. }
  82. if changes:
  83. content["changes"] = changes
  84. if metadata:
  85. content["metadata"] = metadata
  86. log = OperationLog(
  87. tenant_id=tenant_id,
  88. account_id=account_id,
  89. action=action.value,
  90. content=content,
  91. created_ip=request.remote_addr or "0.0.0.0" if request else "0.0.0.0",
  92. )
  93. db.session.add(log)
  94. db.session.commit()
  95. return log
  96. @staticmethod
  97. def get_dataset_operation_logs(
  98. tenant_id: str,
  99. dataset_id: Optional[str] = None,
  100. resource_type: Optional[str] = None,
  101. resource_id: Optional[str] = None,
  102. action: Optional[DatasetOperationType] = None,
  103. account_id: Optional[str] = None,
  104. start_date: Optional[datetime] = None,
  105. end_date: Optional[datetime] = None,
  106. page: int = 1,
  107. per_page: int = 20,
  108. ) -> dict[str, Any]:
  109. """
  110. Query dataset operation logs
  111. Args:
  112. tenant_id: Tenant ID
  113. dataset_id: Filter by dataset ID
  114. resource_type: Filter by resource type
  115. resource_id: Filter by resource ID
  116. action: Filter by action type
  117. account_id: Filter by account ID
  118. start_date: Filter by start date
  119. end_date: Filter by end date
  120. page: Page number
  121. per_page: Items per page
  122. Returns:
  123. Dict with logs and pagination info
  124. """
  125. query = db.session.query(OperationLog).filter(
  126. OperationLog.tenant_id == tenant_id,
  127. or_(
  128. OperationLog.action.like("dataset.%"),
  129. OperationLog.action.like("document.%"),
  130. OperationLog.action.like("segment.%"),
  131. OperationLog.action.like("metadata.%"),
  132. ),
  133. )
  134. if dataset_id:
  135. query = query.filter(OperationLog.content["dataset_id"].as_string() == dataset_id)
  136. if resource_type:
  137. query = query.filter(OperationLog.content["resource_type"].as_string() == resource_type)
  138. if resource_id:
  139. query = query.filter(OperationLog.content["resource_id"].as_string() == resource_id)
  140. if action:
  141. query = query.filter(OperationLog.action == action.value)
  142. if account_id:
  143. query = query.filter(OperationLog.account_id == account_id)
  144. if start_date:
  145. query = query.filter(OperationLog.created_at >= start_date)
  146. if end_date:
  147. query = query.filter(OperationLog.created_at <= end_date)
  148. query = query.order_by(OperationLog.created_at.desc())
  149. paginated = query.paginate(page=page, per_page=per_page, error_out=False)
  150. return {
  151. "logs": paginated.items,
  152. "total": paginated.total,
  153. "page": page,
  154. "per_page": per_page,
  155. "pages": paginated.pages,
  156. }