提问人:Joao Victor 提问时间:10/20/2023 最后编辑:Joao Victor 更新时间:10/20/2023 访问量:32
在后端 Javascript 中处理多个 Firebase 项目
Handle multiple Firebase Projects in the BackEnd Javascript
问:
我在许多项目中都有很多应用程序,我想获取有关它们的信息以便以后更新它们,所以我决定使用 NodeJS 创建一个 API,并且我正在配置我的端点可以从不同项目中检索信息的方式,例如计算特定项目中的应用程序数量,我知道我需要每个项目的服务帐户密钥,并且我已经下载了它们,所以我的核心逻辑是像这样的端点
const { initProject } = require('../config/firebaseConfig')
async function getAllApps(req, res) {
const projectId = req.params.projectId;
const project = await initProject(projectId);
try {
androidApps = await project.projectManagement().listAndroidApps();
iosApps = await project.projectManagement().listIosApps();
const allApps = [...androidApps, ...iosApps];
const appList = allApps.map((app) => ({
appId: app.appId
}));
res.status(200).json(appList);
} catch (error) {
console.error('Error retrieving apps:', error);
res.status(500).json({ error: 'Failed to retrieve apps for the project' });
}
}
initProject 是
const admin = require('firebase-admin');
// Initialize a project cache
const projectCache = new Map();
//initialize the project based on it's id name
async function initProject(projectId) {
// Check if the project has already been initialized
if (projectCache.has(projectId)) {
return projectCache.get(projectId);
}
try {
// Construct the path to the service account key file using the provided path or a default value
if (process.env.FIREBASE_ACCOUNT_SERVICE_PATH !== null) {
var serviceAccountPath = `${process.env.FIREBASE_ACCOUNT_SERVICE_PATH}\\${projectId}.json`;
} else {
var serviceAccountPath = `C:\\path\\to\\cred\\${projectId}.json`;
}
// Initialize the Firebase Admin SDK
const serviceAccount = require(serviceAccountPath);
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: `https://${projectId}.firebaseio.com`,
});
// Get the initialized Firebase project
const firebaseProject = admin;
// Cache the initialized project
projectCache.set(projectId, firebaseProject);
return firebaseProject;
} catch (error) {
console.error('Error initializing Firebase project: ', error);
throw new Error('Failed to initialize Firebase project');
}
}
module.exports = {
initProject
}
路线是这样的
router.get('/:projectId/apps', projectController.getAllApps);
所以我也启动了 express,并且对第一个端点的调用是这样的,所以它工作得很好,我的第一次调用成功了,我可以收到我需要的东西,但是如果我尝试再次调用这个端点,但使用另一个项目,它将失败,因为我已经在没有指定应用程序的情况下初始化了 firebase-admin, 但我的需求正是如此,例如,计算每个项目的应用程序数量,所以我需要初始化它,我试图更改它以在我像这样退出 firebaseConfig 后立即杀死需求http://localhost:99/projects/project-name/apps
http://localhost:99/projects/project-name-2/apps
// Initialize a project cache
const projectCache = new Map();
// Function factory to create a new Firebase App instance
function createFirebaseApp(projectId) {
return () => {
if (projectCache.has(projectId)) {
const cachedProject = projectCache.get(projectId);
return cachedProject;
}
try {
// Construct the path to the service account key file using the provided path or a default value
if (process.env.FIREBASE_ACCOUNT_SERVICE_PATH !== null) {
var serviceAccountPath = `${process.env.FIREBASE_ACCOUNT_SERVICE_PATH}\\${projectId}.json`;
} else {
var serviceAccountPath = `C:\\EscolarManager\\Dados\\AppsCred\\${projectId}.json`;
}
// Initialize a new Firebase Admin SDK for each project
const admin = require('firebase-admin');
const serviceAccount = require(serviceAccountPath);
const firebaseProject = admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: `https://${projectId}.firebaseio.com`,
});
// Cache the initialized project
projectCache.set(projectId, firebaseProject);
return firebaseProject;
} catch (error) {
console.error('Error initializing Firebase project: ', error);
throw new Error('Failed to initialize Firebase project');
}
}
}
module.exports = {
createFirebaseApp
}
我也尝试过制作一个工厂功能
const admin = require('firebase-admin');
// Initialize a project cache
const projectCache = new Map();
// Function factory to create a new Firebase App instance
function createFirebaseApp(projectId) {
return () => {
if (projectCache.has(projectId)) {
const cachedProject = projectCache.get(projectId);
return cachedProject;
}
try {
// Construct the path to the service account key file using the provided path or a default value
if (process.env.FIREBASE_ACCOUNT_SERVICE_PATH !== null) {
var serviceAccountPath = `${process.env.FIREBASE_ACCOUNT_SERVICE_PATH}\\${projectId}.json`;
} else {
var serviceAccountPath = `C:\\EscolarManager\\Dados\\AppsCred\\${projectId}.json`;
}
// Initialize the Firebase Admin SDK without specifying an app name
const serviceAccount = require(serviceAccountPath);
const firebaseProject = admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: `https://${projectId}.firebaseio.com`,
});
// Cache the initialized project
projectCache.set(projectId, firebaseProject);
return firebaseProject;
} catch (error) {
console.error('Error initializing Firebase project: ', error);
throw new Error('Failed to initialize Firebase project');
}
}
}
// Example usage of the factory to create new Firebase App instances
const projectFactory = createFirebaseApp('project1');
const app1 = projectFactory();
const app2 = projectFactory();
console.log(app1 === app2); // This should print false, indicating they are different instances
但它们都不起作用,结果相同。我正在缓存项目,不需要初始化同一个项目两次,但调用不同的项目是我的问题
“samthecodingman”提供的解决方案优雅地解决了这个问题。现在,我将分享完整的解决方案,并进行细微调整,这涉及导入服务。我想向他表达我的感激之情;多亏了他的帮助,我现在对这个图书馆有了更清晰的了解。它的功能类似于一组函数,允许我简单地调用函数并利用其类的内容,例如 ProjectManagement 类。
这是控制器
// Should return the list of apps id for this project
async function getAllApps(req, res) {
const projectId = req.params.projectId;
const project = getFirebaseAdminForProject(projectId);
const projectManagement = getProjectManagement(project);
try {
androidApps = await projectManagement.listAndroidApps();
iosApps = await projectManagement.listIosApps();
const allApps = [...androidApps, ...iosApps];
const appList = allApps.map((app) => ({
appId: app.appId
}));
res.status(200).json(appList);
} catch (error) {
console.error('Error retrieving apps:', error);
res.status(500).json({ error: 'Failed to retrieve apps for the project' });
}
}
所以他提供的功能
const { initializeApp, getApps, cert } = require('firebase-admin/app');
function getFirebaseAdminForProject(projectId) {
if (!projectId)
throw new Error('Project ID is required!');
let projectApp = getApps().find(app => app.name === projectId);
if (projectApp)
return projectApp;
// if here, project is not yet initialized
let serviceAccountPath = process.env.FIREBASE_ACCOUNT_SERVICE_PATH != null
? `${process.env.FIREBASE_ACCOUNT_SERVICE_PATH}\\${projectId}.json`
: `C:\\path\\to\\${projectId}.json`;
// Initialize the Firebase Admin SDK
const serviceAccount = require(serviceAccountPath);
return initializeApp({
credential: cert(serviceAccount),
databaseURL: `https://${projectId}.firebaseio.com`
}, projectId); // <-- using projectId as instance name
}
module.exports = {
getFirebaseAdminForProject
};
我想我不再需要将项目保存在地图变量中,它是在 firebase-admin 内部完成的(如果我错了,您可以在这里发表评论并解释到底在做什么)。
正如你所看到的,他提供的解决方案附带了一个非常重要的信息:firebase-admin 库中的“应用程序”是关于管理应用程序的,它实际上是项目,所以理解这一点非常重要。 谢谢Samthecodingman!
使用 '!=' '!==' 的修复也很重要。我感激不尽!
答:
根据您的代码,您可能不知道 Firebase SDK 能够初始化多个应用实例。当您在未提供实例名称的情况下进行调用时,将初始化默认实例。在上面的代码中,您多次调用,然后尝试将初始化项目存储到字典中。调用同一个实例会引发异常,并且只是 SDK 的命名空间,它不代表您刚刚初始化的应用程序(返回的值是,正如您在第二次尝试中意识到的那样)。initializeApp
admin.initializeApp(config)
admin
initializeApp
admin
initializeApp
注意:只需一个 Admin SDK 实例即可与该项目上的所有资源进行通信,为同一项目初始化多个实例是多余的。这里的“应用”不是在谈论 iOS/Android/Web 应用程序,而是一个初始化的管理 SDK 实例。
考虑到这一点,您可以将代码更新为:
// ./firebase.js
const { initializeApp, getApps } = require('firebase-admin/app');
function getFirebaseAdminForProject(projectId) {
if (!projectId)
throw new Error('Project ID is required!');
let projectApp = getApps().find(app => app.name === projectId);
if (projectApp)
return projectApp;
// if here, project is not yet initialized
let serviceAccountPath = process.env.FIREBASE_ACCOUNT_SERVICE_PATH != null // <- note != instead of !==
? `${process.env.FIREBASE_ACCOUNT_SERVICE_PATH}\\${projectId}.json`
: `C:\\path\\to\\${projectId}.json`;
return initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: `https://${projectId}.firebaseio.com`
}, projectId); // <-- using projectId as instance name
}
exports.getFirebaseAdminForProject = getFirebaseAdminForProject;
exports.project1App = getFirebaseAdminForProject('project-1');
exports.project2App = getFirebaseAdminForProject('project-2');
要获取给定应用的 Firestore,您可以使用:
const { getApp } = require('firebase-admin/app');
const { getFirestore } = require('firebase-admin/firestore');
const db = getFirestore(getApp('project-1'));
或
const { project1App } = require('./firebase');
const { getFirestore } = require('firebase-admin/firestore');
const db = getFirestore(project1App);
请考虑将 require/exports 换成现代导入/导出等效项。
评论
getApp()
admin.app()
App
getApp('project1')
admin.app('project1')
getFirestore()
admin.firebase()
App
getFirestore(someApp, 'db-ios')
admin.firestore(someApp, 'db-ios')
评论