Trying to export backup via admin.firestore.v1.FirestoreAdminClient on a Next.js API route. Error: Could not load the default credentials

Trying to export backup via admin.firestore.v1.FirestoreAdminClient on a Next.js API route. Error: Could not load the default credentials

我正在尝试运行此代码来实施 Firestore 备份。

但这是我第一次将其部署到 Next.js 项目中。

我会在我的 /api/backup 端点上点击它。

const backupHandler: NextApiHandler = async (req,res) => {
  try {
  
    const admin = initializeFirebaseAdmin();
    const PROJECT_ID = getProjectId();
    
    const client = new admin.firestore.v1.FirestoreAdminClient();
    const DB_NAME = client.databasePath(PROJECT_ID, '(default)');

    const TODAY = getToday();  // YYYY-MM-DD
    const hashId = generateId().slice(0,5);
    const BUCKET = `gs://${PROJECT_ID}.appspot.com`;
    const FOLDER = `firestore-backup/${TODAY}-${hashId}`;
    const FULL_PATH = `${BUCKET}/${FOLDER}`;

    await client.exportDocuments({
      name: DB_NAME,
      outputUriPrefix: FULL_PATH,
      collectionIds: [] // CAN LIST SPECIFIC COLLECTIONS
    });

    console.log(`Backup successfully exported`);
    return res.status(200).send("Ok");
    
  }
  catch(err) {
    console.log(err);
    return res.status(500).send("Server error");
  }
  
};

这是initializeFirebaseAdmin()函数

type FirebaseAdmin = typeof import("firebase-admin/lib/firebase-namespace")

const getServiceAccount = () : admin.ServiceAccount => {
  if (process.env.VERCEL_ENV === "production"
  ||  process.env.VERCEL_GIT_COMMIT_REF === "staging")  {
    return SERVICE_ACCOUNT.PROD;
  }
  else return SERVICE_ACCOUNT.TEST;
};

export const initializeFirebaseAdmin = (): FirebaseAdmin => {
  const account = getServiceAccount();  // THIS GETS THE SERVICE ACOUNT (VIA THE DOWNLOADED .json FILE)
  const PROJECT_ID = getProjectId();
  if (!admin.apps.length) {
    admin.initializeApp({
      credential: admin.credential.cert(account),
      databaseURL: `https://${PROJECT_ID}.firebaseio.com`  // I TRIED WITH AND WITHOUT THIS LINE: SAME RESULT
    });
  }
  return admin;
};

这是我遇到的错误:

Error: Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information. at GoogleAuth.getApplicationDefaultAsync (/var/task/node_modules/google-auth-library/build/src/auth/googleauth.js:173:19)

起初我只是使用credential 属性。然后我添加了 databaseURL 看它是否能解决问题,但仍然是相同的结果。

我想我正在正确初始化 firebase-admin。不确定这里出了什么问题。


更新: 刚发现同样的代码在我的本地环境(开发中)上运行良好,但在我的 Next.js [=42 上不起作用=] 部署时的环境。可能出了什么问题?

在地狱般的日子后,才发现我做错了什么。

我正在使用 new admin.firestore.v1.FirestoreAdminClient();,因为 firebase-admin 本身不会公开任何方法供我们访问 exportDocuments() 功能。

但事实是,当您像那样实例化 FirestoreAdminClient 时,它将无法访问您在 admin.initializeApp({credentials}).

上使用的凭据

因此您需要将凭据传递给客户端,方法是:

import * as admin from "firebase-admin";  // OR IMPORT IT FROM SOME SINGLETON initializeFirebaseAdmin FUNCTION
import { NextApiHandler } from "next";
import SERVICE_ACCOUNT from "some-path/serviceAccount.json";

interface CredentialBody { // COPIED THIS FROM google-auth-library > auth > credentials
  client_email?: string;
  private_key?: string;
}

const backupHandler: NextApiHandler = async (req,res) => {

  const client = new admin.firestore.v1.FirestoreAdminClient({
    credentials: SERVICE_ACCOUNT as CredentialBody    // <<<<<< THIS IS THE IMPORTANT PART
  });

  const PROJECT_ID = getProjectId();
  const DB_NAME = client.databasePath(PROJECT_ID, "(default)");

  const TODAY = getToday();  // YYYY-MM-DD
  const hashId = generateId().slice(0,5);
  const BUCKET = `gs://${PROJECT_ID}.appspot.com`;
  const FOLDER = `backup/${TODAY}-${hashId}`;
  const FULL_PATH = `${BUCKET}/${FOLDER}`;

  await client.exportDocuments({
    name: DB_NAME,
    outputUriPrefix: FULL_PATH,
    collectionIds: [] // CAN LIST SPECIFIC COLLECTIONS
  });

  console.log(`Backup successfully exported`);
  return res.status(200).send("Ok");

};