迁移Firebase至Secret Manager后Slack/Bolt初始化报错的解决方法
问题
我们通过Firebase结合Slack/bolt开发机器人,此前用functions.config()管理Slack令牌和密钥,现在计划迁移至Secret Manager。改写后的代码如下:
// boltapp.js const functions = require("firebase-functions"); const { App, ExpressReceiver, subtype } = require("@slack/bolt"); const config = functions.config(); const expressReceiver = new ExpressReceiver({ signingSecret: process.env.SLACK_SECRET, endpoints: "/events", processBeforeResponse: true, }); const app = new App({ receiver: expressReceiver, token: process.env.SLACK_TOKEN }); app.error(error => { console.error(error) }); app.use(async ({ client, payload, context, next }) => { console.info('It\'s payload', JSON.stringify(payload)) if (!context?.retryNum) { await next(); } else { console.debug('app.use.context', context); } }); //**bot processing**// // https://{your domain}.cloudfunctions.net/slack/events module.exports = functions .runWith({ secrets: ["SLACK_TOKEN","SLACK_SECRET"] }) .https.onRequest(expressReceiver.app);
迁移后出现以下报错:
Failed to load function definition from source: FirebaseError: Failed to load function definition from source: Failed to generate manifest from function source: Error: Apps used in a single workspace can be initialized with a token. Apps used in many workspaces should be initialized with oauth installer options or authorize.
Since you have not provided a token or authorize, you might be missing one or more required oauth installer options. See https://slack.dev/bolt-js/concepts#authenticating-oauth for these required fields.
请问如何修改代码以避免该错误?
解决方案
错误核心是函数初始化阶段,process.env.SLACK_TOKEN还未被Secret Manager注入,导致Bolt检测到令牌为undefined,误判你要构建多工作区应用。以下两种方法可修复:
方法一:延迟初始化Bolt App
将App的初始化逻辑放到请求处理流程中,确保环境变量已加载完成:
// boltapp.js const functions = require("firebase-functions"); const { App, ExpressReceiver } = require("@slack/bolt"); const expressReceiver = new ExpressReceiver({ signingSecret: process.env.SLACK_SECRET, endpoints: "/events", processBeforeResponse: true, }); // 动态初始化Bolt App expressReceiver.router.use(async (req, res, next) => { if (!req.app.locals.boltApp) { req.app.locals.boltApp = new App({ receiver: expressReceiver, token: process.env.SLACK_TOKEN }); // 挂载中间件与错误处理 req.app.locals.boltApp.error(error => { console.error(error) }); req.app.locals.boltApp.use(async ({ payload, context, next }) => { console.info('It\'s payload', JSON.stringify(payload)) if (!context?.retryNum) { await next(); } else { console.debug('app.use.context', context); } }); // 在这里添加你的bot业务处理逻辑 // req.app.locals.boltApp.command('/your-command', async ({ command, ack }) => { ... }); } next(); }); module.exports = functions .runWith({ secrets: ["SLACK_TOKEN","SLACK_SECRET"] }) .https.onRequest(expressReceiver.app);
方法二:提前验证环境变量
在初始化App前主动检查令牌和密钥是否存在,避免Bolt误判:
// boltapp.js const functions = require("firebase-functions"); const { App, ExpressReceiver } = require("@slack/bolt"); // 提前校验环境变量 const slackToken = process.env.SLACK_TOKEN; const slackSecret = process.env.SLACK_SECRET; if (!slackToken || !slackSecret) { throw new Error("SLACK_TOKEN或SLACK_SECRET未从Secret Manager正确加载"); } const expressReceiver = new ExpressReceiver({ signingSecret: slackSecret, endpoints: "/events", processBeforeResponse: true, }); const app = new App({ receiver: expressReceiver, token: slackToken }); app.error(error => { console.error(error) }); app.use(async ({ payload, context, next }) => { console.info('It\'s payload', JSON.stringify(payload)) if (!context?.retryNum) { await next(); } else { console.debug('app.use.context', context); } }); // 添加你的bot业务处理逻辑 module.exports = functions .runWith({ secrets: ["SLACK_TOKEN","SLACK_SECRET"] }) .https.onRequest(expressReceiver.app);
额外注意:需确保Firebase控制台的Secret Manager中已正确创建SLACK_TOKEN和SLACK_SECRET密钥,且函数拥有访问这些密钥的权限。
内容的提问来源于stack exchange,提问作者go600

