fix: connect operations pages to real backend
This commit is contained in:
@@ -57,16 +57,56 @@ export interface TemplateListQuery {
|
||||
keyword?: string;
|
||||
}
|
||||
|
||||
export interface ApplicationListQuery {
|
||||
tenantId?: string;
|
||||
keyword?: string;
|
||||
includeConnections?: boolean;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class SmsConfigService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
listApplications(tenantId?: string) {
|
||||
async listApplications(queryOrTenantId?: string | ApplicationListQuery) {
|
||||
const query = typeof queryOrTenantId === 'string' ? { tenantId: queryOrTenantId } : queryOrTenantId ?? {};
|
||||
return this.prisma.smsApplication.findMany({
|
||||
where: tenantId ? { tenantId } : undefined,
|
||||
include: { ipAllowlist: true },
|
||||
where: {
|
||||
tenantId: query.tenantId,
|
||||
OR: query.keyword ? [
|
||||
{ name: { contains: query.keyword } },
|
||||
{ tenant: { name: { contains: query.keyword } } },
|
||||
] : undefined,
|
||||
},
|
||||
include: {
|
||||
tenant: true,
|
||||
ipAllowlist: true,
|
||||
messageRecords: { where: { queuedAt: { gte: startOfToday() } }, take: 1000 },
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 100,
|
||||
}).then(async (applications) => {
|
||||
if (!query.includeConnections) {
|
||||
return applications;
|
||||
}
|
||||
const tenantIds = [...new Set(applications.map((application) => application.tenantId))];
|
||||
const connections = await this.prisma.cmppConnectionState.findMany({
|
||||
where: { tenantId: { in: tenantIds } },
|
||||
include: { channel: true },
|
||||
orderBy: { updatedAt: 'desc' },
|
||||
take: 500,
|
||||
});
|
||||
return applications.map((application) => {
|
||||
const appConnections = connections.filter((connection) => connection.tenantId === application.tenantId);
|
||||
const todayTotal = application.messageRecords.length;
|
||||
const delivered = application.messageRecords.filter((message) => message.status === 'delivered').length;
|
||||
return {
|
||||
...application,
|
||||
cmppConnections: appConnections,
|
||||
cmppStatus: normalizeApplicationCmppStatus(appConnections, application.status),
|
||||
sentToday: todayTotal,
|
||||
deliveryRate: todayTotal > 0 ? Number(((delivered / todayTotal) * 100).toFixed(1)) : 0,
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -121,6 +161,89 @@ export class SmsConfigService {
|
||||
return updated;
|
||||
}
|
||||
|
||||
async listApplicationConnections(applicationId: string) {
|
||||
const application = await this.prisma.smsApplication.findUnique({
|
||||
where: { id: applicationId },
|
||||
include: { tenant: true },
|
||||
});
|
||||
if (!application) {
|
||||
throw new NotFoundException('Application not found');
|
||||
}
|
||||
const connections = await this.prisma.cmppConnectionState.findMany({
|
||||
where: { tenantId: application.tenantId },
|
||||
include: { channel: true },
|
||||
orderBy: { updatedAt: 'desc' },
|
||||
take: 100,
|
||||
});
|
||||
return {
|
||||
application,
|
||||
connections,
|
||||
summary: {
|
||||
desiredConnections: connections.reduce((sum, connection) => sum + connection.desiredConnections, 0),
|
||||
currentConnections: connections.reduce((sum, connection) => sum + connection.currentConnections, 0),
|
||||
status: normalizeApplicationCmppStatus(connections, application.status),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async getApplicationCmppParams(applicationId: string) {
|
||||
const application = await this.prisma.smsApplication.findUnique({
|
||||
where: { id: applicationId },
|
||||
include: { tenant: true },
|
||||
});
|
||||
if (!application) {
|
||||
throw new NotFoundException('Application not found');
|
||||
}
|
||||
const channel = await this.prisma.smsChannel.findFirst({
|
||||
where: { status: { not: 'deleted' } },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
return {
|
||||
applicationId: application.id,
|
||||
applicationName: application.name,
|
||||
tenantId: application.tenantId,
|
||||
tenantName: application.tenant.name,
|
||||
appCode: application.id,
|
||||
gatewayHost: channel?.gatewayHost ?? '',
|
||||
gatewayPort: channel?.gatewayPort ?? 0,
|
||||
enterpriseCode: channel?.enterpriseCode ?? application.tenant.code,
|
||||
account: channel?.account ?? application.tenant.code,
|
||||
passwordCipher: channel?.passwordCipher ?? application.secretHash,
|
||||
srcId: channel?.srcId ?? '',
|
||||
maxConnections: channel?.config && typeof channel.config === 'object' && 'maxConnections' in channel.config ? Number(channel.config.maxConnections) : 1,
|
||||
heartbeatSeconds: 30,
|
||||
windowSize: 16,
|
||||
protocolVersion: channel?.cmppVersion ?? '3.0',
|
||||
};
|
||||
}
|
||||
|
||||
async disconnectApplicationConnection(applicationId: string, connectionId: string, data: StatusChangeDto = { status: 'disconnected' }) {
|
||||
const application = await this.prisma.smsApplication.findUnique({ where: { id: applicationId } });
|
||||
if (!application) {
|
||||
throw new NotFoundException('Application not found');
|
||||
}
|
||||
const connection = await this.prisma.cmppConnectionState.findFirst({
|
||||
where: { tenantId: application.tenantId, connectionId },
|
||||
});
|
||||
if (!connection) {
|
||||
throw new NotFoundException('Connection not found');
|
||||
}
|
||||
const updated = await this.prisma.cmppConnectionState.update({
|
||||
where: { channelId_connectionId: { channelId: connection.channelId, connectionId } },
|
||||
data: {
|
||||
status: 'disconnected',
|
||||
currentConnections: 0,
|
||||
lastDisconnectedAt: new Date(),
|
||||
lastError: data.reason,
|
||||
},
|
||||
});
|
||||
await this.writeOperationLog(application.tenantId, data.operatorId, 'cmpp_connection.disconnected', 'cmpp_connection', `${connection.channelId}:${connectionId}`, {
|
||||
applicationId,
|
||||
reason: data.reason,
|
||||
});
|
||||
return updated;
|
||||
}
|
||||
|
||||
listSignatures(tenantId?: string) {
|
||||
return this.prisma.smsSignature.findMany({
|
||||
where: tenantId ? { tenantId } : undefined,
|
||||
@@ -407,3 +530,22 @@ function inferTemplateVariables(content: string): TemplateVariableInput[] {
|
||||
const matches = content.match(/\$\{[a-zA-Z0-9_]+\}/g) ?? [];
|
||||
return [...new Set(matches)].map((match) => ({ name: match.slice(2, -1), required: true }));
|
||||
}
|
||||
|
||||
function startOfToday() {
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
function normalizeApplicationCmppStatus(connections: Array<{ status: string; currentConnections: number }>, applicationStatus: string) {
|
||||
if (applicationStatus !== 'active') {
|
||||
return 'inactive';
|
||||
}
|
||||
if (connections.some((connection) => ['online', 'connected', 'open'].includes(connection.status) && connection.currentConnections > 0)) {
|
||||
return 'connected';
|
||||
}
|
||||
if (connections.some((connection) => ['auth_failed', 'heartbeat_timeout', 'reconnecting'].includes(connection.status))) {
|
||||
return 'degraded';
|
||||
}
|
||||
return 'disconnected';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user