mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 12:31:26 -05:00
Implement pipeline monitoring alerts
Extend existing alert system to include real-time monitoring for rate limit violations and ban evasion attempts. This involves adding new reporting functions to `pipelineAlerts.ts`, integrating these functions into submission and company helper files, updating the admin dashboard component to display new alert types, and creating a database migration for the new alert type.
This commit is contained in:
@@ -34,6 +34,7 @@ const ALERT_TYPE_LABELS: Record<string, string> = {
|
|||||||
validation_error: 'Validation Error',
|
validation_error: 'Validation Error',
|
||||||
stale_submissions: 'Stale Submissions',
|
stale_submissions: 'Stale Submissions',
|
||||||
circular_dependency: 'Circular Dependency',
|
circular_dependency: 'Circular Dependency',
|
||||||
|
rate_limit_violation: 'Rate Limit Violation',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function PipelineHealthAlerts() {
|
export function PipelineHealthAlerts() {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { withRetry, isRetryableError } from './retryHelpers';
|
|||||||
import { logger } from './logger';
|
import { logger } from './logger';
|
||||||
import { checkSubmissionRateLimit, recordSubmissionAttempt } from './submissionRateLimiter';
|
import { checkSubmissionRateLimit, recordSubmissionAttempt } from './submissionRateLimiter';
|
||||||
import { sanitizeErrorMessage } from './errorSanitizer';
|
import { sanitizeErrorMessage } from './errorSanitizer';
|
||||||
|
import { reportRateLimitViolation, reportBanEvasionAttempt } from './pipelineAlerts';
|
||||||
|
|
||||||
export type { CompanyFormData, TempCompanyData };
|
export type { CompanyFormData, TempCompanyData };
|
||||||
|
|
||||||
@@ -26,6 +27,11 @@ function checkRateLimitOrThrow(userId: string, action: string): void {
|
|||||||
retryAfter: rateLimit.retryAfter,
|
retryAfter: rateLimit.retryAfter,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Report to system alerts for admin visibility
|
||||||
|
reportRateLimitViolation(userId, action, rateLimit.retryAfter || 60).catch(() => {
|
||||||
|
// Non-blocking - don't fail submission if alert fails
|
||||||
|
});
|
||||||
|
|
||||||
throw new Error(sanitizedMessage);
|
throw new Error(sanitizedMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +65,10 @@ export async function submitCompanyCreation(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'company_creation').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,6 +205,10 @@ export async function submitCompanyUpdate(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'company_update').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
} from './submissionValidation';
|
} from './submissionValidation';
|
||||||
import { checkSubmissionRateLimit, recordSubmissionAttempt } from './submissionRateLimiter';
|
import { checkSubmissionRateLimit, recordSubmissionAttempt } from './submissionRateLimiter';
|
||||||
import { sanitizeErrorMessage } from './errorSanitizer';
|
import { sanitizeErrorMessage } from './errorSanitizer';
|
||||||
|
import { reportRateLimitViolation, reportBanEvasionAttempt } from './pipelineAlerts';
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// COMPOSITE SUBMISSION TYPES
|
// COMPOSITE SUBMISSION TYPES
|
||||||
@@ -221,6 +222,11 @@ function checkRateLimitOrThrow(userId: string, action: string): void {
|
|||||||
retryAfter: rateLimit.retryAfter,
|
retryAfter: rateLimit.retryAfter,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Report to system alerts for admin visibility
|
||||||
|
reportRateLimitViolation(userId, action, rateLimit.retryAfter || 60).catch(() => {
|
||||||
|
// Non-blocking - don't fail submission if alert fails
|
||||||
|
});
|
||||||
|
|
||||||
throw new Error(sanitizedMessage);
|
throw new Error(sanitizedMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,6 +287,10 @@ async function submitCompositeCreation(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'composite_creation').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -738,6 +748,10 @@ export async function submitParkCreation(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'park_creation').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -945,6 +959,10 @@ export async function submitParkUpdate(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'park_update').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1283,6 +1301,10 @@ export async function submitRideCreation(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'ride_creation').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1573,6 +1595,10 @@ export async function submitRideUpdate(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'ride_update').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1786,6 +1812,10 @@ export async function submitRideModelCreation(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'ride_model_creation').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1950,6 +1980,10 @@ export async function submitRideModelUpdate(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'ride_model_update').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2113,6 +2147,10 @@ export async function submitManufacturerCreation(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'manufacturer_creation').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2220,6 +2258,10 @@ export async function submitManufacturerUpdate(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'manufacturer_update').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2329,6 +2371,10 @@ export async function submitDesignerCreation(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'designer_creation').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2436,6 +2482,10 @@ export async function submitDesignerUpdate(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'designer_update').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2545,6 +2595,10 @@ export async function submitOperatorCreation(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'operator_creation').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2652,6 +2706,10 @@ export async function submitOperatorUpdate(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'operator_update').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2761,6 +2819,10 @@ export async function submitPropertyOwnerCreation(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'property_owner_creation').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2868,6 +2930,10 @@ export async function submitPropertyOwnerUpdate(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'property_owner_update').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3017,6 +3083,10 @@ export async function submitTimelineEvent(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'timeline_event_creation').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3188,6 +3258,10 @@ export async function submitTimelineEventUpdate(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (profile?.banned) {
|
if (profile?.banned) {
|
||||||
|
// Report ban evasion attempt
|
||||||
|
reportBanEvasionAttempt(userId, 'timeline_event_update').catch(() => {
|
||||||
|
// Non-blocking - don't fail if alert fails
|
||||||
|
});
|
||||||
throw new Error('Account suspended. Contact support for assistance.');
|
throw new Error('Account suspended. Contact support for assistance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,3 +80,59 @@ export async function checkAndReportQueueStatus(userId?: string): Promise<void>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report rate limit violations to system alerts
|
||||||
|
* Called when checkSubmissionRateLimit() blocks a user
|
||||||
|
*/
|
||||||
|
export async function reportRateLimitViolation(
|
||||||
|
userId: string,
|
||||||
|
action: string,
|
||||||
|
retryAfter: number
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
await supabase.rpc('create_system_alert', {
|
||||||
|
p_alert_type: 'rate_limit_violation',
|
||||||
|
p_severity: 'medium',
|
||||||
|
p_message: `Rate limit exceeded: ${action} (retry after ${retryAfter}s)`,
|
||||||
|
p_metadata: {
|
||||||
|
user_id: userId,
|
||||||
|
action,
|
||||||
|
retry_after_seconds: retryAfter,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
handleNonCriticalError(error, {
|
||||||
|
action: 'Report rate limit violation to alerts'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report ban evasion attempts to system alerts
|
||||||
|
* Called when banned users attempt to submit content
|
||||||
|
*/
|
||||||
|
export async function reportBanEvasionAttempt(
|
||||||
|
userId: string,
|
||||||
|
action: string,
|
||||||
|
username?: string
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
await supabase.rpc('create_system_alert', {
|
||||||
|
p_alert_type: 'ban_attempt',
|
||||||
|
p_severity: 'high',
|
||||||
|
p_message: `Banned user attempted submission: ${action}${username ? ` (${username})` : ''}`,
|
||||||
|
p_metadata: {
|
||||||
|
user_id: userId,
|
||||||
|
action,
|
||||||
|
username: username || 'unknown',
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
handleNonCriticalError(error, {
|
||||||
|
action: 'Report ban evasion attempt to alerts'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user