feat: add HTTP API and complete client workflows
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
CREATE TABLE "SmsApplicationHttpConfig" (
|
||||
"id" TEXT NOT NULL,
|
||||
"applicationId" TEXT NOT NULL,
|
||||
"enabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"sendEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"messageQueryEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"receiptWebhookEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"uplinkWebhookEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"uplinkQueryEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"credentialSelfServiceEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"qpsLimit" INTEGER NOT NULL DEFAULT 10,
|
||||
"timestampToleranceSeconds" INTEGER NOT NULL DEFAULT 300,
|
||||
"maxCredentialCount" INTEGER NOT NULL DEFAULT 2,
|
||||
"uplinkRetentionDays" INTEGER NOT NULL DEFAULT 90,
|
||||
"maxQueryRangeDays" INTEGER NOT NULL DEFAULT 31,
|
||||
"maxPageSize" INTEGER NOT NULL DEFAULT 100,
|
||||
"receiptDeliveryMode" TEXT NOT NULL DEFAULT 'cmpp',
|
||||
"uplinkDeliveryMode" TEXT NOT NULL DEFAULT 'cmpp',
|
||||
"webhookRetryEnabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
"webhookMaxAttempts" INTEGER NOT NULL DEFAULT 7,
|
||||
"webhookTimeoutSeconds" INTEGER NOT NULL DEFAULT 10,
|
||||
"requireHttps" BOOLEAN NOT NULL DEFAULT true,
|
||||
"allowClientManualRetry" BOOLEAN NOT NULL DEFAULT true,
|
||||
"allowClientTest" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
CONSTRAINT "SmsApplicationHttpConfig_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE "SmsApplicationHttpIpAllowlist" (
|
||||
"id" TEXT NOT NULL,
|
||||
"applicationId" TEXT NOT NULL,
|
||||
"ipCidr" TEXT NOT NULL,
|
||||
"remark" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "SmsApplicationHttpIpAllowlist_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE "HttpApiCredential" (
|
||||
"id" TEXT NOT NULL,
|
||||
"applicationId" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"accessKey" TEXT NOT NULL,
|
||||
"secretEncrypted" TEXT NOT NULL,
|
||||
"secretLast4" TEXT NOT NULL,
|
||||
"status" TEXT NOT NULL DEFAULT 'active',
|
||||
"expiresAt" TIMESTAMP(3),
|
||||
"lastUsedAt" TIMESTAMP(3),
|
||||
"lastUsedIp" TEXT,
|
||||
"createdById" TEXT,
|
||||
"revokedAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
CONSTRAINT "HttpApiCredential_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE "OpenApiRequest" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenantId" TEXT NOT NULL,
|
||||
"applicationId" TEXT NOT NULL,
|
||||
"credentialId" TEXT NOT NULL,
|
||||
"requestId" TEXT NOT NULL,
|
||||
"idempotencyKey" TEXT NOT NULL,
|
||||
"bodyHash" TEXT NOT NULL,
|
||||
"clientMessageId" TEXT,
|
||||
"messageRecordId" TEXT,
|
||||
"httpStatus" INTEGER,
|
||||
"businessCode" TEXT,
|
||||
"responseBody" JSONB,
|
||||
"sourceIp" TEXT,
|
||||
"userAgent" TEXT,
|
||||
"durationMs" INTEGER,
|
||||
"status" TEXT NOT NULL DEFAULT 'processing',
|
||||
"completedAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
CONSTRAINT "OpenApiRequest_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE "HttpWebhookEndpoint" (
|
||||
"id" TEXT NOT NULL,
|
||||
"applicationId" TEXT NOT NULL,
|
||||
"eventType" TEXT NOT NULL,
|
||||
"url" TEXT NOT NULL,
|
||||
"secretEncrypted" TEXT NOT NULL,
|
||||
"secretLast4" TEXT NOT NULL,
|
||||
"status" TEXT NOT NULL DEFAULT 'active',
|
||||
"lastTestAt" TIMESTAMP(3),
|
||||
"lastTestStatus" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
CONSTRAINT "HttpWebhookEndpoint_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE "HttpWebhookEvent" (
|
||||
"id" TEXT NOT NULL,
|
||||
"eventId" TEXT NOT NULL,
|
||||
"tenantId" TEXT NOT NULL,
|
||||
"applicationId" TEXT NOT NULL,
|
||||
"eventType" TEXT NOT NULL,
|
||||
"messageRecordId" TEXT,
|
||||
"messageId" TEXT,
|
||||
"uplinkMessageId" TEXT,
|
||||
"payload" JSONB NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "HttpWebhookEvent_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE "HttpWebhookDelivery" (
|
||||
"id" TEXT NOT NULL,
|
||||
"eventId" TEXT NOT NULL,
|
||||
"endpointId" TEXT NOT NULL,
|
||||
"status" TEXT NOT NULL DEFAULT 'pending',
|
||||
"attemptCount" INTEGER NOT NULL DEFAULT 0,
|
||||
"nextRetryAt" TIMESTAMP(3),
|
||||
"lastHttpStatus" INTEGER,
|
||||
"lastError" TEXT,
|
||||
"deliveredAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
CONSTRAINT "HttpWebhookDelivery_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE "HttpWebhookAttempt" (
|
||||
"id" TEXT NOT NULL,
|
||||
"deliveryId" TEXT NOT NULL,
|
||||
"attemptNo" INTEGER NOT NULL,
|
||||
"requestHeaders" JSONB,
|
||||
"responseStatus" INTEGER,
|
||||
"responseSummary" TEXT,
|
||||
"errorMessage" TEXT,
|
||||
"durationMs" INTEGER,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "HttpWebhookAttempt_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
ALTER TABLE "SmsMessageRecord" ADD COLUMN "clientMessageId" TEXT;
|
||||
|
||||
CREATE UNIQUE INDEX "SmsApplicationHttpConfig_applicationId_key" ON "SmsApplicationHttpConfig"("applicationId");
|
||||
CREATE UNIQUE INDEX "SmsApplicationHttpIpAllowlist_applicationId_ipCidr_key" ON "SmsApplicationHttpIpAllowlist"("applicationId", "ipCidr");
|
||||
CREATE UNIQUE INDEX "HttpApiCredential_accessKey_key" ON "HttpApiCredential"("accessKey");
|
||||
CREATE INDEX "HttpApiCredential_applicationId_status_idx" ON "HttpApiCredential"("applicationId", "status");
|
||||
CREATE UNIQUE INDEX "OpenApiRequest_requestId_key" ON "OpenApiRequest"("requestId");
|
||||
CREATE UNIQUE INDEX "OpenApiRequest_applicationId_idempotencyKey_key" ON "OpenApiRequest"("applicationId", "idempotencyKey");
|
||||
CREATE INDEX "OpenApiRequest_applicationId_createdAt_idx" ON "OpenApiRequest"("applicationId", "createdAt");
|
||||
CREATE UNIQUE INDEX "HttpWebhookEndpoint_applicationId_eventType_key" ON "HttpWebhookEndpoint"("applicationId", "eventType");
|
||||
CREATE UNIQUE INDEX "HttpWebhookEvent_eventId_key" ON "HttpWebhookEvent"("eventId");
|
||||
CREATE INDEX "HttpWebhookEvent_applicationId_createdAt_idx" ON "HttpWebhookEvent"("applicationId", "createdAt");
|
||||
CREATE UNIQUE INDEX "HttpWebhookDelivery_eventId_endpointId_key" ON "HttpWebhookDelivery"("eventId", "endpointId");
|
||||
CREATE INDEX "HttpWebhookDelivery_status_nextRetryAt_idx" ON "HttpWebhookDelivery"("status", "nextRetryAt");
|
||||
CREATE UNIQUE INDEX "HttpWebhookAttempt_deliveryId_attemptNo_key" ON "HttpWebhookAttempt"("deliveryId", "attemptNo");
|
||||
CREATE UNIQUE INDEX "SmsMessageRecord_applicationId_clientMessageId_key" ON "SmsMessageRecord"("applicationId", "clientMessageId");
|
||||
|
||||
ALTER TABLE "SmsApplicationHttpConfig" ADD CONSTRAINT "SmsApplicationHttpConfig_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "SmsApplication"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "SmsApplicationHttpIpAllowlist" ADD CONSTRAINT "SmsApplicationHttpIpAllowlist_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "SmsApplication"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "HttpApiCredential" ADD CONSTRAINT "HttpApiCredential_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "SmsApplication"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "OpenApiRequest" ADD CONSTRAINT "OpenApiRequest_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
ALTER TABLE "OpenApiRequest" ADD CONSTRAINT "OpenApiRequest_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "SmsApplication"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "OpenApiRequest" ADD CONSTRAINT "OpenApiRequest_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "HttpApiCredential"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
ALTER TABLE "HttpWebhookEndpoint" ADD CONSTRAINT "HttpWebhookEndpoint_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "SmsApplication"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "HttpWebhookEvent" ADD CONSTRAINT "HttpWebhookEvent_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
ALTER TABLE "HttpWebhookEvent" ADD CONSTRAINT "HttpWebhookEvent_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "SmsApplication"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "HttpWebhookDelivery" ADD CONSTRAINT "HttpWebhookDelivery_eventId_fkey" FOREIGN KEY ("eventId") REFERENCES "HttpWebhookEvent"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "HttpWebhookDelivery" ADD CONSTRAINT "HttpWebhookDelivery_endpointId_fkey" FOREIGN KEY ("endpointId") REFERENCES "HttpWebhookEndpoint"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "HttpWebhookAttempt" ADD CONSTRAINT "HttpWebhookAttempt_deliveryId_fkey" FOREIGN KEY ("deliveryId") REFERENCES "HttpWebhookDelivery"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -45,6 +45,8 @@ model Tenant {
|
||||
cmppConnectionStates CmppConnectionState[]
|
||||
gatewayDownstreamRecoveryStatuses GatewayDownstreamRecoveryStatus[]
|
||||
gatewaySubmitDeadLetters GatewaySubmitDeadLetter[]
|
||||
openApiRequests OpenApiRequest[]
|
||||
httpWebhookEvents HttpWebhookEvent[]
|
||||
}
|
||||
|
||||
model EnterpriseCertification {
|
||||
@@ -391,6 +393,12 @@ model SmsApplication {
|
||||
connectionStates CmppConnectionState[]
|
||||
gatewayDownstreamRecoveryStatuses GatewayDownstreamRecoveryStatus[]
|
||||
gatewaySubmitDeadLetters GatewaySubmitDeadLetter[]
|
||||
httpConfig SmsApplicationHttpConfig?
|
||||
httpIpAllowlist SmsApplicationHttpIpAllowlist[]
|
||||
httpApiCredentials HttpApiCredential[]
|
||||
openApiRequests OpenApiRequest[]
|
||||
httpWebhookEndpoints HttpWebhookEndpoint[]
|
||||
httpWebhookEvents HttpWebhookEvent[]
|
||||
|
||||
@@index([tenantId, status])
|
||||
}
|
||||
@@ -407,6 +415,174 @@ model SmsApplicationIpAllowlist {
|
||||
@@unique([applicationId, ipCidr])
|
||||
}
|
||||
|
||||
model SmsApplicationHttpConfig {
|
||||
id String @id @default(cuid())
|
||||
applicationId String @unique
|
||||
enabled Boolean @default(false)
|
||||
sendEnabled Boolean @default(false)
|
||||
messageQueryEnabled Boolean @default(false)
|
||||
receiptWebhookEnabled Boolean @default(false)
|
||||
uplinkWebhookEnabled Boolean @default(false)
|
||||
uplinkQueryEnabled Boolean @default(false)
|
||||
credentialSelfServiceEnabled Boolean @default(false)
|
||||
qpsLimit Int @default(10)
|
||||
timestampToleranceSeconds Int @default(300)
|
||||
maxCredentialCount Int @default(2)
|
||||
uplinkRetentionDays Int @default(90)
|
||||
maxQueryRangeDays Int @default(31)
|
||||
maxPageSize Int @default(100)
|
||||
receiptDeliveryMode String @default("cmpp")
|
||||
uplinkDeliveryMode String @default("cmpp")
|
||||
webhookRetryEnabled Boolean @default(true)
|
||||
webhookMaxAttempts Int @default(7)
|
||||
webhookTimeoutSeconds Int @default(10)
|
||||
requireHttps Boolean @default(true)
|
||||
allowClientManualRetry Boolean @default(true)
|
||||
allowClientTest Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
model SmsApplicationHttpIpAllowlist {
|
||||
id String @id @default(cuid())
|
||||
applicationId String
|
||||
ipCidr String
|
||||
remark String?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([applicationId, ipCidr])
|
||||
}
|
||||
|
||||
model HttpApiCredential {
|
||||
id String @id @default(cuid())
|
||||
applicationId String
|
||||
name String
|
||||
accessKey String @unique
|
||||
secretEncrypted String
|
||||
secretLast4 String
|
||||
status String @default("active")
|
||||
expiresAt DateTime?
|
||||
lastUsedAt DateTime?
|
||||
lastUsedIp String?
|
||||
createdById String?
|
||||
revokedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
||||
requests OpenApiRequest[]
|
||||
|
||||
@@index([applicationId, status])
|
||||
}
|
||||
|
||||
model OpenApiRequest {
|
||||
id String @id @default(cuid())
|
||||
tenantId String
|
||||
applicationId String
|
||||
credentialId String
|
||||
requestId String @unique
|
||||
idempotencyKey String
|
||||
bodyHash String
|
||||
clientMessageId String?
|
||||
messageRecordId String?
|
||||
httpStatus Int?
|
||||
businessCode String?
|
||||
responseBody Json?
|
||||
sourceIp String?
|
||||
userAgent String?
|
||||
durationMs Int?
|
||||
status String @default("processing")
|
||||
completedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id])
|
||||
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
||||
credential HttpApiCredential @relation(fields: [credentialId], references: [id])
|
||||
|
||||
@@unique([applicationId, idempotencyKey])
|
||||
@@index([applicationId, createdAt])
|
||||
}
|
||||
|
||||
model HttpWebhookEndpoint {
|
||||
id String @id @default(cuid())
|
||||
applicationId String
|
||||
eventType String
|
||||
url String
|
||||
secretEncrypted String
|
||||
secretLast4 String
|
||||
status String @default("active")
|
||||
lastTestAt DateTime?
|
||||
lastTestStatus String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
||||
deliveries HttpWebhookDelivery[]
|
||||
|
||||
@@unique([applicationId, eventType])
|
||||
}
|
||||
|
||||
model HttpWebhookEvent {
|
||||
id String @id @default(cuid())
|
||||
eventId String @unique
|
||||
tenantId String
|
||||
applicationId String
|
||||
eventType String
|
||||
messageRecordId String?
|
||||
messageId String?
|
||||
uplinkMessageId String?
|
||||
payload Json
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id])
|
||||
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
||||
deliveries HttpWebhookDelivery[]
|
||||
|
||||
@@index([applicationId, createdAt])
|
||||
}
|
||||
|
||||
model HttpWebhookDelivery {
|
||||
id String @id @default(cuid())
|
||||
eventId String
|
||||
endpointId String
|
||||
status String @default("pending")
|
||||
attemptCount Int @default(0)
|
||||
nextRetryAt DateTime?
|
||||
lastHttpStatus Int?
|
||||
lastError String?
|
||||
deliveredAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
event HttpWebhookEvent @relation(fields: [eventId], references: [id], onDelete: Cascade)
|
||||
endpoint HttpWebhookEndpoint @relation(fields: [endpointId], references: [id], onDelete: Cascade)
|
||||
attempts HttpWebhookAttempt[]
|
||||
|
||||
@@unique([eventId, endpointId])
|
||||
@@index([status, nextRetryAt])
|
||||
}
|
||||
|
||||
model HttpWebhookAttempt {
|
||||
id String @id @default(cuid())
|
||||
deliveryId String
|
||||
attemptNo Int
|
||||
requestHeaders Json?
|
||||
responseStatus Int?
|
||||
responseSummary String?
|
||||
errorMessage String?
|
||||
durationMs Int?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
delivery HttpWebhookDelivery @relation(fields: [deliveryId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([deliveryId, attemptNo])
|
||||
}
|
||||
|
||||
model SmsSignature {
|
||||
id String @id @default(cuid())
|
||||
tenantId String
|
||||
@@ -1135,6 +1311,7 @@ model SmsMessageRecord {
|
||||
drainageInfoId String?
|
||||
reviewTaskId String?
|
||||
messageId String @unique
|
||||
clientMessageId String?
|
||||
phoneNumber String
|
||||
carrier String?
|
||||
province String?
|
||||
@@ -1181,6 +1358,7 @@ model SmsMessageRecord {
|
||||
@@index([phoneNumber])
|
||||
@@index([gatewayMessageId])
|
||||
@@index([drainageInfoId, queuedAt])
|
||||
@@unique([applicationId, clientMessageId])
|
||||
}
|
||||
|
||||
model CmppSubmitSession {
|
||||
|
||||
Reference in New Issue
Block a user