fix: enforce application limits and signature format
This commit is contained in:
@@ -73,6 +73,32 @@ describe('OpenApiService', () => {
|
||||
expect(prisma.httpWebhookEvent.create).toHaveBeenCalled();
|
||||
expect(prisma.httpWebhookDelivery.create).toHaveBeenCalledWith({ data: { eventId: 'event-row-1', endpointId: 'endpoint-1' } });
|
||||
});
|
||||
|
||||
it('defaults a newly enabled HTTP interface to all six capabilities and HTTP webhook delivery', async () => {
|
||||
const prisma = {
|
||||
smsApplication: { findFirst: jest.fn().mockResolvedValue({ id: 'app-1', name: '应用A', httpConfig: null, httpIpAllowlist: [] }) },
|
||||
smsApplicationHttpConfig: { upsert: jest.fn().mockImplementation(({ create }) => Promise.resolve(create)) },
|
||||
smsApplicationHttpIpAllowlist: { deleteMany: jest.fn().mockResolvedValue({ count: 0 }), createMany: jest.fn() },
|
||||
$transaction: jest.fn((operations) => Promise.all(operations)),
|
||||
};
|
||||
const service = new OpenApiService(prisma as never, {} as never);
|
||||
|
||||
await service.updateConfig('app-1', { enabled: true });
|
||||
|
||||
expect(prisma.smsApplicationHttpConfig.upsert).toHaveBeenCalledWith(expect.objectContaining({
|
||||
create: expect.objectContaining({
|
||||
enabled: true,
|
||||
sendEnabled: true,
|
||||
messageQueryEnabled: true,
|
||||
receiptWebhookEnabled: true,
|
||||
uplinkWebhookEnabled: true,
|
||||
uplinkQueryEnabled: true,
|
||||
credentialSelfServiceEnabled: true,
|
||||
receiptDeliveryMode: 'http',
|
||||
uplinkDeliveryMode: 'http',
|
||||
}),
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
function auth() {
|
||||
|
||||
@@ -69,8 +69,8 @@ export class OpenApiService implements OnModuleInit, OnModuleDestroy {
|
||||
}
|
||||
|
||||
async updateConfig(applicationId: string, input: HttpConfigInput, tenantId?: string) {
|
||||
await this.requireApplication(applicationId, tenantId);
|
||||
const data = normalizeConfig(input);
|
||||
const application = await this.requireApplication(applicationId, tenantId);
|
||||
const data = normalizeConfig(input, application.httpConfig);
|
||||
const ipAllowlist = normalizeIpAllowlist(input.ipAllowlist);
|
||||
const [config] = await this.prisma.$transaction([
|
||||
this.prisma.smsApplicationHttpConfig.upsert({
|
||||
@@ -362,32 +362,44 @@ function normalizeOpenApiFailure(error: unknown) {
|
||||
return { httpStatus: 500, code: 'INTERNAL_ERROR', responseBody: { code: 'INTERNAL_ERROR', message: 'Internal server error' } as Prisma.InputJsonValue };
|
||||
}
|
||||
|
||||
function normalizeConfig(input: HttpConfigInput) {
|
||||
for (const mode of [input.receiptDeliveryMode, input.uplinkDeliveryMode]) {
|
||||
function normalizeConfig(input: HttpConfigInput, existing?: { enabled?: boolean } | null) {
|
||||
const enabling = input.enabled === true && existing?.enabled !== true;
|
||||
const effective = enabling ? {
|
||||
sendEnabled: true,
|
||||
messageQueryEnabled: true,
|
||||
receiptWebhookEnabled: true,
|
||||
uplinkWebhookEnabled: true,
|
||||
uplinkQueryEnabled: true,
|
||||
credentialSelfServiceEnabled: true,
|
||||
receiptDeliveryMode: 'http',
|
||||
uplinkDeliveryMode: 'http',
|
||||
...input,
|
||||
} : input;
|
||||
for (const mode of [effective.receiptDeliveryMode, effective.uplinkDeliveryMode]) {
|
||||
if (mode !== undefined && !DELIVERY_MODES.includes(mode as typeof DELIVERY_MODES[number])) throw new BadRequestException('投递模式仅支持 cmpp、http、both、none');
|
||||
}
|
||||
return {
|
||||
enabled: input.enabled,
|
||||
sendEnabled: input.sendEnabled,
|
||||
messageQueryEnabled: input.messageQueryEnabled,
|
||||
receiptWebhookEnabled: input.receiptWebhookEnabled,
|
||||
uplinkWebhookEnabled: input.uplinkWebhookEnabled,
|
||||
uplinkQueryEnabled: input.uplinkQueryEnabled,
|
||||
credentialSelfServiceEnabled: input.credentialSelfServiceEnabled,
|
||||
qpsLimit: bounded(input.qpsLimit, 1, 1000, 'QPS'),
|
||||
timestampToleranceSeconds: bounded(input.timestampToleranceSeconds, 60, 900, '时间戳容差'),
|
||||
maxCredentialCount: bounded(input.maxCredentialCount, 1, 10, '凭据数'),
|
||||
uplinkRetentionDays: bounded(input.uplinkRetentionDays, 1, 365, '上行保留天数'),
|
||||
maxQueryRangeDays: bounded(input.maxQueryRangeDays, 1, 90, '查询跨度'),
|
||||
maxPageSize: bounded(input.maxPageSize, 10, 500, '分页上限'),
|
||||
receiptDeliveryMode: input.receiptDeliveryMode,
|
||||
uplinkDeliveryMode: input.uplinkDeliveryMode,
|
||||
webhookRetryEnabled: input.webhookRetryEnabled,
|
||||
webhookMaxAttempts: bounded(input.webhookMaxAttempts, 1, 7, '回调重试次数'),
|
||||
webhookTimeoutSeconds: bounded(input.webhookTimeoutSeconds, 1, 30, '回调超时'),
|
||||
requireHttps: input.requireHttps,
|
||||
allowClientManualRetry: input.allowClientManualRetry,
|
||||
allowClientTest: input.allowClientTest,
|
||||
enabled: effective.enabled,
|
||||
sendEnabled: effective.sendEnabled,
|
||||
messageQueryEnabled: effective.messageQueryEnabled,
|
||||
receiptWebhookEnabled: effective.receiptWebhookEnabled,
|
||||
uplinkWebhookEnabled: effective.uplinkWebhookEnabled,
|
||||
uplinkQueryEnabled: effective.uplinkQueryEnabled,
|
||||
credentialSelfServiceEnabled: effective.credentialSelfServiceEnabled,
|
||||
qpsLimit: bounded(effective.qpsLimit, 1, 1000, 'QPS'),
|
||||
timestampToleranceSeconds: bounded(effective.timestampToleranceSeconds, 60, 900, '时间戳容差'),
|
||||
maxCredentialCount: bounded(effective.maxCredentialCount, 1, 10, '凭据数'),
|
||||
uplinkRetentionDays: bounded(effective.uplinkRetentionDays, 1, 365, '上行保留天数'),
|
||||
maxQueryRangeDays: bounded(effective.maxQueryRangeDays, 1, 90, '查询跨度'),
|
||||
maxPageSize: bounded(effective.maxPageSize, 10, 500, '分页上限'),
|
||||
receiptDeliveryMode: effective.receiptDeliveryMode,
|
||||
uplinkDeliveryMode: effective.uplinkDeliveryMode,
|
||||
webhookRetryEnabled: effective.webhookRetryEnabled,
|
||||
webhookMaxAttempts: bounded(effective.webhookMaxAttempts, 1, 7, '回调重试次数'),
|
||||
webhookTimeoutSeconds: bounded(effective.webhookTimeoutSeconds, 1, 30, '回调超时'),
|
||||
requireHttps: effective.requireHttps,
|
||||
allowClientManualRetry: effective.allowClientManualRetry,
|
||||
allowClientTest: effective.allowClientTest,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user