Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions backend/src/analytics/analytics.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AnalyticsService } from './analytics.service';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';

@Controller('analytics')
export class AnalyticsController {
constructor(private readonly analyticsService: AnalyticsService) {}

// Using AuthGuard if there's one, but maybe for now let's just expose it
// @UseGuards(JwtAuthGuard)
@Get('dashboard')
async getDashboard() {
return this.analyticsService.getDashboardMetrics();
}
}
17 changes: 17 additions & 0 deletions backend/src/analytics/analytics.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AnalyticsController } from './analytics.controller';
import { AnalyticsService } from './analytics.service';
import { Identity } from '../identity/identity.entity';
import { Verification } from '../verification/verification.entity';
import { SharedData } from '../data-sharing/data-sharing.entity';

@Module({
imports: [
TypeOrmModule.forFeature([Identity, Verification, SharedData]),
],
controllers: [AnalyticsController],
providers: [AnalyticsService],
exports: [AnalyticsService],
})
export class AnalyticsModule {}
71 changes: 71 additions & 0 deletions backend/src/analytics/analytics.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Identity } from '../identity/identity.entity';
import { Verification, VerificationStatus } from '../verification/verification.entity';
import { SharedData } from '../data-sharing/data-sharing.entity';

@Injectable()
export class AnalyticsService {
constructor(
@InjectRepository(Identity)
private readonly identityRepository: Repository<Identity>,
@InjectRepository(Verification)
private readonly verificationRepository: Repository<Verification>,
@InjectRepository(SharedData)
private readonly sharedDataRepository: Repository<SharedData>,
) {}

async getDashboardMetrics() {
const totalIdentities = await this.identityRepository.count();
const activeIdentities = await this.identityRepository.count({ where: { revoked: false } });
const revokedIdentities = await this.identityRepository.count({ where: { revoked: true } });

const totalVerifications = await this.verificationRepository.count();
const approvedVerifications = await this.verificationRepository.count({ where: { status: VerificationStatus.APPROVED } });
const pendingVerifications = await this.verificationRepository.count({ where: { status: VerificationStatus.PENDING } });
const rejectedVerifications = await this.verificationRepository.count({ where: { status: VerificationStatus.REJECTED } });

const totalShares = await this.sharedDataRepository.count();
const activeShares = await this.sharedDataRepository.count({ where: { isActive: true } });
const expiredShares = await this.sharedDataRepository.count({ where: { isActive: false } });

// Try to get some time-series data for the last 7 days of verifications
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);

const recentVerifications = await this.verificationRepository
.createQueryBuilder('verification')
.select("DATE(verification.createdAt)", "date")
.addSelect("COUNT(*)", "count")
.where("verification.createdAt >= :date", { date: sevenDaysAgo })
.groupBy("DATE(verification.createdAt)")
.orderBy("DATE(verification.createdAt)", "ASC")
.getRawMany();

const formattedTrend = recentVerifications.map(item => ({
date: new Date(item.date).toLocaleDateString(),
count: parseInt(item.count, 10),
}));

return {
usage: {
totalIdentities,
activeIdentities,
revokedIdentities,
},
verificationRates: {
total: totalVerifications,
approved: approvedVerifications,
pending: pendingVerifications,
rejected: rejectedVerifications,
},
sharingPatterns: {
total: totalShares,
active: activeShares,
expired: expiredShares,
},
verificationTrend: formattedTrend,
};
}
}
2 changes: 2 additions & 0 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { IndexerModule } from './indexer/indexer.module';
import { HealthModule } from './health/health.module';
import { AuditModule } from './audit/audit.module';
import { BackupModule } from './backup/backup.module';
import { AnalyticsModule } from './analytics/analytics.module';
import { PermissionsModule } from './permissions/permissions.module';

@Module({
Expand Down Expand Up @@ -73,6 +74,7 @@ import { PermissionsModule } from './permissions/permissions.module';
HealthModule,
AuditModule,
BackupModule,
AnalyticsModule,
PermissionsModule,
],
providers: [
Expand Down
Loading
Loading