527 lines
16 KiB
TypeScript
527 lines
16 KiB
TypeScript
import { Body, Controller, Get, Param, Post, Query, Res } from '@nestjs/common';
|
|
import { ApiTags } from '@nestjs/swagger';
|
|
import { CurrentSessionUserId } from '../auth/current-session-user.decorator';
|
|
import { RequireRecentAuthentication } from '../auth/require-recent-authentication.decorator';
|
|
import { SendChainService } from '../send-chain/send-chain.service';
|
|
import { OperationsService } from './operations.service';
|
|
import { ProtocolLogsService } from '../protocol-logs/protocol-logs.service';
|
|
|
|
type DownloadResponse = {
|
|
setHeader(name: string, value: number | string): void;
|
|
send(content: string | Buffer): void;
|
|
};
|
|
|
|
@ApiTags('operations')
|
|
@Controller('admin/operations')
|
|
export class AdminOperationsController {
|
|
constructor(
|
|
private readonly operations: OperationsService,
|
|
private readonly sendChain: SendChainService,
|
|
) {}
|
|
|
|
@Get('monitor')
|
|
monitor(@Query('tenantId') tenantId?: string, @Query('channelId') channelId?: string) {
|
|
return this.operations.monitor({ tenantId, channelId });
|
|
}
|
|
|
|
@Get('task-progress')
|
|
taskProgress(@Query('tenantId') tenantId?: string, @Query('status') status?: string) {
|
|
return this.operations.listBatchTasks({ tenantId, status });
|
|
}
|
|
|
|
@Get('messages')
|
|
listMessages(
|
|
@Query('tenantId') tenantId?: string,
|
|
@Query('applicationId') applicationId?: string,
|
|
@Query('channelId') channelId?: string,
|
|
@Query('taskId') taskId?: string,
|
|
@Query('messageId') messageId?: string,
|
|
@Query('phoneNumber') phoneNumber?: string,
|
|
@Query('contentKeyword') contentKeyword?: string,
|
|
@Query('channelKeyword') channelKeyword?: string,
|
|
@Query('carrier') carrier?: string,
|
|
@Query('queuedAtFrom') queuedAtFrom?: string,
|
|
@Query('queuedAtTo') queuedAtTo?: string,
|
|
@Query('status') status?: string,
|
|
@Query('hasDrainage') hasDrainage?: string,
|
|
@Query('page') page?: string,
|
|
@Query('pageSize') pageSize?: string,
|
|
@Query('monitorSnapshotId') monitorSnapshotId?: string,
|
|
) {
|
|
return this.operations.listMessagesPage({
|
|
monitorSnapshotId,
|
|
tenantId,
|
|
applicationId,
|
|
channelId,
|
|
taskId,
|
|
messageId,
|
|
phoneNumber,
|
|
contentKeyword,
|
|
channelKeyword,
|
|
carrier,
|
|
queuedAtFrom,
|
|
queuedAtTo,
|
|
status,
|
|
hasDrainage,
|
|
page: Number(page),
|
|
pageSize: Number(pageSize),
|
|
});
|
|
}
|
|
|
|
@Get('messages/export')
|
|
async exportMessages(
|
|
@Query('tenantId') tenantId: string | undefined,
|
|
@Query('applicationId') applicationId: string | undefined,
|
|
@Query('channelId') channelId: string | undefined,
|
|
@Query('phoneNumber') phoneNumber: string | undefined,
|
|
@Query('contentKeyword') contentKeyword: string | undefined,
|
|
@Query('channelKeyword') channelKeyword: string | undefined,
|
|
@Query('carrier') carrier: string | undefined,
|
|
@Query('queuedAtFrom') queuedAtFrom: string | undefined,
|
|
@Query('queuedAtTo') queuedAtTo: string | undefined,
|
|
@Query('status') status: string | undefined,
|
|
@Query('hasDrainage') hasDrainage: string | undefined,
|
|
@Res() response: DownloadResponse,
|
|
@Query('monitorSnapshotId') monitorSnapshotId?: string,
|
|
) {
|
|
const exported = await this.operations.exportMessages({
|
|
monitorSnapshotId,
|
|
tenantId,
|
|
applicationId,
|
|
channelId,
|
|
phoneNumber,
|
|
contentKeyword,
|
|
channelKeyword,
|
|
carrier,
|
|
queuedAtFrom,
|
|
queuedAtTo,
|
|
status,
|
|
hasDrainage,
|
|
});
|
|
response.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
|
response.setHeader('Content-Disposition', `attachment; filename*=UTF-8''${encodeURIComponent(exported.fileName)}`);
|
|
response.send(`\uFEFF${exported.content}`);
|
|
}
|
|
|
|
@Get('messages/:id')
|
|
getMessage(@Param('id') id: string) {
|
|
return this.operations.getMessage(id);
|
|
}
|
|
|
|
@Get('message-segment-audits')
|
|
messageSegmentAudits(@Query('messageId') messageId?: string, @Query('messageRecordId') messageRecordId?: string) {
|
|
return this.operations.listMessageSegmentAudits({ messageId, messageRecordId });
|
|
}
|
|
|
|
@Get('uplink-messages')
|
|
listUplinkMessages(
|
|
@Query('tenantId') tenantId?: string,
|
|
@Query('channelId') channelId?: string,
|
|
@Query('phoneNumber') phoneNumber?: string,
|
|
@Query('keyword') keyword?: string,
|
|
@Query('startTime') startTime?: string,
|
|
@Query('endTime') endTime?: string,
|
|
@Query('page') page?: string,
|
|
@Query('pageSize') pageSize?: string,
|
|
) {
|
|
return page || pageSize
|
|
? this.operations.listUplinkMessagesPage({
|
|
tenantId,
|
|
channelId,
|
|
phoneNumber,
|
|
keyword,
|
|
startTime,
|
|
endTime,
|
|
page: Number(page),
|
|
pageSize: Number(pageSize),
|
|
})
|
|
: this.operations.listUplinkMessages({ tenantId, channelId, phoneNumber, keyword, startTime, endTime });
|
|
}
|
|
|
|
@Post('uplink-messages/:id/claim')
|
|
claimUplinkMatchCandidate(@Param('id') id: string, @Body() body: { candidateId?: string; operatorId?: string }) {
|
|
return this.sendChain.claimUplinkMatchCandidate(id, String(body.candidateId ?? ''), body.operatorId);
|
|
}
|
|
|
|
@Get('dashboard')
|
|
dashboard(@Query('tenantId') tenantId?: string) {
|
|
return this.operations.dashboard({ tenantId });
|
|
}
|
|
|
|
@Get('pending-audits')
|
|
pendingAudits(@Query('tenantId') tenantId?: string) {
|
|
return this.operations.pendingAudits(tenantId);
|
|
}
|
|
|
|
@Get('dashboard/statistics')
|
|
dashboardStatistics(@Query('tenantId') tenantId?: string) {
|
|
return this.operations.dashboard({ tenantId });
|
|
}
|
|
|
|
@Get('statistics')
|
|
statistics(@Query('tenantId') tenantId?: string, @Query('groupBy') groupBy?: string) {
|
|
return this.operations.statistics({ tenantId, groupBy });
|
|
}
|
|
|
|
@Get('send-quality')
|
|
sendQuality(@Query('date') date?: string) {
|
|
return this.operations.sendQuality(date);
|
|
}
|
|
|
|
@Get('signature-quality')
|
|
signatureQuality(
|
|
@Query('date') date?: string,
|
|
@Query('keyword') keyword?: string,
|
|
@Query('page') page?: string,
|
|
@Query('pageSize') pageSize?: string,
|
|
) {
|
|
return this.operations.signatureQuality({
|
|
date,
|
|
keyword,
|
|
page: page === undefined ? 1 : Number(page),
|
|
pageSize: pageSize === undefined ? 25 : Number(pageSize),
|
|
});
|
|
}
|
|
|
|
@Get('audit-logs')
|
|
auditLogs(
|
|
@Query('tenantId') tenantId?: string,
|
|
@Query('userId') userId?: string,
|
|
@Query('page') page?: string,
|
|
@Query('pageSize') pageSize?: string,
|
|
) {
|
|
return this.operations.auditLogs({ tenantId, userId, page: Number(page), pageSize: Number(pageSize) });
|
|
}
|
|
|
|
@Get('audit-summary')
|
|
auditSummary(@Query('tenantId') tenantId?: string) {
|
|
return this.operations.auditSummary({ tenantId });
|
|
}
|
|
|
|
@Get('trace')
|
|
trace(
|
|
@Query('tenantId') tenantId?: string,
|
|
@Query('applicationId') applicationId?: string,
|
|
@Query('channelId') channelId?: string,
|
|
@Query('taskId') taskId?: string,
|
|
@Query('phoneNumber') phoneNumber?: string,
|
|
@Query('messageId') messageId?: string,
|
|
) {
|
|
return this.operations.trace({ tenantId, applicationId, channelId, taskId, phoneNumber, messageId });
|
|
}
|
|
|
|
@Get('reconciliation')
|
|
reconciliation(@Query('tenantId') tenantId?: string, @Query('taskId') taskId?: string) {
|
|
return this.operations.reconciliation({ tenantId, taskId });
|
|
}
|
|
|
|
@Get('gateway-submit-dead-letters')
|
|
gatewaySubmitDeadLetters(
|
|
@Query('tenantId') tenantId?: string,
|
|
@Query('applicationId') applicationId?: string,
|
|
@Query('channelId') channelId?: string,
|
|
@Query('status') status?: string,
|
|
@Query('keyword') keyword?: string,
|
|
@Query('page') page?: string,
|
|
@Query('pageSize') pageSize?: string,
|
|
) {
|
|
return this.operations.listGatewaySubmitDeadLetters({
|
|
tenantId,
|
|
applicationId,
|
|
channelId,
|
|
status,
|
|
keyword,
|
|
page: Number(page),
|
|
pageSize: Number(pageSize),
|
|
});
|
|
}
|
|
|
|
@Post('gateway-submit-dead-letters/:id/requeue')
|
|
@RequireRecentAuthentication()
|
|
requeueGatewaySubmitDeadLetter(
|
|
@Param('id') id: string,
|
|
@Body() body: { confirmedNotSubmitted?: boolean; reason?: string },
|
|
@CurrentSessionUserId() operatorId?: string,
|
|
) {
|
|
return this.sendChain.requeueGatewaySubmitDeadLetter(id, { ...body, operatorId });
|
|
}
|
|
|
|
@Post('gateway-submit-dead-letters/:id/resolve')
|
|
resolveGatewaySubmitDeadLetter(@Param('id') id: string, @CurrentSessionUserId() operatorId?: string) {
|
|
return this.sendChain.resolveGatewaySubmitDeadLetter(id, operatorId);
|
|
}
|
|
|
|
@Get('receipt-anomalies')
|
|
receiptAnomalies(
|
|
@Query('tenantId') tenantId?: string,
|
|
@Query('applicationId') applicationId?: string,
|
|
@Query('channelId') channelId?: string,
|
|
@Query('anomalyType') anomalyType?: string,
|
|
@Query('status') status?: string,
|
|
@Query('keyword') keyword?: string,
|
|
@Query('page') page?: string,
|
|
@Query('pageSize') pageSize?: string,
|
|
) {
|
|
return this.operations.listReceiptAnomalies({
|
|
tenantId,
|
|
applicationId,
|
|
channelId,
|
|
anomalyType,
|
|
status,
|
|
keyword,
|
|
page: Number(page),
|
|
pageSize: Number(pageSize),
|
|
});
|
|
}
|
|
|
|
@Get('downstream-deliveries')
|
|
downstreamDeliveries(
|
|
@Query('tenantId') tenantId?: string,
|
|
@Query('applicationId') applicationId?: string,
|
|
@Query('deliveryType') deliveryType?: string,
|
|
@Query('createdAtFrom') createdAtFrom?: string,
|
|
@Query('createdAtTo') createdAtTo?: string,
|
|
@Query('status') status?: string,
|
|
@Query('keyword') keyword?: string,
|
|
@Query('page') page?: string,
|
|
@Query('pageSize') pageSize?: string,
|
|
) {
|
|
return this.operations.listDownstreamDeliveries({
|
|
tenantId,
|
|
applicationId,
|
|
deliveryType,
|
|
createdAtFrom,
|
|
createdAtTo,
|
|
status,
|
|
keyword,
|
|
page: Number(page),
|
|
pageSize: Number(pageSize),
|
|
});
|
|
}
|
|
|
|
@Get('downstream-deliveries/dashboard')
|
|
downstreamDeliveryDashboard(
|
|
@Query('tenantId') tenantId?: string,
|
|
@Query('applicationId') applicationId?: string,
|
|
@Query('deliveryType') deliveryType?: string,
|
|
@Query('createdAtFrom') createdAtFrom?: string,
|
|
@Query('createdAtTo') createdAtTo?: string,
|
|
) {
|
|
return this.operations.downstreamDeliveryDashboard({
|
|
tenantId,
|
|
applicationId,
|
|
deliveryType,
|
|
createdAtFrom,
|
|
createdAtTo,
|
|
});
|
|
}
|
|
|
|
@Get('downstream-recovery-statuses')
|
|
downstreamRecoveryStatuses(
|
|
@Query('tenantId') tenantId?: string,
|
|
@Query('applicationId') applicationId?: string,
|
|
@Query('state') state?: string,
|
|
@Query('failureCategory') failureCategory?: string,
|
|
@Query('keyword') keyword?: string,
|
|
@Query('updatedAtFrom') updatedAtFrom?: string,
|
|
@Query('updatedAtTo') updatedAtTo?: string,
|
|
@Query('page') page?: string,
|
|
@Query('pageSize') pageSize?: string,
|
|
) {
|
|
return this.operations.listDownstreamRecoveryStatuses({
|
|
tenantId,
|
|
applicationId,
|
|
state,
|
|
failureCategory,
|
|
keyword,
|
|
updatedAtFrom,
|
|
updatedAtTo,
|
|
page: Number(page),
|
|
pageSize: Number(pageSize),
|
|
});
|
|
}
|
|
|
|
@Get('downstream-recovery-statuses/export')
|
|
async exportDownstreamRecoveryStatuses(
|
|
@Query('tenantId') tenantId: string | undefined,
|
|
@Query('applicationId') applicationId: string | undefined,
|
|
@Query('state') state: string | undefined,
|
|
@Query('failureCategory') failureCategory: string | undefined,
|
|
@Query('keyword') keyword: string | undefined,
|
|
@Query('updatedAtFrom') updatedAtFrom: string | undefined,
|
|
@Query('updatedAtTo') updatedAtTo: string | undefined,
|
|
@Res() response: DownloadResponse,
|
|
) {
|
|
const exported = await this.operations.exportDownstreamRecoveryStatuses({
|
|
tenantId,
|
|
applicationId,
|
|
state,
|
|
failureCategory,
|
|
keyword,
|
|
updatedAtFrom,
|
|
updatedAtTo,
|
|
});
|
|
response.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
|
response.setHeader('Content-Disposition', `attachment; filename*=UTF-8''${encodeURIComponent(exported.fileName)}`);
|
|
response.send(`\uFEFF${exported.content}`);
|
|
}
|
|
|
|
@Get('downstream-recovery-statuses/:id')
|
|
downstreamRecoveryStatusDetail(@Param('id') id: string) {
|
|
return this.operations.getDownstreamRecoveryStatus(id);
|
|
}
|
|
|
|
@Post('downstream-deliveries/:id/requeue')
|
|
requeueDownstreamDelivery(@Param('id') id: string) {
|
|
return this.sendChain.requeueDownstreamDelivery(id);
|
|
}
|
|
|
|
@Post('downstream-deliveries/requeue')
|
|
batchRequeueDownstreamDeliveries(@Body() body: { ids?: string[] }) {
|
|
return this.sendChain.batchRequeueDownstreamDeliveries(body.ids ?? []);
|
|
}
|
|
|
|
@Post('downstream-requeue-tasks/preview')
|
|
previewDownstreamRequeueTask(
|
|
@Body() body: { filter?: Record<string, string | undefined> },
|
|
@CurrentSessionUserId() operatorId?: string,
|
|
) {
|
|
return this.sendChain.previewDownstreamRequeueTask(body.filter ?? {}, operatorId);
|
|
}
|
|
|
|
@Post('downstream-requeue-tasks')
|
|
createDownstreamRequeueTask(
|
|
@Body() body: { previewToken?: string; reason?: string; ratePerSecond?: number; consecutiveFailureLimit?: number },
|
|
@CurrentSessionUserId() operatorId?: string,
|
|
) {
|
|
return this.sendChain.createDownstreamRequeueTask(
|
|
{
|
|
previewToken: body.previewToken ?? '',
|
|
reason: body.reason ?? '',
|
|
ratePerSecond: body.ratePerSecond,
|
|
consecutiveFailureLimit: body.consecutiveFailureLimit,
|
|
},
|
|
operatorId,
|
|
);
|
|
}
|
|
|
|
@Get('downstream-requeue-tasks')
|
|
listDownstreamRequeueTasks(
|
|
@Query('status') status?: string,
|
|
@Query('page') page?: string,
|
|
@Query('pageSize') pageSize?: string,
|
|
) {
|
|
return this.sendChain.listDownstreamRequeueTasks({ status, page: Number(page), pageSize: Number(pageSize) });
|
|
}
|
|
|
|
@Get('downstream-requeue-tasks/:id')
|
|
getDownstreamRequeueTask(@Param('id') id: string) {
|
|
return this.sendChain.getDownstreamRequeueTask(id);
|
|
}
|
|
|
|
@Get('downstream-requeue-tasks/:id/items')
|
|
listDownstreamRequeueTaskItems(
|
|
@Param('id') id: string,
|
|
@Query('status') status?: string,
|
|
@Query('keyword') keyword?: string,
|
|
@Query('page') page?: string,
|
|
@Query('pageSize') pageSize?: string,
|
|
) {
|
|
return this.sendChain.listDownstreamRequeueTaskItems(id, {
|
|
status,
|
|
keyword,
|
|
page: Number(page),
|
|
pageSize: Number(pageSize),
|
|
});
|
|
}
|
|
|
|
@Post('downstream-requeue-tasks/:id/:action')
|
|
changeDownstreamRequeueTaskStatus(
|
|
@Param('id') id: string,
|
|
@Param('action') action: 'pause' | 'resume' | 'terminate',
|
|
@CurrentSessionUserId() operatorId?: string,
|
|
) {
|
|
return this.sendChain.changeDownstreamRequeueTaskStatus(id, action, operatorId);
|
|
}
|
|
}
|
|
|
|
@ApiTags('admin-system-logs')
|
|
@Controller('admin/system-logs')
|
|
export class AdminSystemLogsController {
|
|
constructor(
|
|
private readonly operations: OperationsService,
|
|
private readonly protocolLogs: ProtocolLogsService,
|
|
) {}
|
|
|
|
@Get('protocol-interactions')
|
|
protocolInteractions(
|
|
@Query('protocol') protocol?: string,
|
|
@Query('direction') direction?: string,
|
|
@Query('eventType') eventType?: string,
|
|
@Query('status') status?: string,
|
|
@Query('keyword') keyword?: string,
|
|
@Query('range') range?: string,
|
|
@Query('createdAtFrom') createdAtFrom?: string,
|
|
@Query('createdAtTo') createdAtTo?: string,
|
|
@Query('page') page?: string,
|
|
@Query('pageSize') pageSize?: string,
|
|
) {
|
|
return this.protocolLogs.list({
|
|
protocol,
|
|
direction,
|
|
eventType,
|
|
status,
|
|
keyword,
|
|
range,
|
|
createdAtFrom,
|
|
createdAtTo,
|
|
page: Number(page),
|
|
pageSize: Number(pageSize),
|
|
});
|
|
}
|
|
|
|
@Get()
|
|
list(
|
|
@Query('tenantId') tenantId?: string,
|
|
@Query('userId') userId?: string,
|
|
@Query('keyword') keyword?: string,
|
|
@Query('level') level?: string,
|
|
@Query('module') module?: string,
|
|
@Query('range') range?: string,
|
|
@Query('createdAtFrom') createdAtFrom?: string,
|
|
@Query('createdAtTo') createdAtTo?: string,
|
|
@Query('page') page?: string,
|
|
@Query('pageSize') pageSize?: string,
|
|
) {
|
|
return this.operations.systemLogs({
|
|
tenantId,
|
|
userId,
|
|
keyword,
|
|
level,
|
|
module,
|
|
range,
|
|
createdAtFrom,
|
|
createdAtTo,
|
|
page: Number(page),
|
|
pageSize: Number(pageSize),
|
|
});
|
|
}
|
|
|
|
@Post('exports')
|
|
export(
|
|
@Body()
|
|
body: {
|
|
tenantId?: string;
|
|
userId?: string;
|
|
keyword?: string;
|
|
level?: string;
|
|
module?: string;
|
|
range?: string;
|
|
createdAtFrom?: string;
|
|
createdAtTo?: string;
|
|
},
|
|
) {
|
|
return this.operations.exportSystemLogs(body);
|
|
}
|
|
}
|