136 lines
4.8 KiB
TypeScript
136 lines
4.8 KiB
TypeScript
import { Body, Controller, Post } from '@nestjs/common';
|
|
import { ApiTags } from '@nestjs/swagger';
|
|
import {
|
|
GatewayInboundAuthDto,
|
|
GatewayInboundSubmitDto,
|
|
GatewayPendingDeliveryQueryDto,
|
|
GatewayDownstreamAcknowledgedDto,
|
|
GatewayDownstreamFailureType,
|
|
GatewayDownstreamSentDto,
|
|
GatewayDownstreamRecoveryStatusDto,
|
|
GatewayReceiptEventDto,
|
|
GatewaySubmitDeadLetterDto,
|
|
GatewaySubmitResultDto,
|
|
GatewayUplinkEventDto,
|
|
SendChainService,
|
|
} from './send-chain.service';
|
|
import { GatewayDownstreamConnectionEventDto, SmsConfigService } from '../sms-config/sms-config.service';
|
|
import { ProtocolLogsService, type ProtocolLogInput } from '../protocol-logs/protocol-logs.service';
|
|
|
|
@ApiTags('gateway-events')
|
|
@Controller('gateway/events')
|
|
export class GatewayEventsController {
|
|
constructor(
|
|
private readonly sendChain: SendChainService,
|
|
private readonly smsConfig: SmsConfigService,
|
|
private readonly protocolLogs: ProtocolLogsService,
|
|
) {}
|
|
|
|
@Post('submit-result')
|
|
submitResult(@Body() body: GatewaySubmitResultDto) {
|
|
return this.trackGatewayEvent('submit_resp', body, () => this.sendChain.handleSubmitResult(body));
|
|
}
|
|
|
|
@Post('receipt')
|
|
receipt(@Body() body: GatewayReceiptEventDto) {
|
|
return this.trackGatewayEvent('deliver_receipt', body, () => this.sendChain.handleReceipt(body));
|
|
}
|
|
|
|
@Post('uplink')
|
|
uplink(@Body() body: GatewayUplinkEventDto) {
|
|
return this.trackGatewayEvent('deliver_uplink', body, () => this.sendChain.handleUplink(body));
|
|
}
|
|
|
|
@Post('dead-letter')
|
|
deadLetter(@Body() body: GatewaySubmitDeadLetterDto) {
|
|
return this.sendChain.recordGatewaySubmitDeadLetter(body);
|
|
}
|
|
|
|
@Post('inbound/authenticate')
|
|
authenticateInbound(@Body() body: GatewayInboundAuthDto) {
|
|
return this.trackGatewayEvent('connect', body, () => this.sendChain.authenticateInboundApplication(body), 'client_to_platform');
|
|
}
|
|
|
|
@Post('inbound/submit')
|
|
submitInbound(@Body() body: GatewayInboundSubmitDto) {
|
|
return this.trackGatewayEvent('submit', body, () => this.sendChain.submitInboundMessage(body), 'client_to_platform');
|
|
}
|
|
|
|
@Post('inbound/connection')
|
|
inboundConnection(@Body() body: GatewayDownstreamConnectionEventDto) {
|
|
return this.smsConfig.recordDownstreamConnectionEvent(body);
|
|
}
|
|
|
|
@Post('downstream/pending')
|
|
pendingDownstream(@Body() body: GatewayPendingDeliveryQueryDto) {
|
|
return this.sendChain.listPendingDownstreamDeliveries(body);
|
|
}
|
|
|
|
@Post('downstream/delivered')
|
|
downstreamDelivered(@Body() body: { id: string }) {
|
|
return this.sendChain.markDownstreamDeliveryDelivered(body.id);
|
|
}
|
|
|
|
@Post('downstream/sent')
|
|
downstreamSent(@Body() body: GatewayDownstreamSentDto) {
|
|
return this.sendChain.markDownstreamDeliverySent(body);
|
|
}
|
|
|
|
@Post('downstream/acknowledged')
|
|
downstreamAcknowledged(@Body() body: GatewayDownstreamAcknowledgedDto) {
|
|
return this.sendChain.acknowledgeDownstreamDelivery(body);
|
|
}
|
|
|
|
@Post('downstream/failed')
|
|
downstreamFailed(@Body() body: { id: string; errorMessage?: string; failureType?: GatewayDownstreamFailureType }) {
|
|
return this.sendChain.markDownstreamDeliveryFailed(body.id, body.errorMessage, body.failureType);
|
|
}
|
|
|
|
@Post('downstream/recovery-status')
|
|
downstreamRecoveryStatus(@Body() body: GatewayDownstreamRecoveryStatusDto) {
|
|
return this.sendChain.recordGatewayDownstreamRecoveryStatus(body);
|
|
}
|
|
|
|
private async trackGatewayEvent<T>(
|
|
eventType: string,
|
|
body: object,
|
|
action: () => Promise<T> | T,
|
|
direction: ProtocolLogInput['direction'] = 'channel_to_platform',
|
|
) {
|
|
const startedAt = Date.now();
|
|
const value = body as Record<string, unknown>;
|
|
const common: Omit<ProtocolLogInput, 'status'> = {
|
|
protocol: 'cmpp',
|
|
direction,
|
|
eventType,
|
|
tenantId: value.tenantId as string,
|
|
applicationId: value.applicationId as string,
|
|
channelId: value.channelId as string,
|
|
account: (value.account ?? value.loginAccount) as string,
|
|
messageId: (value.messageId ?? value.platformMessageId) as string,
|
|
gatewayMessageId: (value.gatewayMessageId ?? value.msgId ?? value.upstreamMessageId) as string,
|
|
phone: (value.phoneNumber ?? value.srcTerminalId ?? value.destinationId) as string,
|
|
resultCode: (value.result ?? value.status ?? value.stat) as string,
|
|
detail: { sequenceId: value.sequenceId, connectionId: value.connectionId },
|
|
};
|
|
this.protocolLogs.record({ ...common, status: 'received', durationMs: 0 });
|
|
try {
|
|
const result = await action();
|
|
this.protocolLogs.record({
|
|
...common,
|
|
status: 'success',
|
|
durationMs: Date.now() - startedAt,
|
|
});
|
|
return result;
|
|
} catch (error) {
|
|
this.protocolLogs.record({
|
|
...common,
|
|
status: 'failed',
|
|
durationMs: Date.now() - startedAt,
|
|
detail: { error: error instanceof Error ? error.message : 'unknown error' },
|
|
});
|
|
throw error;
|
|
}
|
|
}
|
|
}
|