fix: Add additional checks for disabled projects
This commit is contained in:
@@ -121,6 +121,16 @@ SMTP_DOMAIN=smtp.example.com
|
|||||||
# PLUNK_API_KEY=
|
# PLUNK_API_KEY=
|
||||||
# PLUNK_FROM_ADDRESS=
|
# PLUNK_FROM_ADDRESS=
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# OPTIONAL: Security Settings
|
||||||
|
# ========================================
|
||||||
|
# Controls whether projects are automatically disabled when bounce/complaint rate thresholds are exceeded
|
||||||
|
# When enabled (default), projects exceeding security limits will be automatically suspended
|
||||||
|
# When disabled, violations will be logged and notifications sent, but projects won't be auto-disabled
|
||||||
|
# Recommended for self-hosters: false (manage project status manually)
|
||||||
|
# Default: true
|
||||||
|
# AUTO_PROJECT_DISABLE=false
|
||||||
|
|
||||||
# ========================================
|
# ========================================
|
||||||
# ADVANCED (rarely needed)
|
# ADVANCED (rarely needed)
|
||||||
# ========================================
|
# ========================================
|
||||||
|
|||||||
@@ -76,3 +76,11 @@ STRIPE_WEBHOOK_SECRET=
|
|||||||
STRIPE_PRICE_ONBOARDING= # Optional: One-time onboarding fee price ID (e.g., price_xxxxx)
|
STRIPE_PRICE_ONBOARDING= # Optional: One-time onboarding fee price ID (e.g., price_xxxxx)
|
||||||
STRIPE_PRICE_EMAIL_USAGE= # Required: Metered price ID for pay-per-email billing
|
STRIPE_PRICE_EMAIL_USAGE= # Required: Metered price ID for pay-per-email billing
|
||||||
STRIPE_METER_EVENT_NAME=emails # Meter event name (API key from your Stripe meter, default: emails)
|
STRIPE_METER_EVENT_NAME=emails # Meter event name (API key from your Stripe meter, default: emails)
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# Security (Optional)
|
||||||
|
# ==============================================================================
|
||||||
|
# Controls whether projects are automatically disabled when bounce/complaint rate thresholds are exceeded
|
||||||
|
# Set to 'false' to disable automatic project suspension (useful for self-hosters who manage manually)
|
||||||
|
# Default: true (automatic project disabling enabled)
|
||||||
|
# AUTO_PROJECT_DISABLE=true
|
||||||
|
|||||||
@@ -98,3 +98,8 @@ export const SMTP_ENABLED =
|
|||||||
export const PLUNK_API_KEY = validateEnv('PLUNK_API_KEY', '');
|
export const PLUNK_API_KEY = validateEnv('PLUNK_API_KEY', '');
|
||||||
export const PLUNK_FROM_ADDRESS = validateEnv('PLUNK_FROM_ADDRESS', '');
|
export const PLUNK_FROM_ADDRESS = validateEnv('PLUNK_FROM_ADDRESS', '');
|
||||||
export const PLUNK_ENABLED = PLUNK_API_KEY !== '' && PLUNK_FROM_ADDRESS !== '';
|
export const PLUNK_ENABLED = PLUNK_API_KEY !== '' && PLUNK_FROM_ADDRESS !== '';
|
||||||
|
|
||||||
|
// Security (optional)
|
||||||
|
// Controls whether projects are automatically disabled when bounce/complaint rate thresholds are exceeded
|
||||||
|
// Useful for self-hosters who want to manage project status manually
|
||||||
|
export const AUTO_PROJECT_DISABLE = validateEnv('AUTO_PROJECT_DISABLE', 'true') === 'true';
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {redis} from '../database/redis.js';
|
|||||||
import {Keys} from './keys.js';
|
import {Keys} from './keys.js';
|
||||||
import {NtfyService} from './NtfyService.js';
|
import {NtfyService} from './NtfyService.js';
|
||||||
import {QueueService} from './QueueService.js';
|
import {QueueService} from './QueueService.js';
|
||||||
import {DASHBOARD_URI, LANDING_URI} from '../app/constants.js';
|
import {AUTO_PROJECT_DISABLE, DASHBOARD_URI, LANDING_URI} from '../app/constants.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Security thresholds for bounce and complaint rates
|
* Security thresholds for bounce and complaint rates
|
||||||
@@ -110,9 +110,31 @@ export class SecurityService {
|
|||||||
// Get current security status
|
// Get current security status
|
||||||
const status = await this.getSecurityStatus(projectId);
|
const status = await this.getSecurityStatus(projectId);
|
||||||
|
|
||||||
// If project should be disabled, disable it
|
// If project should be disabled, disable it (only if auto-disable is enabled)
|
||||||
if (status.shouldDisable) {
|
if (status.shouldDisable && AUTO_PROJECT_DISABLE) {
|
||||||
await this.disableProject(projectId, status);
|
await this.disableProject(projectId, status);
|
||||||
|
} else if (status.shouldDisable && !AUTO_PROJECT_DISABLE) {
|
||||||
|
// Log critical violations but don't auto-disable (self-hosted mode)
|
||||||
|
const project = await prisma.project.findUnique({
|
||||||
|
where: {id: projectId},
|
||||||
|
select: {name: true},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (project) {
|
||||||
|
signale.error(
|
||||||
|
`[SECURITY] Project ${projectId} (${project.name}) has CRITICAL security violations but auto-disable is turned off:`,
|
||||||
|
status.violations,
|
||||||
|
);
|
||||||
|
signale.info(
|
||||||
|
`[SECURITY] 7-day stats: ${status.sevenDay.bounces} bounces, ${status.sevenDay.complaints} complaints out of ${status.sevenDay.total} emails`,
|
||||||
|
);
|
||||||
|
signale.info(
|
||||||
|
`[SECURITY] All-time stats: ${status.allTime.bounces} bounces, ${status.allTime.complaints} complaints out of ${status.allTime.total} emails`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Send notification about critical security violations
|
||||||
|
await NtfyService.notifySecurityWarning(project.name, projectId, status.violations);
|
||||||
|
}
|
||||||
} else if (status.warnings.length > 0) {
|
} else if (status.warnings.length > 0) {
|
||||||
// Log warnings for monitoring
|
// Log warnings for monitoring
|
||||||
signale.warn(`[SECURITY] Project ${projectId} has security warnings:`, status.warnings);
|
signale.warn(`[SECURITY] Project ${projectId} has security warnings:`, status.warnings);
|
||||||
|
|||||||
@@ -187,6 +187,9 @@ services:
|
|||||||
PLUNK_API_KEY: ${PLUNK_API_KEY:-}
|
PLUNK_API_KEY: ${PLUNK_API_KEY:-}
|
||||||
PLUNK_FROM_ADDRESS: ${PLUNK_FROM_ADDRESS:-}
|
PLUNK_FROM_ADDRESS: ${PLUNK_FROM_ADDRESS:-}
|
||||||
|
|
||||||
|
# Security
|
||||||
|
AUTO_PROJECT_DISABLE: ${AUTO_PROJECT_DISABLE:-false}
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
# Persistent storage for application data
|
# Persistent storage for application data
|
||||||
- plunk_data:/app/data
|
- plunk_data:/app/data
|
||||||
|
|||||||
Reference in New Issue
Block a user