fix: enforce application limits and signature format

This commit is contained in:
hectorzhao
2026-07-22 16:29:22 +08:00
parent cd085d2712
commit a09036c67b
20 changed files with 479 additions and 79 deletions
@@ -0,0 +1,75 @@
-- Enforce a real per-application daily send quota and make 100,000 the platform default.
UPDATE "SmsApplication"
SET "dailyLimit" = 100000
WHERE "dailyLimit" IS NULL;
ALTER TABLE "SmsApplication"
ALTER COLUMN "dailyLimit" SET DEFAULT 100000,
ALTER COLUMN "dailyLimit" SET NOT NULL;
CREATE TABLE "SmsApplicationDailyUsage" (
"id" TEXT NOT NULL,
"applicationId" TEXT NOT NULL,
"usageDate" DATE NOT NULL,
"usedCount" INTEGER NOT NULL DEFAULT 0,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "SmsApplicationDailyUsage_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "SmsApplicationDailyUsage_applicationId_usageDate_key"
ON "SmsApplicationDailyUsage"("applicationId", "usageDate");
CREATE INDEX "SmsApplicationDailyUsage_usageDate_idx"
ON "SmsApplicationDailyUsage"("usageDate");
ALTER TABLE "SmsApplicationDailyUsage"
ADD CONSTRAINT "SmsApplicationDailyUsage_applicationId_fkey"
FOREIGN KEY ("applicationId") REFERENCES "SmsApplication"("id")
ON DELETE CASCADE ON UPDATE CASCADE;
-- Preserve sends already accepted earlier on the deployment day, so deploying at
-- midday cannot grant an extra full quota. The business day is Asia/Shanghai.
INSERT INTO "SmsApplicationDailyUsage" (
"id", "applicationId", "usageDate", "usedCount", "createdAt", "updatedAt"
)
SELECT
md5(message."applicationId" || ':' || ((CURRENT_TIMESTAMP AT TIME ZONE 'Asia/Shanghai')::date)::text),
message."applicationId",
(CURRENT_TIMESTAMP AT TIME ZONE 'Asia/Shanghai')::date,
COUNT(*)::integer,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
FROM "SmsMessageRecord" message
WHERE message."applicationId" IS NOT NULL
AND message."queuedAt" >= ((CURRENT_TIMESTAMP AT TIME ZONE 'Asia/Shanghai')::date::timestamp AT TIME ZONE 'Asia/Shanghai')
AND message."queuedAt" < (((CURRENT_TIMESTAMP AT TIME ZONE 'Asia/Shanghai')::date + 1)::timestamp AT TIME ZONE 'Asia/Shanghai')
GROUP BY message."applicationId";
-- HTTP applications created before the UI default fix retained CMPP delivery modes.
-- Existing enabled webhook capabilities are migrated to the HTTP transport they expose.
UPDATE "SmsApplicationHttpConfig"
SET "receiptDeliveryMode" = 'http'
WHERE "enabled" = TRUE
AND "receiptWebhookEnabled" = TRUE
AND "receiptDeliveryMode" = 'cmpp';
UPDATE "SmsApplicationHttpConfig"
SET "uplinkDeliveryMode" = 'http'
WHERE "enabled" = TRUE
AND "uplinkWebhookEnabled" = TRUE
AND "uplinkDeliveryMode" = 'cmpp';
ALTER TABLE "SmsApplicationHttpConfig"
ALTER COLUMN "sendEnabled" SET DEFAULT TRUE,
ALTER COLUMN "messageQueryEnabled" SET DEFAULT TRUE,
ALTER COLUMN "receiptWebhookEnabled" SET DEFAULT TRUE,
ALTER COLUMN "uplinkWebhookEnabled" SET DEFAULT TRUE,
ALTER COLUMN "uplinkQueryEnabled" SET DEFAULT TRUE,
ALTER COLUMN "credentialSelfServiceEnabled" SET DEFAULT TRUE,
ALTER COLUMN "receiptDeliveryMode" SET DEFAULT 'http',
ALTER COLUMN "uplinkDeliveryMode" SET DEFAULT 'http';
-- Rollback guidance:
-- Drop SmsApplicationDailyUsage and its FK/indexes, restore SmsApplication.dailyLimit
-- to nullable/no-default, and restore HTTP column defaults to FALSE/'cmpp'. Data migrated
-- from cmpp to http is intentionally not auto-reversed because explicit edits after upgrade
-- cannot be distinguished safely.
@@ -0,0 +1,20 @@
-- SMS signature names are canonical business identifiers and must contain
-- exactly one complete pair of Chinese black brackets: 【signature】.
WITH normalized AS (
SELECT
id,
btrim(
regexp_replace(
regexp_replace(btrim(name), '^[【\[]+', ''),
'[】\]]+$',
''
)
) AS inner_name
FROM "SmsSignature"
)
UPDATE "SmsSignature" AS signature
SET name = '' || normalized.inner_name || ''
FROM normalized
WHERE signature.id = normalized.id
AND normalized.inner_name <> ''
AND signature.name IS DISTINCT FROM '' || normalized.inner_name || '';
+24 -9
View File
@@ -366,7 +366,7 @@ model SmsApplication {
interfaceType String @default("cmpp20")
cmppMaxConnections Int @default(1)
cmppWindowSize Int @default(16)
dailyLimit Int?
dailyLimit Int @default(100000)
customerUnitPrice BigInt @default(0)
queuePriority String @default("normal")
maxPhonesPerTask Int @default(1000000)
@@ -399,6 +399,7 @@ model SmsApplication {
openApiRequests OpenApiRequest[]
httpWebhookEndpoints HttpWebhookEndpoint[]
httpWebhookEvents HttpWebhookEvent[]
dailyUsages SmsApplicationDailyUsage[]
@@index([tenantId, status])
}
@@ -419,20 +420,20 @@ 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)
sendEnabled Boolean @default(true)
messageQueryEnabled Boolean @default(true)
receiptWebhookEnabled Boolean @default(true)
uplinkWebhookEnabled Boolean @default(true)
uplinkQueryEnabled Boolean @default(true)
credentialSelfServiceEnabled Boolean @default(true)
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")
receiptDeliveryMode String @default("http")
uplinkDeliveryMode String @default("http")
webhookRetryEnabled Boolean @default(true)
webhookMaxAttempts Int @default(7)
webhookTimeoutSeconds Int @default(10)
@@ -445,6 +446,20 @@ model SmsApplicationHttpConfig {
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
}
model SmsApplicationDailyUsage {
id String @id @default(cuid())
applicationId String
usageDate DateTime @db.Date
usedCount Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
@@unique([applicationId, usageDate])
@@index([usageDate])
}
model SmsApplicationHttpIpAllowlist {
id String @id @default(cuid())
applicationId String