尝试通过 Next.js API 路由上的 admin.firestore.v1.FirestoreAdminClient 导出备份。错误:无法加载默认凭据

cbd*_*per 0 node.js firebase firebase-admin next.js google-cloud-firestore

我正在尝试运行此代码来实现 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");
  }
  
};
Run Code Online (Sandbox Code Playgroud)

这是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;
};
Run Code Online (Sandbox Code Playgroud)

这是我收到的错误:

错误:无法加载默认凭据。浏览至https://cloud.google.com/docs/authentication/getting-started了解更多信息。在 GoogleAuth.getApplicationDefaultAsync (/var/task/node_modules/google-auth-library/build/src/auth/googleauth.js:173:19)

在此输入图像描述

起初我只是使用该credential房产。然后我添加了databaseURL看看是否可以解决问题,但结果仍然相同。

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


更新:刚刚发现这段完全相同的代码在我的本地环境(开发中)上运行良好,但在部署它时在我的 Next.js Node.js 环境中不起作用。可能出了什么问题?

cbd*_*per 8

在地狱里度过了几天后,我才发现我做错了什么。

我使用它是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");

};
Run Code Online (Sandbox Code Playgroud)