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
32 changes: 32 additions & 0 deletions backend/src/admin/admin.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Controller, Get, Query, UseGuards } from '@nestjs/common';
import { AdminService } from './admin.service';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { RolesGuard } from '../auth/guards/roles.guard';
import { Roles } from '../auth/guards/roles.decorator';

@Controller('admin')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
export class AdminController {
constructor(private readonly adminService: AdminService) {}

@Get('stats')
async getStats() {
return this.adminService.getStats();
}

@Get('documents')
async getDocuments(
@Query('page') page?: number,
@Query('limit') limit?: number,
@Query('status') status?: string,
@Query('search') search?: string,
) {
return this.adminService.getDocuments(page, limit, status, search);
}

@Get('queue/stats')
async getQueueStats() {
return this.adminService.getQueueStats();
}
}
21 changes: 21 additions & 0 deletions backend/src/admin/admin.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AdminController } from './admin.controller';
import { AdminService } from './admin.service';
import { UsersModule } from '../users/users.module';
import { DocumentsModule } from '../documents/documents.module';
import { QueueModule } from '../queue/queue.module';
import { User } from '../users/entities/user.entity';
import { Document } from '../documents/entities/document.entity';

@Module({
imports: [
TypeOrmModule.forFeature([User, Document]),
UsersModule,
DocumentsModule,
QueueModule,
],
controllers: [AdminController],
providers: [AdminService],
})
export class AdminModule {}
65 changes: 65 additions & 0 deletions backend/src/admin/admin.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Like } from 'typeorm';
import { User } from '../users/entities/user.entity';
import { Document, DocumentStatus } from '../documents/entities/document.entity';
import { QueueService } from '../queue/queue.service';

@Injectable()
export class AdminService {
constructor(
@InjectRepository(User) private readonly userRepository: Repository<User>,
@InjectRepository(Document) private readonly documentRepository: Repository<Document>,
private readonly queueService: QueueService,
) {}

async getStats() {
const totalUsers = await this.userRepository.count();
const totalDocuments = await this.documentRepository.count();
const verifiedDocuments = await this.documentRepository.count({ where: { status: DocumentStatus.VERIFIED } });
const flaggedDocuments = await this.documentRepository.count({ where: { status: DocumentStatus.FLAGGED } });
const rejectedDocuments = await this.documentRepository.count({ where: { status: DocumentStatus.REJECTED } });
const verificationRate = totalDocuments > 0
? Math.round((verifiedDocuments / totalDocuments) * 100)
: 0;

return {
totalUsers,
totalDocuments,
verifiedDocuments,
flaggedDocuments,
rejectedDocuments,
verificationRate,
};
}

async getDocuments(
page = 1,
limit = 20,
status?: string,
search?: string,
) {
const where: any = {};
if (status) where.status = status;
if (search) where.title = Like(`%${search}%`);

const [data, total] = await this.documentRepository.findAndCount({
where,
order: { createdAt: 'DESC' },
skip: (page - 1) * limit,
take: limit,
});

return {
data,
total,
page,
limit,
totalPages: Math.ceil(total / limit),
};
}

async getQueueStats() {
return this.queueService.getQueueStats();
}
}
2 changes: 2 additions & 0 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { WinstonModule } from 'nest-winston';

import { AdminModule } from './admin/admin.module';
import { ApprovalModule } from './approval/approval.module';
import { AdminVerificationsModule } from './admin-verifications/admin-verifications.module';
import { AppController } from './app.controller';
Expand Down Expand Up @@ -58,6 +59,7 @@ import { ConfigValidationSchema } from './config/config.validation';
synchronize: true,
}),
}),
AdminModule,
I18nModule,
ApprovalModule,
AuditModule,
Expand Down
7 changes: 7 additions & 0 deletions backend/src/queue/document.processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ export class DocumentProcessor implements OnModuleDestroy {
this.queueService.queueName,
async (job) => {
if (job.name === 'analyze') {
await job.updateProgress(10);
await this.riskService.assessDocument(job.data.documentId);
await job.updateProgress(50);
await this.documentsService.updateStatus(job.data.documentId, DocumentStatus.ANALYZING);
await job.updateProgress(100);
await this.documentsService.updateStatus(
job.data.documentId,
DocumentStatus.ANALYZING,
Expand All @@ -42,7 +47,9 @@ export class DocumentProcessor implements OnModuleDestroy {
return;
}
if (job.name === 'anchor') {
await job.updateProgress(10);
await this.handleAnchor(job.data.documentId);
await job.updateProgress(100);
}
},
{ connection },
Expand Down
11 changes: 11 additions & 0 deletions backend/src/queue/queue.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ export class QueueService implements OnModuleDestroy {
return this.queue.add('anchor', { documentId });
}

async getQueueStats() {
const counts = await this.queue.getJobCounts();
return {
waiting: counts.waiting || 0,
active: counts.active || 0,
completed: counts.completed || 0,
failed: counts.failed || 0,
delayed: counts.delayed || 0,
};
}

async onModuleDestroy(): Promise<void> {
await this.queue.close();
}
Expand Down
Loading