无法在我的 Typescript React Native 应用程序中成功将 Mailgun 与“从 Firestore 触发电子邮件”集成,导致超时

问题描述 投票:0回答:1

我正在创建一个 Typescript React Native 应用程序(使用 Expo),并尝试构建一个“邀请用户功能”,您可以在其中输入昵称和电子邮件,然后该用户将收到一封邀请他们加入该应用程序的电子邮件。

它的工作原理是为邀请和邮件创建 Firestore 集合

enter image description here

enter image description here

然后将它们从 Firestore 推送到 Trigger Email,然后通过 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 = "[email protected]";
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
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;
reactjs typescript firebase react-native mailgun
1个回答
0
投票

问题是我将 SMTP 和 API 结合起来。我还需要重新初始化我的 firestore 功能。我还完全删除了扩展程序。

这是最终起作用的代码:

import * as functions from 'firebase-functions';
import { NodeMailgun } from 'ts-mailgun';

// Mailgun Configuration
const mailer = new NodeMailgun();
mailer.apiKey = '';
mailer.domain = '.org';
mailer.fromEmail = '';
mailer.fromTitle = '';
mailer.init();

// Firestore Trigger for Invitation
exports.sendInvitationEmail = functions.firestore
    .document('invitations/{invitationId}')
    .onCreate(async (snap, context) => {
        const data = snap.data();
        const email = data.email;
        const nickname = data.nickname;
        try {
            await mailer.send(email, 'You are invited!', `<h1>Hello ${nickname}, you have been invited!</h1>`);
            console.log('Invitation sent to', email);
        } catch (error) {
            console.error('Mail sending error:', error);
            throw new functions.https.HttpsError('unknown', 'Failed to send invitation');
        }
    });

import React, { useState } from 'react';
import { View, TextInput, Button, StyleSheet, Text } from 'react-native';
import { db } from '../firebaseConfig'; // 
import { collection, addDoc } from 'firebase/firestore';

export default function SettingsScreen() {
    const [email, setEmail] = useState<string>('');
    const [nickname, setNickname] = useState<string>('');

    async function handleInvite() {
        try {
            await addDoc(collection(db, 'invitations'), {
                email: email,
                nickname: nickname,
                createdAt: new Date(),
                invitationSent: false,
                status: 'pending'
            });
            alert('Invitation sent!');
        } catch (error) {
            console.error(error);
            alert('Failed to send invitation.');
        }
    }

    return (
        <View style={styles.container}>
            <Text>Email:</Text>
            <TextInput style={styles.input} value={email} onChangeText={setEmail} placeholder="Enter email" />
            <Text>Nickname:</Text>
            <TextInput style={styles.input} value={nickname} onChangeText={setNickname} placeholder="Enter nickname" />
            <Button title="Send Invitation" onPress={handleInvite} />
        </View>
    );
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        padding: 20,
    },
    input: {
        height: 40,
        marginBottom: 12,
        borderWidth: 1,
        padding: 10,
    },
});
© www.soinside.com 2019 - 2024. All rights reserved.