使用 Deno 进行 Web 推送

Web Push with Deno

提问人:Mark Tyers 提问时间:11/2/2023 更新时间:11/2/2023 访问量:47

问:

我正在构建一个简单的概念证明,尝试使用 Deno 启用网络推送。似乎没有任何作为 Deno 模块的原生实现,所以我一直在使用 NodeJS 库。这是基于一篇 Medium 文章

我运行了以下命令来生成密钥:

$ deno run --allow-read npm:[email protected] generate-vapid-keys

经过大量修补,我得到了一些代码,但它在发送通知时抛出错误:

error: Uncaught (in promise) TypeError: Invalid RSA private key
    at SignImpl.sign (ext:deno_node/internal/crypto/sig.ts:48:33)
    at sign (file:///home/codio/.cache/deno/npm/registry.npmjs.org/jwa/2.0.0/index.js:152:45)
    at Object.sign (file:///home/codio/.cache/deno/npm/registry.npmjs.org/jwa/2.0.0/index.js:200:27)
    at Object.jwsSign [as sign] (file:///home/codio/.cache/deno/npm/registry.npmjs.org/jws/4.0.0/lib/sign-stream.js:32:24)
    at Object.getVapidHeaders (file:///home/codio/.cache/deno/npm/registry.npmjs.org/web-push/3.6.6/src/vapid-helper.js:226:19)
    at WebPushLib.generateRequestDetails (file:///home/codio/.cache/deno/npm/registry.npmjs.org/web-push/3.6.6/src/web-push-lib.js:278:40)
    at WebPushLib.sendNotification (file:///home/codio/.cache/deno/npm/registry.npmjs.org/web-push/3.6.6/src/web-push-lib.js:341:29)
    at file:///home/codio/workspace/app.js:37:13
    at Layer.handle [as handle_request] (file:///home/codio/.cache/deno/npm/registry.npmjs.org/express/4.18.2/lib/router/layer.js:95:5)
    at next (file:///home/codio/.cache/deno/npm/registry.npmjs.org/express/4.18.2/lib/router/route.js:144:13)

代码如下

import_map.json

{
  "imports": {
    "express": "npm:[email protected]",
    "web-push": "npm:[email protected]",
    "cors": "npm:[email protected]"
  }
}

// app.js

import express from 'express'

const app = express()

import webpush from 'web-push'
import cors from 'cors'

const port = 8080

app.use(cors())
app.use(express.json())
app.use(express.static('public'))

const subDatabse = []

const apiKeys = {
  pubKey: 'BMmS-0NgSh8tRe0JBMKuVbeAYUQDju4m5jaQaVV4XuuH59ySZttn_SqlWnLO2lw4o7pDo7A0dMYRRbu9Rr59zFY',
  privKey: 'ERZAl-oGmLZRur-wIyRO-1WM2dNSlipINkX71q9oUwQ'
}

webpush.setVapidDetails('mailto:[email protected]', apiKeys.pubKey, apiKeys.privKey)

app.get('/', (req, res) => res.send('Hello world'))

app.post('/save-subscription', (req, res) => {
  subDatabse.push(req.body)
  console.log(subDatabse)
  res.json({ status: 'Success', message: 'Subscription saved!' })
})

app.get('/send-notification', (req, res) => {
  console.log('send-notification')
  if (subDatabse.length === 0) throw new Error('no subscriptions')
  console.log(subDatabse[0])
  webpush.sendNotification(subDatabse[0], 'Hello world')
  res.json({ 'statue': 'Success', 'message': 'Message sent to push service' })
})

app.listen(port, () => {
  console.log(`Server running on port ${port}`)
})

<!DOCTYPE html>
<html>
  <head>
    <script src="script.js" defer></script>
  </head>
  <body>
    <button onclick="main()">Enable notification</button>
    <button id="register">Register</button>
  </body>
</html>
// script.js

const checkPermission = () => {
  if (!('serviceWorker' in navigator)) throw new Error("No support for service worker!")
  if (!('Notification' in window)) throw new Error("No support for notification API")
  if (!('PushManager' in window)) throw new Error("No support for Push API")
}

const registerSW = async () => {
  console.log('registerSW')
  const registration = await navigator.serviceWorker.register('sw.js')
  return registration
}

const requestNotificationPermission = async () => {
  const permission = await Notification.requestPermission();
  if (permission !== 'granted') throw new Error('Notification permission not granted')
}

const main = async () => {
  console.log('enabling notifications')
  checkPermission()
  await requestNotificationPermission()
  await registerSW()
}

navigator.serviceWorker.ready.then((registration) => {
  document.querySelector('button#register').addEventListener('click', event => {
    navigator.serviceWorker.ready.then( registration => {
      registration.active.postMessage({ type: 'subscribe' })
    })
  })
})
// sw.js

const serverPort = 8080

const urlBase64ToUint8Array = base64String => {
  const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
  const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/');
  const rawData = atob(base64);
  const outputArray = new Uint8Array(rawData.length);
  for (let i = 0; i < rawData.length; ++i) outputArray[i] = rawData.charCodeAt(i)
  return outputArray;
}

const saveSubscription = async subscription => {
  const response = await fetch(`https://roundtower-iconricardo-8080.codio-box.uk/save-subscription`, {
    method: 'post',
    headers: { 'Content-type': 'application/json' },
    body: JSON.stringify(subscription)
  })
  return response.json()
}

self.addEventListener('install', async event => {
  self.skipWaiting()
})

self.addEventListener('activate', event => console.log('activate'))

self.addEventListener('message', async event => {
  if (event.data && event.data.type === 'subscribe') {
    console.log('subscribe message')
    const subscription = await self.registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: urlBase64ToUint8Array('BMmS-0NgSh8tRe0JBMKuVbeAYUQDju4m5jaQaVV4XuuH59ySZttn_SqlWnLO2lw4o7pDo7A0dMYRRbu9Rr59zFY')
    })
    const response = await saveSubscription(subscription)
    console.log(response)
  }
})

self.addEventListener('push', event => {
  self.registration.showNotification('Wohoo!!', { body: event.data.text() })
})
JavaScript 浏览器 Service-Worker deno web-push

评论

0赞 jabaa 11/2/2023
您是否检查过 RSA 私钥?它似乎是无效的。
0赞 Mark Tyers 11/2/2023
公钥和私钥都存在于 ~/.ssh 目录中,并且已与 GitHub 推送一起使用,因此我假设它们是有效的:authorized_keys id_rsa id_rsa.pub
0赞 jabaa 11/2/2023
是否使用调试器单步执行代码并检查了变量的值?
0赞 Mark Tyers 11/3/2023
我在云IDE中运行此代码,而无需访问调试器。
1赞 jabaa 11/3/2023
创建一个最小的可重现示例并在本地进行调试。

答: 暂无答案