diff --git a/backend/src/activity-tracker/entities/activity.entity.ts b/backend/src/activity-tracker/entities/activity.entity.ts index 6a9ab0f..d4cc88e 100644 --- a/backend/src/activity-tracker/entities/activity.entity.ts +++ b/backend/src/activity-tracker/entities/activity.entity.ts @@ -11,6 +11,15 @@ export class Activity { @Column() actionType: string; + @Column({ nullable: true }) + resourceType: string | null; + + @Column({ nullable: true }) + resourceId: string | null; + + @Column({ type: 'jsonb', nullable: true }) + metadata: Record | null; + @Column({ type: 'timestamptz', default: () => 'CURRENT_TIMESTAMP' }) timestamp: Date; } diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index e8f565b..87f1f4d 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -10,7 +10,7 @@ import { buildWinstonOptions } from './common/logger.config'; import { LoggerMiddleware } from './common/middleware/logger.middleware'; import { DocumentsModule } from './documents/documents.module'; import { MailModule } from './mail/mail.module'; -import { HealthModule } from './module/health/health.module'; +import { UserActivityModule } from './module/user-activity/user-activity.module'; import { QueueModule } from './queue/queue.module'; import { RiskAssessmentModule } from './risk-assessment/risk-assessment.module'; import { StellarModule } from './stellar/stellar.module'; @@ -52,7 +52,7 @@ import { ConfigValidationSchema } from './config/config.validation'; UsersModule, AuthModule, DocumentsModule, - HealthModule, + UserActivityModule, RiskAssessmentModule, StellarModule, VerificationModule, diff --git a/backend/src/module/user-activity/user-activity-dashboard.service.ts b/backend/src/module/user-activity/user-activity-dashboard.service.ts new file mode 100644 index 0000000..0fa6e50 --- /dev/null +++ b/backend/src/module/user-activity/user-activity-dashboard.service.ts @@ -0,0 +1,48 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { Activity } from '../../activity-tracker/entities/activity.entity'; + +@Injectable() +export class UserActivityDashboardService { + constructor( + @InjectRepository(Activity) + private readonly activityRepository: Repository, + ) {} + + async findRecentActivity( + userId: string, + startDate?: string, + endDate?: string, + ) { + const query = this.activityRepository + .createQueryBuilder('activity') + .where('activity.userId = :userId', { userId }) + .orderBy('activity.timestamp', 'DESC') + .take(50); + + if (startDate) { + query.andWhere('activity.timestamp >= :startDate', { + startDate: new Date(startDate), + }); + } + + if (endDate) { + query.andWhere('activity.timestamp <= :endDate', { + endDate: new Date(endDate), + }); + } + + const activities = await query.getMany(); + + return activities.map((activity) => ({ + id: activity.id, + actionType: activity.actionType, + resourceType: activity.resourceType, + resourceId: activity.resourceId, + metadata: activity.metadata ?? {}, + timestamp: activity.timestamp, + })); + } +} diff --git a/backend/src/module/user-activity/user-activity.controller.ts b/backend/src/module/user-activity/user-activity.controller.ts new file mode 100644 index 0000000..c4d2e80 --- /dev/null +++ b/backend/src/module/user-activity/user-activity.controller.ts @@ -0,0 +1,49 @@ +import { + BadRequestException, + Controller, + Get, + Query, + Req, + UseGuards, +} from '@nestjs/common'; + +import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard'; +import { User } from '../../users/entities/user.entity'; +import { UserActivityDashboardService } from './user-activity-dashboard.service'; + +@Controller('module/users') +@UseGuards(JwtAuthGuard) +export class UserActivityController { + constructor( + private readonly userActivityDashboardService: UserActivityDashboardService, + ) {} + + @Get('me/activity') + async getMyActivity( + @Req() req: { user: User }, + @Query('startDate') startDate?: string, + @Query('endDate') endDate?: string, + ) { + if (startDate !== undefined && !startDate.trim()) { + throw new BadRequestException('startDate cannot be empty'); + } + + if (endDate !== undefined && !endDate.trim()) { + throw new BadRequestException('endDate cannot be empty'); + } + + if (startDate && Number.isNaN(new Date(startDate).getTime())) { + throw new BadRequestException('startDate must be a valid date'); + } + + if (endDate && Number.isNaN(new Date(endDate).getTime())) { + throw new BadRequestException('endDate must be a valid date'); + } + + return this.userActivityDashboardService.findRecentActivity( + req.user.id, + startDate, + endDate, + ); + } +} diff --git a/backend/src/module/user-activity/user-activity.module.ts b/backend/src/module/user-activity/user-activity.module.ts new file mode 100644 index 0000000..a692e8f --- /dev/null +++ b/backend/src/module/user-activity/user-activity.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { Activity } from '../../activity-tracker/entities/activity.entity'; +import { UserActivityController } from './user-activity.controller'; +import { UserActivityDashboardService } from './user-activity-dashboard.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([Activity])], + controllers: [UserActivityController], + providers: [UserActivityDashboardService], +}) +export class UserActivityModule {}