# Activity Logging API The Activity Logging API provides comprehensive audit trail and activity tracking functionality for the Mosaic Stack platform. It logs user actions, workspace changes, task/event modifications, and authentication events. ## Overview Activity logs are automatically created for: - **CRUD Operations**: Task, event, project, and workspace modifications - **Authentication Events**: Login, logout, password resets - **User Actions**: Task assignments, workspace member changes - **System Events**: Configuration updates, permission changes All activity logs are workspace-scoped and support multi-tenant isolation through Row-Level Security (RLS). ## Endpoints ### List Activity Logs ``` GET /api/activity ``` Get a paginated list of activity logs with optional filters. **Query Parameters:** | Parameter | Type | Required | Description | | ------------- | -------------- | -------- | ---------------------------------------------- | | `workspaceId` | UUID | Yes | Workspace to filter by | | `userId` | UUID | No | Filter by user who performed the action | | `action` | ActivityAction | No | Filter by action type (CREATED, UPDATED, etc.) | | `entityType` | EntityType | No | Filter by entity type (TASK, EVENT, etc.) | | `entityId` | UUID | No | Filter by specific entity | | `startDate` | ISO 8601 | No | Filter activities after this date | | `endDate` | ISO 8601 | No | Filter activities before this date | | `page` | Number | No | Page number (default: 1) | | `limit` | Number | No | Items per page (default: 50, max: 100) | **Response:** ```json { "data": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "workspaceId": "660e8400-e29b-41d4-a716-446655440001", "userId": "770e8400-e29b-41d4-a716-446655440002", "action": "CREATED", "entityType": "TASK", "entityId": "880e8400-e29b-41d4-a716-446655440003", "details": { "title": "New Task", "status": "NOT_STARTED" }, "ipAddress": "192.168.1.1", "userAgent": "Mozilla/5.0...", "createdAt": "2024-01-28T12:00:00Z", "user": { "id": "770e8400-e29b-41d4-a716-446655440002", "name": "John Doe", "email": "john@example.com" } } ], "meta": { "total": 150, "page": 1, "limit": 50, "totalPages": 3 } } ``` **Example Requests:** ```bash # Get all activities in a workspace GET /api/activity?workspaceId=660e8400-e29b-41d4-a716-446655440001 # Get activities for a specific user GET /api/activity?workspaceId=660e8400-e29b-41d4-a716-446655440001&userId=770e8400-e29b-41d4-a716-446655440002 # Get task creation events GET /api/activity?workspaceId=660e8400-e29b-41d4-a716-446655440001&action=CREATED&entityType=TASK # Get activities in date range GET /api/activity?workspaceId=660e8400-e29b-41d4-a716-446655440001&startDate=2024-01-01&endDate=2024-01-31 # Paginate results GET /api/activity?workspaceId=660e8400-e29b-41d4-a716-446655440001&page=2&limit=25 ``` --- ### Get Single Activity Log ``` GET /api/activity/:id ``` Retrieve a single activity log entry by ID. **Path Parameters:** | Parameter | Type | Required | Description | | --------- | ---- | -------- | --------------- | | `id` | UUID | Yes | Activity log ID | **Query Parameters:** | Parameter | Type | Required | Description | | ------------- | ---- | -------- | ----------------------------------------- | | `workspaceId` | UUID | Yes | Workspace ID (for multi-tenant isolation) | **Response:** ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "workspaceId": "660e8400-e29b-41d4-a716-446655440001", "userId": "770e8400-e29b-41d4-a716-446655440002", "action": "UPDATED", "entityType": "TASK", "entityId": "880e8400-e29b-41d4-a716-446655440003", "details": { "changes": { "status": "IN_PROGRESS" } }, "ipAddress": "192.168.1.1", "userAgent": "Mozilla/5.0...", "createdAt": "2024-01-28T12:00:00Z", "user": { "id": "770e8400-e29b-41d4-a716-446655440002", "name": "John Doe", "email": "john@example.com" } } ``` **Example Request:** ```bash GET /api/activity/550e8400-e29b-41d4-a716-446655440000?workspaceId=660e8400-e29b-41d4-a716-446655440001 ``` --- ### Get Entity Audit Trail ``` GET /api/activity/audit/:entityType/:entityId ``` Retrieve complete audit trail for a specific entity (task, event, project, etc.). **Path Parameters:** | Parameter | Type | Required | Description | | ------------ | ---------- | -------- | ------------------------------------------------------ | | `entityType` | EntityType | Yes | Type of entity (TASK, EVENT, PROJECT, WORKSPACE, USER) | | `entityId` | UUID | Yes | Entity ID | **Query Parameters:** | Parameter | Type | Required | Description | | ------------- | ---- | -------- | ----------------------------------------- | | `workspaceId` | UUID | Yes | Workspace ID (for multi-tenant isolation) | **Response:** Returns array of activity logs in chronological order (oldest first). ```json [ { "id": "550e8400-e29b-41d4-a716-446655440000", "workspaceId": "660e8400-e29b-41d4-a716-446655440001", "userId": "770e8400-e29b-41d4-a716-446655440002", "action": "CREATED", "entityType": "TASK", "entityId": "880e8400-e29b-41d4-a716-446655440003", "details": { "title": "New Task" }, "createdAt": "2024-01-28T10:00:00Z", "user": { "id": "770e8400-e29b-41d4-a716-446655440002", "name": "John Doe", "email": "john@example.com" } }, { "id": "660e8400-e29b-41d4-a716-446655440004", "workspaceId": "660e8400-e29b-41d4-a716-446655440001", "userId": "770e8400-e29b-41d4-a716-446655440005", "action": "UPDATED", "entityType": "TASK", "entityId": "880e8400-e29b-41d4-a716-446655440003", "details": { "changes": { "status": "IN_PROGRESS" } }, "createdAt": "2024-01-28T12:00:00Z", "user": { "id": "770e8400-e29b-41d4-a716-446655440005", "name": "Jane Smith", "email": "jane@example.com" } } ] ``` **Example Requests:** ```bash # Get audit trail for a task GET /api/activity/audit/TASK/880e8400-e29b-41d4-a716-446655440003?workspaceId=660e8400-e29b-41d4-a716-446655440001 # Get audit trail for a project GET /api/activity/audit/PROJECT/990e8400-e29b-41d4-a716-446655440006?workspaceId=660e8400-e29b-41d4-a716-446655440001 # Get audit trail for a workspace GET /api/activity/audit/WORKSPACE/660e8400-e29b-41d4-a716-446655440001?workspaceId=660e8400-e29b-41d4-a716-446655440001 ``` --- ## Enums ### ActivityAction Actions that can be logged: - `CREATED` - Entity was created - `UPDATED` - Entity was updated - `DELETED` - Entity was deleted - `COMPLETED` - Task or project was completed - `ASSIGNED` - Task was assigned to a user - `COMMENTED` - Comment was added (future use) - `LOGIN` - User logged in - `LOGOUT` - User logged out - `PASSWORD_RESET` - Password was reset - `EMAIL_VERIFIED` - Email was verified ### EntityType Types of entities that can be tracked: - `TASK` - Task entity - `EVENT` - Calendar event - `PROJECT` - Project - `WORKSPACE` - Workspace - `USER` - User profile --- ## Automatic Logging The Activity Logging system includes an interceptor that automatically logs: - **POST requests** → `CREATED` action - **PATCH/PUT requests** → `UPDATED` action - **DELETE requests** → `DELETED` action The interceptor extracts: - User information from the authenticated session - Workspace context from request - IP address and user agent from HTTP headers - Entity ID from route parameters or response --- ## Manual Logging For custom logging scenarios, use the `ActivityService` helper methods: ```typescript import { ActivityService } from "@/activity/activity.service"; @Injectable() export class TaskService { constructor(private activityService: ActivityService) {} async createTask(data, userId, workspaceId) { const task = await this.prisma.task.create({ data }); // Log task creation await this.activityService.logTaskCreated(workspaceId, userId, task.id, { title: task.title }); return task; } } ``` ### Available Helper Methods #### Task Activities - `logTaskCreated(workspaceId, userId, taskId, details?)` - `logTaskUpdated(workspaceId, userId, taskId, details?)` - `logTaskDeleted(workspaceId, userId, taskId, details?)` - `logTaskCompleted(workspaceId, userId, taskId, details?)` - `logTaskAssigned(workspaceId, userId, taskId, assigneeId)` #### Event Activities - `logEventCreated(workspaceId, userId, eventId, details?)` - `logEventUpdated(workspaceId, userId, eventId, details?)` - `logEventDeleted(workspaceId, userId, eventId, details?)` #### Project Activities - `logProjectCreated(workspaceId, userId, projectId, details?)` - `logProjectUpdated(workspaceId, userId, projectId, details?)` - `logProjectDeleted(workspaceId, userId, projectId, details?)` #### Workspace Activities - `logWorkspaceCreated(workspaceId, userId, details?)` - `logWorkspaceUpdated(workspaceId, userId, details?)` - `logWorkspaceMemberAdded(workspaceId, userId, memberId, role)` - `logWorkspaceMemberRemoved(workspaceId, userId, memberId)` #### User Activities - `logUserUpdated(workspaceId, userId, details?)` --- ## Security & Privacy ### Multi-Tenant Isolation All activity logs are scoped to workspaces using Row-Level Security (RLS). Users can only access activity logs for workspaces they belong to. ### Data Retention Activity logs are retained indefinitely by default. Consider implementing a retention policy based on: - Compliance requirements - Storage constraints - Business needs ### Sensitive Data Activity logs should NOT contain: - Passwords or authentication tokens - Credit card information - Personal health information - Other sensitive PII Store only metadata needed for audit purposes. Use the `details` field for non-sensitive context. --- ## Best Practices ### 1. Use Descriptive Details Include enough context to understand what changed: ```typescript // Good await activityService.logTaskUpdated(workspaceId, userId, taskId, { changes: { status: { from: "NOT_STARTED", to: "IN_PROGRESS" }, assignee: { from: null, to: "user-456" }, }, }); // Less useful await activityService.logTaskUpdated(workspaceId, userId, taskId); ``` ### 2. Log Business-Critical Actions Prioritize logging actions that: - Change permissions or access control - Delete data - Modify billing or subscription - Export data - Change security settings ### 3. Query Efficiently Use appropriate filters to reduce data transfer: ```typescript // Efficient - filters at database level const activities = await fetch("/api/activity?workspaceId=xxx&entityType=TASK&page=1&limit=50"); // Inefficient - transfers all data then filters const activities = await fetch("/api/activity?workspaceId=xxx"); const taskActivities = activities.filter((a) => a.entityType === "TASK"); ``` ### 4. Display User-Friendly Activity Feeds Transform activity logs into human-readable messages: ```typescript function formatActivityMessage(activity: ActivityLog) { const { user, action, entityType, details } = activity; switch (action) { case "CREATED": return `${user.name} created ${entityType.toLowerCase()} "${details.title}"`; case "UPDATED": return `${user.name} updated ${entityType.toLowerCase()}`; case "DELETED": return `${user.name} deleted ${entityType.toLowerCase()}`; default: return `${user.name} performed ${action}`; } } ``` --- ## Error Handling Activity logging failures should NOT block the primary operation. The interceptor and service methods handle errors gracefully: ```typescript try { await activityService.logActivity(data); } catch (error) { // Log error but don't throw logger.error("Failed to log activity", error); } ``` If activity logging is critical for compliance, implement synchronous validation before the operation completes. --- ## Performance Considerations ### Indexing The following indexes optimize common queries: - `workspaceId` - Filter by workspace - `workspaceId + createdAt` - Recent activities per workspace - `entityType + entityId` - Audit trail queries - `userId` - User activity history - `action` - Filter by action type ### Pagination Always use pagination for activity queries. Default limit is 50 items, maximum is 100. ### Background Processing For high-volume systems, consider: - Async activity logging with message queues - Batch inserts for multiple activities - Separate read replicas for reporting --- ## Related Documentation - [Authentication API](../2-authentication/README.md) - [API Conventions](../1-conventions/README.md) - [Database Schema](../../3-architecture/2-database/README.md)