feat: new secrets
This commit is contained in:
@@ -7,6 +7,7 @@ const sharp = require('sharp')
|
||||
const crypto = require('crypto')
|
||||
|
||||
// Imports internes
|
||||
const { GEMINI_API_KEY } = require('../config/secrets')
|
||||
const { generateImageV2 } = require('../helpers/gemini')
|
||||
const { generatePicturePrompt } = require('../helpers/prompts')
|
||||
const { ALERT_TYPE, refList } = require('../index')
|
||||
@@ -284,6 +285,7 @@ exports.onTaskCreateGenerateCover = onDocumentCreated(
|
||||
timeoutSeconds: 540, // 9 minutes max (Imagen peut être lent)
|
||||
memory: '1GiB',
|
||||
document: 'tasks/{taskId}',
|
||||
secrets: [GEMINI_API_KEY],
|
||||
},
|
||||
async (event) => {
|
||||
const data = event.data?.data() || {}
|
||||
|
||||
@@ -2,7 +2,7 @@ const { onCall, HttpsError } = require('firebase-functions/v2/https')
|
||||
const { z } = require('genkit')
|
||||
const { generateAI, analyseLyrics } = require('../helpers/gemini')
|
||||
const { SUNO_API_BASE, SUNO_TIMESTAMPED_LYRICS_PATH } = require('../config/suno')
|
||||
const { SUNO_API_KEY } = require('../config/keys')
|
||||
const { GEMINI_API_KEY, SUNO_API_KEY } = require('../config/secrets')
|
||||
const axios = require('axios')
|
||||
const { FieldValue } = require('firebase-admin/firestore')
|
||||
const { refList } = require('../index')
|
||||
@@ -137,7 +137,7 @@ const buildModerationBrief = ({
|
||||
|
||||
// --- GENERATION DE PAROLES (MAIN) ---
|
||||
|
||||
exports.generateLyrics = onCall({}, async ({ auth = {}, data = {} }) => {
|
||||
exports.generateLyrics = onCall({ secrets: [GEMINI_API_KEY] }, async ({ auth = {}, data = {} }) => {
|
||||
try {
|
||||
const {
|
||||
objective = '',
|
||||
@@ -344,7 +344,7 @@ const softenModerationDecision = (rawResult = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
exports.analyseLyricsToxicity = onCall({}, async ({ data = {} }) => {
|
||||
exports.analyseLyricsToxicity = onCall({ secrets: [GEMINI_API_KEY] }, async ({ data = {} }) => {
|
||||
try {
|
||||
const requestSchema = z.object({
|
||||
title: z.string().optional(),
|
||||
@@ -420,7 +420,7 @@ async function getSunoTimestamps(projectId) {
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${SUNO_API_KEY}`,
|
||||
Authorization: `Bearer ${SUNO_API_KEY.value()}`,
|
||||
},
|
||||
timeout: 30000,
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ const { randomUUID } = require('crypto')
|
||||
const { ALERT_TYPE, refList } = require('../index')
|
||||
const { sendNotification } = require('./notifications')
|
||||
const { createOrderDocument, ORDER_TYPES } = require('./helpers/orders')
|
||||
const { SUNO_API_KEY } = require('../config/keys')
|
||||
const { SUNO_API_KEY } = require('../config/secrets')
|
||||
const {
|
||||
SUNO_MODEL,
|
||||
SUNO_CALLBACK_URL,
|
||||
@@ -468,7 +468,7 @@ const mergeMusicUrls = async (projectRef, newUrls) => {
|
||||
// 5. FONCTIONS PRINCIPALES (CLOUD FUNCTIONS)
|
||||
// ==========================================
|
||||
|
||||
exports.generateMusic = onCall(async ({ data = {} }) => {
|
||||
exports.generateMusic = onCall({ secrets: [SUNO_API_KEY] }, async ({ data = {} }) => {
|
||||
try {
|
||||
// Extraction des données du Frontend
|
||||
const {
|
||||
@@ -542,7 +542,7 @@ exports.generateMusic = onCall(async ({ data = {} }) => {
|
||||
const response = await axios.post(`${SUNO_API_BASE}${SUNO_API_PATH}`, payload, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${SUNO_API_KEY}`,
|
||||
Authorization: `Bearer ${SUNO_API_KEY.value()}`,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -571,12 +571,12 @@ exports.generateMusic = onCall(async ({ data = {} }) => {
|
||||
})
|
||||
|
||||
// GET STATUS (inchangé mais inclus pour complétude)
|
||||
exports.getSunoStatus = onCall(async ({ data = {} }) => {
|
||||
exports.getSunoStatus = onCall({ secrets: [SUNO_API_KEY] }, async ({ data = {} }) => {
|
||||
const { taskId } = data
|
||||
if (!taskId) throw new Error('TaskId manquant')
|
||||
try {
|
||||
const response = await axios.get(`${SUNO_API_BASE}${SUNO_STATUS_PATH}?taskId=${taskId}`, {
|
||||
headers: { Authorization: `Bearer ${SUNO_API_KEY}` },
|
||||
headers: { Authorization: `Bearer ${SUNO_API_KEY.value()}` },
|
||||
timeout: 30000,
|
||||
})
|
||||
return {
|
||||
|
||||
@@ -4,9 +4,16 @@ const { refList, ALERT_TYPE } = require('../index')
|
||||
const { Expo } = require('expo-server-sdk')
|
||||
const { Resend } = require('resend')
|
||||
const { basicTemplate } = require('../helpers/email')
|
||||
const { RESEND_API_KEY } = require('../config/keys')
|
||||
const { RESEND_API_KEY } = require('../config/secrets')
|
||||
|
||||
const resendInstance = RESEND_API_KEY ? new Resend(RESEND_API_KEY) : null
|
||||
let resendInstance = null
|
||||
|
||||
const getResendInstance = () => {
|
||||
if (!resendInstance) {
|
||||
resendInstance = new Resend(RESEND_API_KEY.value())
|
||||
}
|
||||
return resendInstance
|
||||
}
|
||||
|
||||
// Initialisation de Expo SDK
|
||||
let expo = new Expo()
|
||||
@@ -58,7 +65,11 @@ function buildNotificationEmailPayload({ title = '', message = '', template = {}
|
||||
}
|
||||
|
||||
exports.sendNotificationWhenDocIsCreated = onDocumentCreated(
|
||||
{ region: 'europe-west1', document: 'notifications/{notificationId}' },
|
||||
{
|
||||
region: 'europe-west1',
|
||||
document: 'notifications/{notificationId}',
|
||||
secrets: [RESEND_API_KEY],
|
||||
},
|
||||
async (event) => {
|
||||
try {
|
||||
const {
|
||||
@@ -112,24 +123,20 @@ exports.sendNotificationWhenDocIsCreated = onDocumentCreated(
|
||||
}
|
||||
|
||||
if ((emailNotifications || mailOnly) && !!receiverEmail) {
|
||||
if (!resendInstance) {
|
||||
console.warn('Resend client not configured; unable to send notification email.')
|
||||
} else {
|
||||
try {
|
||||
const { subject, html } = buildNotificationEmailPayload({
|
||||
title,
|
||||
message,
|
||||
template: notifData?.email,
|
||||
})
|
||||
await resendInstance.emails.send({
|
||||
from: EMAIL_FROM,
|
||||
to: [receiverEmail],
|
||||
subject,
|
||||
html,
|
||||
})
|
||||
} catch (e) {
|
||||
console.log('Error sending email:', e)
|
||||
}
|
||||
try {
|
||||
const { subject, html } = buildNotificationEmailPayload({
|
||||
title,
|
||||
message,
|
||||
template: notifData?.email,
|
||||
})
|
||||
await getResendInstance().emails.send({
|
||||
from: EMAIL_FROM,
|
||||
to: [receiverEmail],
|
||||
subject,
|
||||
html,
|
||||
})
|
||||
} catch (e) {
|
||||
console.log('Error sending email:', e)
|
||||
}
|
||||
} else {
|
||||
console.log(
|
||||
|
||||
@@ -3,11 +3,17 @@ const { FieldValue } = require('firebase-admin/firestore')
|
||||
const { onDocumentWritten } = require('firebase-functions/firestore')
|
||||
const _ = require('lodash')
|
||||
const { refList, db } = require('../index')
|
||||
const { SUNO_API_KEY } = require('../config/secrets')
|
||||
const { getSunoTimestamps } = require('./lyrics')
|
||||
const { deleteFolder } = require('../helpers/firebase')
|
||||
const { buildMonthKey } = require('../helpers/stats')
|
||||
|
||||
exports.onProjectWritten = onDocumentWritten('projects/{projectId}', async (projectSnap) => {
|
||||
const PROJECT_FUNCTION_OPTIONS = {
|
||||
document: 'projects/{projectId}',
|
||||
secrets: [SUNO_API_KEY],
|
||||
}
|
||||
|
||||
exports.onProjectWritten = onDocumentWritten(PROJECT_FUNCTION_OPTIONS, async (projectSnap) => {
|
||||
try {
|
||||
const { projectId } = projectSnap.params
|
||||
const beforeData = projectSnap?.data?.before?.data() || null
|
||||
|
||||
@@ -9,6 +9,7 @@ try {
|
||||
}
|
||||
|
||||
const { REGION, refsList } = require('../index')
|
||||
const { STRIPE_SECRET_KEY } = require('../config/secrets')
|
||||
const STRIPE_MODE = 'test'
|
||||
const {
|
||||
getStripeClient,
|
||||
@@ -22,6 +23,7 @@ const {
|
||||
} = require('../helpers/stripe')
|
||||
|
||||
const paymentsCollection = admin.firestore().collection('payments')
|
||||
const STRIPE_FUNCTION_OPTIONS = { region: REGION, secrets: [STRIPE_SECRET_KEY] }
|
||||
|
||||
const getServerTimestamp = () => {
|
||||
if (FieldValue?.serverTimestamp) {
|
||||
@@ -47,7 +49,7 @@ const normalizePaymentIntent = (paymentIntent) => ({
|
||||
metadata: paymentIntent.metadata,
|
||||
})
|
||||
|
||||
const createCheckoutSession = onCall({ region: REGION }, async (request) => {
|
||||
const createCheckoutSession = onCall(STRIPE_FUNCTION_OPTIONS, async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid
|
||||
if (!uid) {
|
||||
@@ -123,7 +125,7 @@ const createCheckoutSession = onCall({ region: REGION }, async (request) => {
|
||||
}
|
||||
})
|
||||
|
||||
const getPremiumStatus = onCall({ region: REGION }, async (request) => {
|
||||
const getPremiumStatus = onCall(STRIPE_FUNCTION_OPTIONS, async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid
|
||||
if (!uid) {
|
||||
@@ -203,7 +205,7 @@ const getPremiumStatus = onCall({ region: REGION }, async (request) => {
|
||||
}
|
||||
})
|
||||
|
||||
const createStripeCustomerPortalSession = onCall({ region: REGION }, async (request) => {
|
||||
const createStripeCustomerPortalSession = onCall(STRIPE_FUNCTION_OPTIONS, async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid
|
||||
if (!uid) {
|
||||
@@ -365,7 +367,7 @@ const ensureStripeConnectAccount = async ({ uid, stripe, userRef, userData }) =>
|
||||
}
|
||||
}
|
||||
|
||||
const createStripeConnectLoginLink = onCall({ region: REGION }, async (request) => {
|
||||
const createStripeConnectLoginLink = onCall(STRIPE_FUNCTION_OPTIONS, async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid
|
||||
if (!uid) {
|
||||
@@ -425,7 +427,7 @@ const createStripeConnectLoginLink = onCall({ region: REGION }, async (request)
|
||||
}
|
||||
})
|
||||
|
||||
const verifyStripePayment = onCall({ region: REGION }, async (request) => {
|
||||
const verifyStripePayment = onCall(STRIPE_FUNCTION_OPTIONS, async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid
|
||||
if (!uid) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const { HttpsError, onCall } = require('firebase-functions/https')
|
||||
|
||||
const { getStripeClient, mapStripeErrorToHttps } = require('../../helpers/stripe')
|
||||
const { STRIPE_SECRET_KEY } = require('../../config/secrets')
|
||||
const { REGION } = require('./config')
|
||||
const {
|
||||
SUBSCRIPTION_PRICE_IDS,
|
||||
@@ -8,6 +9,7 @@ const {
|
||||
COIN_PACK_PRODUCTS,
|
||||
} = require('./constants')
|
||||
const { parseCoinsPerMonth, formatCoinPack } = require('./shared')
|
||||
const STRIPE_FUNCTION_OPTIONS = { region: REGION, secrets: [STRIPE_SECRET_KEY] }
|
||||
|
||||
const formatPlan = (price, priceId) => {
|
||||
if (!price || typeof price !== 'object') {
|
||||
@@ -40,7 +42,7 @@ const formatPlan = (price, priceId) => {
|
||||
}
|
||||
}
|
||||
|
||||
const listSubscriptionPlans = onCall({ region: REGION }, async () => {
|
||||
const listSubscriptionPlans = onCall(STRIPE_FUNCTION_OPTIONS, async () => {
|
||||
try {
|
||||
const stripe = getStripeClient()
|
||||
|
||||
@@ -76,7 +78,7 @@ const listSubscriptionPlans = onCall({ region: REGION }, async () => {
|
||||
}
|
||||
})
|
||||
|
||||
const listCoinPacks = onCall({ region: REGION }, async () => {
|
||||
const listCoinPacks = onCall(STRIPE_FUNCTION_OPTIONS, async () => {
|
||||
try {
|
||||
const stripe = getStripeClient()
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ const {
|
||||
formatCheckoutSessionResponse,
|
||||
mapStripeErrorToHttps,
|
||||
} = require('../../helpers/stripe')
|
||||
const { STRIPE_SECRET_KEY } = require('../../config/secrets')
|
||||
const { REGION } = require('./config')
|
||||
const {
|
||||
ALL_SUBSCRIPTION_PRICE_IDS,
|
||||
@@ -17,6 +18,7 @@ const {
|
||||
const { refsList, formatCoinPack, getSubscriptionMetaFromPrice } = require('./shared')
|
||||
|
||||
const CHECKOUT_UI_MODES = new Set(['hosted', 'embedded'])
|
||||
const STRIPE_FUNCTION_OPTIONS = { region: REGION, secrets: [STRIPE_SECRET_KEY] }
|
||||
|
||||
const sanitizePriceId = (value) => {
|
||||
if (typeof value !== 'string') {
|
||||
@@ -65,7 +67,7 @@ const withCheckoutNavigationParams = (baseParams, { uiMode, successUrl, cancelUr
|
||||
}
|
||||
}
|
||||
|
||||
const createSubscriptionCheckoutSession = onCall({ region: REGION }, async (request) => {
|
||||
const createSubscriptionCheckoutSession = onCall(STRIPE_FUNCTION_OPTIONS, async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid
|
||||
if (!uid) {
|
||||
@@ -152,7 +154,7 @@ const createSubscriptionCheckoutSession = onCall({ region: REGION }, async (requ
|
||||
}
|
||||
})
|
||||
|
||||
const createSongDownloadCheckoutSession = onCall({ region: REGION }, async (request) => {
|
||||
const createSongDownloadCheckoutSession = onCall(STRIPE_FUNCTION_OPTIONS, async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid
|
||||
if (!uid) {
|
||||
@@ -231,7 +233,7 @@ const createSongDownloadCheckoutSession = onCall({ region: REGION }, async (requ
|
||||
}
|
||||
})
|
||||
|
||||
const createPlaybackDownloadCheckoutSession = onCall({ region: REGION }, async (request) => {
|
||||
const createPlaybackDownloadCheckoutSession = onCall(STRIPE_FUNCTION_OPTIONS, async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid
|
||||
if (!uid) {
|
||||
@@ -310,7 +312,7 @@ const createPlaybackDownloadCheckoutSession = onCall({ region: REGION }, async (
|
||||
}
|
||||
})
|
||||
|
||||
const createCoinPackCheckoutSession = onCall({ region: REGION }, async (request) => {
|
||||
const createCoinPackCheckoutSession = onCall(STRIPE_FUNCTION_OPTIONS, async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid
|
||||
if (!uid) {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
const { HttpsError, onCall } = require('firebase-functions/https')
|
||||
|
||||
const { getStripeClient, mapStripeErrorToHttps } = require('../../helpers/stripe')
|
||||
const { STRIPE_SECRET_KEY } = require('../../config/secrets')
|
||||
const { REGION } = require('./config')
|
||||
const { CANCELABLE_SUBSCRIPTION_STATUSES, ACTIVE_SUBSCRIPTION_STATUSES } = require('./constants')
|
||||
const { refsList, formatSubscriptionForClient, resolveUserContext } = require('./shared')
|
||||
const STRIPE_FUNCTION_OPTIONS = { region: REGION, secrets: [STRIPE_SECRET_KEY] }
|
||||
|
||||
const cancelActiveSubscription = onCall({ region: REGION }, async (request) => {
|
||||
const cancelActiveSubscription = onCall(STRIPE_FUNCTION_OPTIONS, async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid
|
||||
if (!uid) {
|
||||
@@ -101,7 +103,7 @@ const cancelActiveSubscription = onCall({ region: REGION }, async (request) => {
|
||||
}
|
||||
})
|
||||
|
||||
const getActiveSubscription = onCall({ region: REGION }, async (request) => {
|
||||
const getActiveSubscription = onCall(STRIPE_FUNCTION_OPTIONS, async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid
|
||||
if (!uid) {
|
||||
|
||||
@@ -2,7 +2,7 @@ const admin = require('firebase-admin')
|
||||
const { FieldValue } = require('firebase-admin/firestore')
|
||||
|
||||
const { refList } = require('../../index')
|
||||
const { STRIPE_WEBHOOK_SECRET } = require('../../config/keys')
|
||||
const { STRIPE_WEBHOOK_SECRET } = require('../../config/secrets')
|
||||
const {
|
||||
SUBSCRIPTION_LEVEL_ALLOWANCES,
|
||||
SUBSCRIPTION_PRICE_METADATA,
|
||||
@@ -120,13 +120,7 @@ const resolveStripeWebhookSecret = () => {
|
||||
return cachedStripeWebhookSecret
|
||||
}
|
||||
|
||||
const envSecret =
|
||||
typeof process?.env?.STRIPE_WEBHOOK_SECRET === 'string'
|
||||
? process.env.STRIPE_WEBHOOK_SECRET.trim()
|
||||
: ''
|
||||
const inlineSecret = typeof STRIPE_WEBHOOK_SECRET === 'string' ? STRIPE_WEBHOOK_SECRET.trim() : ''
|
||||
|
||||
const secret = envSecret || inlineSecret
|
||||
const secret = STRIPE_WEBHOOK_SECRET.value().trim()
|
||||
if (!secret) {
|
||||
throw new Error('STRIPE_WEBHOOK_SECRET not configured')
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ const { HttpsError } = require('firebase-functions/https')
|
||||
|
||||
const { ORDER_TYPES, createOrderDocument } = require('../helpers/orders')
|
||||
const { getStripeClient } = require('../../helpers/stripe')
|
||||
const { STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET } = require('../../config/secrets')
|
||||
const { REGION } = require('./config')
|
||||
const {
|
||||
paymentsCollection,
|
||||
@@ -20,6 +21,10 @@ const {
|
||||
upsertPaymentDocument,
|
||||
} = require('./shared')
|
||||
const { PREMIUM_SUBSCRIPTION_STATUSES } = require('./constants')
|
||||
const STRIPE_WEBHOOK_OPTIONS = {
|
||||
region: REGION,
|
||||
secrets: [STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET],
|
||||
}
|
||||
|
||||
const isPaidCheckoutSession = (session) => {
|
||||
if (!session || typeof session !== 'object') {
|
||||
@@ -749,7 +754,7 @@ const handleStripeWebhookEvent = async ({ event, stripe }) => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleStripeWebhook = onRequest({ region: REGION }, async (req, res) => {
|
||||
const handleStripeWebhook = onRequest(STRIPE_WEBHOOK_OPTIONS, async (req, res) => {
|
||||
if (req.method !== 'POST') {
|
||||
res.status(405).send('Method Not Allowed')
|
||||
return
|
||||
|
||||
+18
-10
@@ -6,14 +6,26 @@ const { ORDER_TYPES, createOrderDocument } = require('./helpers/orders')
|
||||
const { deleteFolder } = require('../helpers/firebase')
|
||||
const { Resend } = require('resend')
|
||||
const { welcomeTemplate } = require('../helpers/email')
|
||||
const { RESEND_API_KEY } = require('../config/keys')
|
||||
const { RESEND_API_KEY } = require('../config/secrets')
|
||||
const { onRequest } = require('firebase-functions/https')
|
||||
|
||||
const resendClient = new Resend(RESEND_API_KEY)
|
||||
let resendClient = null
|
||||
|
||||
const getResendClient = () => {
|
||||
if (!resendClient) {
|
||||
resendClient = new Resend(RESEND_API_KEY.value())
|
||||
}
|
||||
return resendClient
|
||||
}
|
||||
|
||||
const WELCOME_EMAIL_FROM = 'MusicLand <musicland@musicland.ai>'
|
||||
const WELCOME_EMAIL_SUBJECT = 'Bienvenue sur MusicLand'
|
||||
const USER_CREATED_OPTIONS = {
|
||||
document: 'users/{userID}',
|
||||
secrets: [RESEND_API_KEY],
|
||||
}
|
||||
|
||||
exports.testWelcomMail = onRequest(async (req, res) => {
|
||||
exports.testWelcomMail = onRequest({ secrets: [RESEND_API_KEY] }, async (req, res) => {
|
||||
if (req.method !== 'GET') {
|
||||
res.set('Allow', 'GET')
|
||||
return res.status(405).json({ success: false, error: 'Method not allowed' })
|
||||
@@ -22,7 +34,7 @@ exports.testWelcomMail = onRequest(async (req, res) => {
|
||||
const targetEmail = req.query.email || 'tdtomthomas@gmail.com'
|
||||
const firstName = req.query.firstName || 'Toto'
|
||||
const lastName = req.query.lastName || 'Test'
|
||||
const { data, error } = await resendClient.emails.send({
|
||||
const { data, error } = await getResendClient().emails.send({
|
||||
from: WELCOME_EMAIL_FROM,
|
||||
to: [targetEmail],
|
||||
subject: WELCOME_EMAIL_SUBJECT,
|
||||
@@ -40,7 +52,7 @@ exports.testWelcomMail = onRequest(async (req, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
exports.onUserCreated = onDocumentCreated('users/{userID}', async (event) => {
|
||||
exports.onUserCreated = onDocumentCreated(USER_CREATED_OPTIONS, async (event) => {
|
||||
try {
|
||||
const { email = '', firstName = '', lastName = '' } = event?.data?.data() || {}
|
||||
|
||||
@@ -62,13 +74,9 @@ exports.onUserCreated = onDocumentCreated('users/{userID}', async (event) => {
|
||||
)
|
||||
}
|
||||
if (email) {
|
||||
if (!resendClient) {
|
||||
console.warn('Resend API key not configured; skipping welcome email.')
|
||||
return
|
||||
}
|
||||
try {
|
||||
console.log(`Sending welcome email to ${email}`)
|
||||
const { data, error } = await resendClient.emails.send({
|
||||
const { data, error } = await getResendClient().emails.send({
|
||||
from: WELCOME_EMAIL_FROM,
|
||||
to: [email],
|
||||
subject: WELCOME_EMAIL_SUBJECT,
|
||||
|
||||
Reference in New Issue
Block a user