diff --git a/apps/api/src/controllers/Webhooks.ts b/apps/api/src/controllers/Webhooks.ts index 9ac0c3c..1334aeb 100644 --- a/apps/api/src/controllers/Webhooks.ts +++ b/apps/api/src/controllers/Webhooks.ts @@ -573,6 +573,16 @@ export class Webhooks { signale.success(`[WEBHOOK] Invoice paid for project ${project.name} (${project.id})`); + // Re-enable the project only if it was previously disabled for a failed payment. + // Projects disabled for other reasons (reputation, phishing, manual) must stay disabled. + if (project.disabled && project.disabledReason === 'PAYMENT_FAILED') { + await prisma.project.update({ + where: {id: project.id}, + data: {disabled: false, disabledReason: null}, + }); + signale.success(`[WEBHOOK] Project ${project.name} (${project.id}) re-enabled after payment`); + } + // Send notification about invoice payment await NtfyService.notifyInvoicePaid(project.name, project.id); break; @@ -606,7 +616,7 @@ export class Webhooks { await prisma.project.update({ where: {id: project.id}, - data: {disabled: true}, + data: {disabled: true, disabledReason: 'PAYMENT_FAILED'}, }); await NtfyService.notifyProjectDisabledForPayment(project.name, project.id); diff --git a/apps/api/src/services/SecurityService.ts b/apps/api/src/services/SecurityService.ts index 1877ba4..27d6b0c 100644 --- a/apps/api/src/services/SecurityService.ts +++ b/apps/api/src/services/SecurityService.ts @@ -713,7 +713,7 @@ export class SecurityService { // Disable the project await prisma.project.update({ where: {id: projectId}, - data: {disabled: true}, + data: {disabled: true, disabledReason: 'EMAIL_REPUTATION'}, }); // Log critical security event @@ -971,7 +971,7 @@ ${strippedBody.substring(0, 2000)}`, // Disable the project await prisma.project.update({ where: {id: projectId}, - data: {disabled: true}, + data: {disabled: true, disabledReason: 'PHISHING_DETECTED'}, }); const violation = `A policy violation was detected. Please contact support for more details.`; diff --git a/packages/db/prisma/migrations/20260522130136_add_project_disabled_reason/migration.sql b/packages/db/prisma/migrations/20260522130136_add_project_disabled_reason/migration.sql new file mode 100644 index 0000000..6ef0d27 --- /dev/null +++ b/packages/db/prisma/migrations/20260522130136_add_project_disabled_reason/migration.sql @@ -0,0 +1,5 @@ +-- CreateEnum +CREATE TYPE "ProjectDisabledReason" AS ENUM ('PAYMENT_FAILED', 'EMAIL_REPUTATION', 'PHISHING_DETECTED', 'MANUAL'); + +-- AlterTable +ALTER TABLE "projects" ADD COLUMN "disabledReason" "ProjectDisabledReason"; diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index 258e4ef..637da26 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -43,7 +43,8 @@ model Project { secret String @unique // Admin - disabled Boolean @default(false) + disabled Boolean @default(false) + disabledReason ProjectDisabledReason? // Billing customer String? @unique @@ -632,6 +633,13 @@ model Event { // ENUMS // ============================================ +enum ProjectDisabledReason { + PAYMENT_FAILED // Subscription renewal payment failed + EMAIL_REPUTATION // Bounce or complaint rate thresholds exceeded + PHISHING_DETECTED // Phishing content detected by LLM scan + MANUAL // Disabled by support/admin (e.g. directly in DB) +} + enum AuthMethod { PASSWORD GOOGLE_OAUTH