diff --git a/apps/api/src/services/billing/credit_billing.ts b/apps/api/src/services/billing/credit_billing.ts index 694d0e5c..69b0617a 100644 --- a/apps/api/src/services/billing/credit_billing.ts +++ b/apps/api/src/services/billing/credit_billing.ts @@ -66,7 +66,8 @@ export async function supaCheckTeamCredits(chunk: AuthCreditUsageChunk, team_id: team_id, NotificationType.LIMIT_REACHED, chunk.sub_current_period_start, - chunk.sub_current_period_end + chunk.sub_current_period_end, + chunk ); return { success: false, message: "Insufficient credits. For more credits, you can upgrade your plan at https://firecrawl.dev/pricing.", remainingCredits: chunk.remaining_credits, chunk }; } else if (creditUsagePercentage >= 0.8 && creditUsagePercentage < 1) { @@ -75,7 +76,8 @@ export async function supaCheckTeamCredits(chunk: AuthCreditUsageChunk, team_id: team_id, NotificationType.APPROACHING_LIMIT, chunk.sub_current_period_start, - chunk.sub_current_period_end + chunk.sub_current_period_end, + chunk ); } diff --git a/apps/api/src/services/notification/email_notification.ts b/apps/api/src/services/notification/email_notification.ts index a63d78ff..cf02892e 100644 --- a/apps/api/src/services/notification/email_notification.ts +++ b/apps/api/src/services/notification/email_notification.ts @@ -3,6 +3,9 @@ import { withAuth } from "../../lib/withAuth"; import { Resend } from "resend"; import { NotificationType } from "../../types"; import { Logger } from "../../../src/lib/logger"; +import { sendSlackWebhook } from "../alerts/slack"; +import { getNotificationString } from "./notification_string"; +import { AuthCreditUsageChunk } from "../../controllers/v1/types"; const emailTemplates: Record< NotificationType, @@ -27,19 +30,21 @@ export async function sendNotification( team_id: string, notificationType: NotificationType, startDateString: string, - endDateString: string + endDateString: string, + chunk: AuthCreditUsageChunk ) { return withAuth(sendNotificationInternal)( team_id, notificationType, startDateString, - endDateString + endDateString, + chunk ); } async function sendEmailNotification( email: string, - notificationType: NotificationType + notificationType: NotificationType, ) { const resend = new Resend(process.env.RESEND_API_KEY); @@ -66,7 +71,8 @@ export async function sendNotificationInternal( team_id: string, notificationType: NotificationType, startDateString: string, - endDateString: string + endDateString: string, + chunk: AuthCreditUsageChunk ): Promise<{ success: boolean }> { if (team_id === "preview") { return { success: true }; @@ -135,6 +141,16 @@ export async function sendNotificationInternal( }, ]); + if (process.env.SLACK_ADMIN_WEBHOOK_URL && emails.length > 0) { + sendSlackWebhook( + `${getNotificationString(notificationType)}: Team ${team_id}, with email ${emails[0].email}. Number of credits used: ${chunk.adjusted_credits_used} | Number of credits in the plan: ${chunk.price_credits}`, + false, + process.env.SLACK_ADMIN_WEBHOOK_URL + ).catch((error) => { + Logger.debug(`Error sending slack notification: ${error}`); + }); + } + if (insertError) { Logger.debug(`Error inserting notification record: ${insertError}`); return { success: false }; diff --git a/apps/api/src/services/notification/notification_string.ts b/apps/api/src/services/notification/notification_string.ts new file mode 100644 index 00000000..8369a0ca --- /dev/null +++ b/apps/api/src/services/notification/notification_string.ts @@ -0,0 +1,17 @@ +import { NotificationType } from "../../types"; + +// depending on the notification type, return the appropriate string +export function getNotificationString( + notificationType: NotificationType +): string { + switch (notificationType) { + case NotificationType.APPROACHING_LIMIT: + return "Approaching the limit (80%)"; + case NotificationType.LIMIT_REACHED: + return "Limit reached (100%)"; + case NotificationType.RATE_LIMIT_REACHED: + return "Rate limit reached"; + default: + return "Unknown notification type"; + } +}