如何在 React Service Worker 中使用 process.env

问题描述 投票:0回答:5

我正在尝试设置 Firebase-messaging-sw.js 文件(用于网络推送通知)。我想知道是否有办法尽可能避免将我的 Firebase 配置数据公开给公众 - 尽管它可能会被泄露? (我不太确定其中的细微差别)

我尝试过以下操作:如何根据环境变量自定义我的 Service Worker?但是答案的 swEnvbuild 似乎没有运行,因为找不到 swenv.js 文件。我怀疑它可能需要在 React 中进行不同的设置?

(第一个问题,请随时对我的问题提出建设性的批评)

reactjs firebase-cloud-messaging service-worker web-push
5个回答
12
投票

我最近不得不使用 CRA 应用程序来执行此操作,找到相关信息并不容易,所以我认为我应该分享我的解决方案。假设您已经在

serviceWorker.unregister()
中将
serviceWorker.register()
更改为
./src/index.js
,并且在项目根目录中有一个包含变量集的
.env
文件,那么您可以更新
./src/serviceWorker.js
以包含您的
process.env
变量作为查询字符串。

register
中的
serviceWorker.js
函数中,更新
const swUrl
,如下所示,注意
const firebaseConfig
w/process.env,在
swUrl
..
之前声明 ./src/serviceWorker.js:

// Convert environment variables to URL `search` parameters
const firebaseConfig = new URLSearchParams({
  apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
  authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
  databaseURL: process.env.REACT_APP_FIREBASE_DATABASE_URL,
  projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
  storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.REACT_APP_FIREBASE_APP_ID,
  measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENT_ID
}).toString();

// Service worker URL w/config variables
const swUrl = `${process.env.PUBLIC_URL}/firebase-messaging-sw.js?${firebaseConfig}`;

然后在

./public/firebase-messaging-sw.js
(如果不存在则创建它),你可以执行如下操作..
./public/firebase-messaging-sw.js

importScripts('https://www.gstatic.com/firebasejs/8.0.2/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.0.2/firebase-messaging.js');

// Set Firebase configuration, once available
self.addEventListener('fetch', () => {
  const urlParams = new URLSearchParams(location.search);
  self.firebaseConfig = Object.fromEntries(urlParams);
});

// "Default" Firebase configuration (prevents errors)
const defaultConfig = {
  apiKey: true,
  projectId: true,
  messagingSenderId: true,
  appId: true,
};

// Initialize Firebase app
firebase.initializeApp(self.firebaseConfig || defaultConfig);
const messaging = firebase.messaging();

// Configure message handler (assumes backend is set up)
messaging.onBackgroundMessage((payload) => {
  const { icon, body, title } = payload.data;
  self.registration.showNotification(title, { body, icon });
});    

如果有更理想的解决方案,很想听听,但这个配置对我有用。


11
投票

我发现这篇文章使用cra-append-sw来附加环境变量。然后我在我的

pre
中创建了两个
package.json
脚本。当我运行
npm start
时,
prestart
脚本会运行创建一个包含环境变量的
[root folder]/public/firebase-messaging-sw.js
文件(经过 webpack 处理后)。

实施

我创建了一个

[root folder]/firebase-messaging-sw.js
。该文件将由 webpack 处理,替换环境变量的值。

// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here. Other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/8.1.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.1.1/firebase-messaging.js');

// Initialize the Firebase app in the service worker by passing in
// your app's Firebase config object.
// https://firebase.google.com/docs/web/setup#config-object
firebase.initializeApp({
    apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
    authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
    databaseURL: process.env.REACT_APP_FIREBASE_DATABASE_URL,
    projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
    storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
    messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
    appId: process.env.REACT_APP_FIREBASE_APP_ID,
    measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENT_ID,
});

// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function (payload) {
    console.log('[firebase-messaging-sw.js] Received background message ', payload);
    // Customize notification here
    const notificationTitle = 'Background Message Title';
    const notificationOptions = {
        body: 'Background Message body.',
        icon: '/logo.png'
    };

    self.registration.showNotification(notificationTitle, notificationOptions);
});

