TypeScript React Native集成Mailgun与Firestore触发邮件超时问题排查
Expo TypeScript React Native 邀请功能邮件发送超时排查
我正在开发一款基于Expo的TypeScript React Native应用,搭建「邀请用户」功能:输入昵称和邮箱后,受邀用户会收到加入应用的邮件。实现逻辑为创建Firestore的invitations和mail集合,将数据推送到「Trigger Email from Firestore」扩展,再通过SMTP转发至Mailgun。目前Firestore中已成功生成邀请记录和邮件文档,但Google控制台出现超时错误,且Mailgun无任何相关记录。
控制台日志
INFO 2024-04-23T18:51:39.332357Z [resource.labels.functionName: ext-firestore-send-email-processQueue] Initializing extension with configuration DEBUG 2024-04-23T18:51:39.402786533Z [resource.labels.functionName: ext-firestore-send-email-processQueue] [labels.executionId: y8j24zp92do1] Function execution started INFO 2024-04-23T18:51:39.645436Z [resource.labels.functionName: ext-firestore-send-email-processQueue] [labels.executionId: y8j24zp92do1] Started execution of extension with configuration INFO 2024-04-23T18:51:43.879766Z [resource.labels.functionName: ext-firestore-send-email-processQueue] [labels.executionId: y8j24zp92do1] Completed execution of extension DEBUG 2024-04-23T18:51:43.883830637Z [resource.labels.functionName: ext-firestore-send-email-processQueue] [labels.executionId: y8j24zp92do1] Function execution took 4481 ms, finished with status: 'ok' DEBUG 2024-04-23T18:52:35.972814575Z [resource.labels.functionName: ext-firestore-send-email-processQueue] [labels.executionId: yfsqgcwx6njq] Function execution took 59999 ms, finished with status: 'timeout' INFO 2024-04-23T18:52:46.694433Z [resource.labels.functionName: ext-firestore-send-email-processQueue] Initializing extension with configuration
超时错误详情
{ insertId: "15ci506ffa9z6x", labels: {2}, logName: "projects/icecreamsync/logs/cloudfunctions.googleapis.com%2Fcloud-functions", receiveTimestamp: "2024-04-23T18:52:35.983830631Z", resource: {2}, severity: "DEBUG", textPayload: "Function execution took 59999 ms, finished with status: 'timeout'", timestamp: "2024-04-23T18:52:35.972814575Z", trace: "projects/icecreamsync/traces/651c7307ad3a2528ac08440baec5accb" }
相关代码
index.ts(云函数)
import * as functions from "firebase-functions"; import * as admin from "firebase-admin"; import { NodeMailgun } from "ts-mailgun"; admin.initializeApp(); // Mailgun instance const mailer = new NodeMailgun(); mailer.apiKey = "api"; mailer.domain = "sandboxcxxxxx.mailgun.org"; mailer.fromEmail = "noreply@xxxxx.mailgun.org"; mailer.fromTitle = "App Name"; mailer.init(); // Cloud Function to send an email export const sendEmailOnCreate = functions.runWith({ timeoutSeconds: 120, // Set higher timeout memory: '256MB' // Set adequate memory }).firestore.document("mail/{mailId}").onCreate(async (snap, context) => { const mailData = snap.data(); if (!mailData.to || !mailData.subject || !mailData.html) { console.error("Required email fields are missing", mailData); return null; // terminate function if essential fields are missing } try { await mailer.send(mailData.to, mailData.subject, mailData.html); console.log("Mail sent successfully"); return null; } catch (error) { console.error("Failed to send email:", error); return null; // consider logging this error to a persistent store } });
FirebaseService.ts(Firestore操作)
import { db } from '../firebaseConfig'; import { collection, addDoc, serverTimestamp } from 'firebase/firestore'; export async function inviteFriend(friendNickname: string, friendEmail: string, userId: string): Promise<string> { const invitation = { nickname: friendNickname, email: friendEmail, invitationSent: true, // Set to true assuming email will be sent status: 'pending', invitedBy: userId, createdAt: serverTimestamp() }; try { const docRef = await addDoc(collection(db, "invitations"), invitation); console.log("Invitation created with ID: ", docRef.id); // Create a document in the mail collection expected by the Trigger Email extension const emailContent = { to: friendEmail, message: { subject: "You've been invited!", html: `<p>Hello ${friendNickname},</p><p>You have been invited by ${userId}. Click here to accept the invitation.</p>` } }; await addDoc(collection(db, "mail"), emailContent); return docRef.id; } catch (error) { console.error("Error creating invitation and sending email:", error); throw error; } }
SettingsScreen.tsx(前端界面)
import React, { useState } from 'react'; import { View, TextInput, Button, Text, Alert, StyleSheet } from 'react-native'; import { inviteFriend } from '../services/firebaseService'; const SettingsScreen: React.FC = () => { const [email, setEmail] = useState<string>(''); const [nickname, setNickname] = useState<string>(''); const handleInvite = async () => { const userId = "user123"; // This should ideally be fetched from your auth state try { // Pass UserID here const invitationId = await inviteFriend(nickname, email, userId); Alert.alert("Success", `Invitation sent successfully! ID: ${invitationId}`); } catch (error) { console.error("Failed to send invitation:", error); Alert.alert("Error", "Failed to send invitation."); } }; return ( <View style={styles.container}> <Text>Invite a Friend</Text> <TextInput style={styles.input} placeholder="Nickname" value={nickname} onChangeText={setNickname} /> <TextInput style={styles.input} placeholder="Email" value={email} onChangeText={setEmail} /> <Button title="Invite" onPress={handleInvite} /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', padding: 20 }, input: { height: 40, borderColor: 'gray', borderWidth: 1, marginBottom: 10, padding: 10 } }); export default SettingsScreen;
排查与修复方案
1. 移除自定义云函数与官方扩展的冲突
你同时启用了「Trigger Email from Firestore」官方扩展,又自定义了sendEmailOnCreate云函数监听mail集合创建事件,两者会同时触发导致资源竞争,直接引发扩展函数超时。
修复:删除自定义的sendEmailOnCreate云函数,完全依赖官方扩展处理邮件发送逻辑。
2. 修正Mailgun配置问题
- 自定义云函数中
mailer.apiKey填写的是无效值"api",如果继续使用自定义函数,需替换为真实的Mailgun API密钥;若使用官方扩展,需在扩展配置页面正确填写Mailgun的SMTP或API认证信息。 - 确认Mailgun域名、发件邮箱已在Mailgun后台完成验证,避免邮件被拦截或无法发送。
3. 验证邮件文档结构
官方「Trigger Email from Firestore」扩展要求的mail文档结构是正确的(你当前的emailContent结构符合要求),但需确保没有其他字段干扰扩展解析,比如不要额外添加扩展不识别的字段。
4. 扩展日志排查
在Firebase控制台的「扩展」页面,查看「Trigger Email from Firestore」的运行日志,确认超时具体原因是SMTP连接失败、API调用错误还是权限问题。
内容的提问来源于stack exchange,提问作者Jeremy Alexander
相关产品推荐
相关产品推荐

