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

kx5bkwkv  于 2022-11-05  发布在  其他
关注(0)|答案(2)|浏览(221)

我正在尝试运行此代码来实现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;
};

这是我得到的错误:

**错误:无法加载默认凭据。**请浏览到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环境中却不起作用。

5vf7fwbs

5vf7fwbs1#

在地狱里呆了几天后,我才发现我做错了什么。
我之所以使用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");

};
y0u0uwnf

y0u0uwnf2#

在我的例子中,我调用了firebase云函数作用域之外的const client = new firestore.v1.FirestoreAdminClient();
之前(不工作):

import * as firestore from '@google-cloud/firestore';
import * as functions from 'firebase-functions';

const client = new firestore.v1.FirestoreAdminClient(); // <--- removed this

export const scheduledFirestoreExport = functions
  .region('europe-west3')
  .pubsub
  .schedule('every 24 hours')
  .onRun((context) => {

    const projectId2 = process.env.GCP_PROJECT || process.env.GCLOUD_PROJECT;
    functions.logger.info('Running backup for projectId2 --> ', projectId2);
    // Replace BUCKET_NAME

我在cloud函数中移动了const client = new firestore.v1.FirestoreAdminClient();-见下文

AFTER -代码最终运行并且可以部署功能

import * as firestore from '@google-cloud/firestore';
import * as functions from 'firebase-functions';

export const scheduledFirestoreExport = functions
  .region('europe-west3')
  .pubsub
  .schedule('every 24 hours')
  .onRun((context) => {

    const client = new firestore.v1.FirestoreAdminClient(); // <-- put it here :)

    const projectId2 = process.env.GCP_PROJECT || process.env.GCLOUD_PROJECT;
    functions.logger.info('Running backup for projectId2 --> ', projectId2);
    // Replace BUCKET_NAME
    const bucket = `gs://${projectId2}-backup`;
    functions.logger.log('using the following bucket --> ', bucket);
    if (!projectId2) {
      functions.logger.warn('ProjectID not found --> escaping backup... --> ', projectId2);
      functions.logger.error('ProjectID not found --> escaping backup... --> ', projectId2);
      return;
    }

我希望这能帮助到一些人
欢呼声

相关问题