然后我有

[root folder]/.env.dev
[root folder]/.env.prod

REACT_APP_FIREBASE_API_KEY=A...
REACT_APP_FIREBASE_AUTH_DOMAIN=d...
REACT_APP_FIREBASE_DATABASE_URL=h...
REACT_APP_FIREBASE_PROJECT_ID=d...
REACT_APP_FIREBASE_STORAGE_BUCKET=d...
REACT_APP_FIREBASE_MESSAGING_SENDER_ID=7...
REACT_APP_FIREBASE_APP_ID=1...
REACT_APP_FIREBASE_MEASUREMENT_ID=G...

最后,我在我的

pre
 中添加了 2 个 
package.json

脚本
...
"scripts": {
    "prestart": "cra-append-sw --mode dev --env ./.env.dev ./firebase-messaging-sw.js",
    "prebuild": "cra-append-sw --mode build --env ./.env.prod ./firebase-messaging-sw.js",
...

0
投票

我自己在这方面确实遇到了麻烦。在您的环境启动之前,服务工作人员会以堆栈方式参与其中,因此它无法访问您的

.Env
变量是有道理的。

我的解决方案

我构建了一个 npm 模块,在构建时使用 webpack 从 .env 文件中提取“安全”版本控制变量,并将它们放入独立的 JS 文件中。然后,您可以继续导入此文件并在 Service Worker 中使用它。

https://www.npmjs.com/package/vue-enverywhere

免责声明:

我知道这是针对 vue 的,但它是 webpack,而且不是 vue 特定的。另外,您可能最好只复制代码,而不使用该模块。 这对我自己来说更是一次有趣的练习:)


0
投票

在index.js文件中(或者任何你想注册服务工作者的地方):

if ("serviceWorker" in navigator) {
  console.log("Registration started");
  const firebaseConfig = encodeURIComponent(
    JSON.stringify({
      apiKey: process.env.FCM_API_KEY,
      projectId: process.env.FCM_PROJECT_ID,
      messagingSenderId: process.env.FCM_SENDER_ID,
      appId: process.env.FCM_APP_ID,
    })
  );
  navigator.serviceWorker
    .register(
      `../../../firebase-messaging-sw.js?firebaseConfig=${firebaseConfig}`
    )
    .then(function (registration) {
      console.log("Registration successful, scope is:", registration.scope);
    })
    .catch(function (err) {
      console.log("Service worker registration failed, error:", err);
    });

在 Service Worker 中,firebase-messaging-sw.js :

importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js");
importScripts(
  "https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js"
);

firebase.initializeApp(
  JSON.parse(new URL(location).searchParams.get("firebaseConfig"))
);

firebase.messaging();

这足以在发送notification-type消息时接收后台推送通知。

这个解决方案会起作用, 只需记住根据您的项目编辑 Service-worer 文件的路径(在 index.js 等中注册时), 否则会给出“Mime Error”。


0
投票

将 Firebase 密钥显示在 Service Worker 文件页面上就可以了。请参阅此答案(Google Cloud Collective 推荐)以更好地理解它。

现在为了实现为较低环境和生产环境提供两个不同的 Firebase Service Worker 文件的最终目标,我已将以下代码添加到我的 Angular 应用程序代码库中的 middleware.ts 文件中。如果您使用 js 文件作为应用程序中的主要语言,您也可以将其添加到 js 文件中。将此文件保存到您的 src 文件夹(对于 Angular 应用程序)或 Pages 文件夹(如果使用 JS 框架)。

import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
export function middleware(request: NextRequest) {
  if (request.nextUrl.pathname.startsWith('/firebase-messaging-sw.js')) {
    if (request.nextUrl.hostname === 'your-website-prod-hostname.com') {
      return NextResponse.rewrite(new URL('/firebase-messaging-sw-prod.js', request.url));
    }
    return NextResponse.rewrite(new URL('/firebase-messaging-sw-dev.js', request.url));
  }
}

export const config = {
  matcher: '/firebase-messaging-sw*',
}
© www.soinside.com 2019 - 2024. All rights reserved.