Server

Cài đặt expressjs + ejs bằng dòng lệnh

express -e

Cài đặt thêm module body-parser, webpush, dotenv

yarn add dotenv, body-parser, web-push

Ở file app.js sẽ được

require('dotenv').config({ path: `.env` });
var createError = require('http-errors');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var debug = require('debug')('vision-notification:server');
var http = require('http');
const bodyParser = require('body-parser');
var express = require('express');
var app = express();
var server = http.createServer(app);

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(bodyParser.json());
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

server.listen(process.env.PORT || 3000);

Tạo file .env với các biến môi trường

PORT = 5005
PUBLIC_VAPID_KEY="BJ8MNpjj2_cNG02uDP3WcKEaI1dR4hUIERoEqI9RjLGeRIV8Ziy2TSmUuv9T2YrQKLgnWajN8ACoDaBMrFsQVRg"
PRIVATE_VAPID_KEY="ISRHeHIdMvK83dqN_srrqSpjrv1G3u80lXu08TrNgd4"

Lưu ý publickey và private key có thể tạo tại trang: https://vapidkeys.com/
Tại file index.js ở thư mục routes chúng ta sẽ thêm api để đăng ký nhận thông báo và gửi thông báo

var express = require('express');
var router = express.Router();
const webPush = require('web-push');
const publicVapidKey = process.env.PUBLIC_VAPID_KEY;
const privateVapidKey = process.env.PRIVATE_VAPID_KEY;

webPush.setVapidDetails('mailto:[email protected]', publicVapidKey, privateVapidKey);

let list_sub = []; //lưu lại danh sách các thiết bị nhận thông báo

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index');
});

router.post('send-notification', async function(req, res, next) {
    //send to notification
      const payload = JSON.stringify({
        title: `Tiêu đề thông báo`,
        body: 'Nội dung thông báo'
      });
      //lặp các phần tử của danh sách thông báo đã lưu để gửi thông báo
      for(let i = 0; i <list_sub.length; i++) { webPush.sendNotification(list_sub[i], payload) .catch(error => console.error(error));
      }
    }

  return res.send({status: true});
});

//đăng ký nhận thông báo
router.post('/subscribe', (req, res) => {
  const subscription = req.body;
  //lưu người đăng ký nhận thông báo vào danh sách
  list_sub.push(subscription);

  res.status(201).json({});

  const payload = JSON.stringify({
    title: 'Đăng ký nhận thông tin trạng thái hệ thống thành công!',
    body: 'Bật Chrome để nhận được thông báo'
  });

  webPush.sendNotification(subscription, payload)
    .catch(error => console.error(error));
});

module.exports = router;

Client

Mở thư mục public, tạo thư mục javascripts và 2 file main.js và sw.js nằm bên trong nó
File main.js sẽ chứa nội dung

const publicVapidKey = "BJ8MNpjj2_cNG02uDP3WcKEaI1dR4hUIERoEqI9RjLGeRIV8Ziy2TSmUuv9T2YrQKLgnWajN8ACoDaBMrFsQVRg";

async function triggerPushNotification() {
  if ('serviceWorker' in navigator) {
    await navigator.serviceWorker.register('/javascripts/sw.js', {
      scope: '/javascripts/'
    }).then(reg => {
      reg.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(publicVapidKey),
      }).then(
        // (B3-1) OK - TEST PUSH NOTIFICATION
        sub => {
          fetch("/subscribe", {
            method: "POST",
            body: JSON.stringify(sub),
            headers: { "content-type": "application/json" }
          })
          .then(res => res.text())
          .then(txt => console.log(txt))
          .catch(err => console.error(err));
        },

        // (B3-2) ERROR!
        err => console.error(err)
      );
    });

  } else {
    console.error('Service workers are not supported in this browser');
  }
}

triggerPushNotification();

function urlBase64ToUint8Array(base64String) {
  const padding = '='.repeat((4 - base64String.length % 4) % 4);
  const base64 = (base64String + padding)
    .replace(/-/g, '+')
    .replace(/_/g, '/');

  const rawData = window.atob(base64);
  const outputArray = new Uint8Array(rawData.length);

  for (let i = 0; i < rawData.length; ++i) {
    outputArray[i] = rawData.charCodeAt(i);
  }
  return outputArray;
}

Lưu ý: publickey phải giống với publickey lưu trong file .env
Nội dung của file sw.js

self.addEventListener('push', event => {
  const data = event.data.json();

  self.registration.showNotification(data.title, {
    body: data.body,
  });
});

Cuối cùng mở thư mục views và sửa file index.ejs như sau

<!DOCTYPE html>
<html>
<head>
<title>Notifications</title>
<script src="/javascripts/main.js"></script>
</head>
<body>
<h1>Notifications</h1>
</body>
</html>

Chạy server và kiểm tra kết quả

Leave a Reply

Your email address will not be published. Required fields are marked *