سرور اعلان‌های فشاری بسازید

در این کد لبه، شما یک سرور اعلان‌های فشاری خواهید ساخت. سرور لیستی از اشتراک های فشار را مدیریت می کند و اعلان ها را برای آنها ارسال می کند.

کد کلاینت در حال حاضر کامل شده است – در این لبه کد، شما روی عملکرد سمت سرور کار خواهید کرد.

برنامه نمونه را ریمیکس کنید و آن را در یک تب جدید مشاهده کنید

اعلان‌ها به‌طور خودکار از برنامه Glitch تعبیه‌شده مسدود می‌شوند، بنابراین نمی‌توانید برنامه را در این صفحه پیش‌نمایش کنید. در عوض، در اینجا چه باید کرد:

  1. روی Remix to Edit کلیک کنید تا پروژه قابل ویرایش باشد.
  2. برای پیش نمایش سایت، View App را فشار دهید. سپس تمام صفحه را فشار دهید تمام صفحه .

برنامه زنده در یک برگه جدید Chrome باز می شود. در Glitch تعبیه شده، روی View Source کلیک کنید تا کد دوباره نمایش داده شود.

همانطور که از طریق این لبه کد کار می کنید، تغییراتی در کد موجود در Glitch تعبیه شده در این صفحه ایجاد کنید. برای مشاهده تغییرات، برگه جدید را با برنامه زنده خود بازخوانی کنید.

با برنامه شروع و کد آن آشنا شوید

با نگاهی به رابط کاربری مشتری برنامه شروع کنید.

در برگه جدید کروم:

  1. «Control+Shift+J» (یا «Command+Option+J» در Mac) را فشار دهید تا DevTools باز شود. روی تب Console کلیک کنید.

  2. روی دکمه‌ها در رابط کاربری کلیک کنید (کنسول توسعه‌دهنده Chrome را برای خروجی بررسی کنید).

    • Register Service Worker یک سرویسکار را برای محدوده URL پروژه Glitch شما ثبت می کند. لغو ثبت نام کارگر خدمات، کارگر خدمات را حذف می کند. اگر اشتراک پوش به آن متصل شود، اشتراک پوش نیز غیرفعال می شود.

    • اشتراک در فشار یک اشتراک فشار ایجاد می کند. فقط زمانی در دسترس است که یک سرویس‌کار ثبت‌شده باشد و یک ثابت VAPID_PUBLIC_KEY در کد کلاینت وجود داشته باشد (در ادامه در این مورد بیشتر توضیح خواهیم داد)، بنابراین هنوز نمی‌توانید روی آن کلیک کنید.

    • هنگامی که یک اشتراک فشار فعال دارید، Notify درخواست اشتراک فعلی می‌دهد که سرور یک اعلان به نقطه پایانی خود ارسال کند.

    • Notify all subscriptions به سرور می گوید که یک اعلان به تمام نقاط پایانی اشتراک در پایگاه داده خود ارسال کند.

      توجه داشته باشید که برخی از این نقاط پایانی ممکن است غیرفعال باشند. همیشه این امکان وجود دارد که یک اشتراک تا زمانی که سرور برای آن اعلان ارسال می کند ناپدید شود.

بیایید ببینیم در سمت سرور چه می‌گذرد. برای دیدن پیام‌ها از کد سرور، به گزارش Node.js در رابط Glitch نگاه کنید.

  • در برنامه Glitch، روی Tools -> Logs کلیک کنید.

    احتمالاً پیامی مانند Listening on port 3000 خواهید دید.

    اگر روی «اعلان اشتراک فعلی» یا «اعلان همه اشتراک‌ها» در رابط کاربری برنامه زنده کلیک کنید، پیام زیر را نیز خواهید دید:

    TODO: Implement sendNotifications()
    Endpoints to send to:  []
    

حالا بیایید به چند کد نگاه کنیم.

  • public/index.js حاوی کد مشتری تکمیل شده است. تشخیص ویژگی را انجام می‌دهد، سرویس‌کار را ثبت و لغو ثبت می‌کند، و اشتراک کاربر را برای فشار دادن اعلان‌ها کنترل می‌کند. همچنین اطلاعات مربوط به اشتراک های جدید و حذف شده را به سرور ارسال می کند.

    از آنجایی که قرار است فقط روی عملکرد سرور کار کنید، این فایل را ویرایش نخواهید کرد (به غیر از پر کردن ثابت VAPID_PUBLIC_KEY ).

  • public/service-worker.js یک سرویس دهنده ساده است که رویدادهای فشار را ضبط می کند و اعلان ها را نمایش می دهد.

  • /views/index.html شامل رابط کاربری برنامه است.

  • .env شامل متغیرهای محیطی است که Glitch هنگام راه اندازی آن در سرور برنامه شما بارگیری می کند. شما .env با جزئیات احراز هویت برای ارسال اعلان ها پر می کنید.

  • server.js فایلی است که در طول این کد لبه بیشتر کار خود را در آن انجام خواهید داد.

    کد شروع یک وب سرور ساده Express ایجاد می کند. چهار مورد TODO برای شما وجود دارد که در نظرات کد با TODO: . شما باید:

    در این کد لبه، شما با این موارد TODO یکی یکی کار خواهید کرد.

جزئیات VAPID را ایجاد و بارگیری کنید

اولین مورد TODO شما این است که جزئیات VAPID را تولید کنید، آنها را به متغیرهای محیط Node.js اضافه کنید و کد کلاینت و سرور را با مقادیر جدید به روز کنید.

زمینه

وقتی کاربران مشترک اعلان‌ها می‌شوند، باید به هویت برنامه و سرور آن اعتماد کنند. کاربران همچنین باید مطمئن باشند که وقتی اعلان دریافت می کنند، از همان برنامه ای است که اشتراک را تنظیم کرده است. آنها همچنین باید اعتماد کنند که هیچ کس دیگری نمی تواند محتوای اعلان را بخواند.

پروتکلی که اعلان‌های فشار را ایمن و خصوصی می‌کند، شناسایی داوطلبانه سرور برنامه برای فشار وب (VAPID) نامیده می‌شود. VAPID از رمزنگاری کلید عمومی برای تأیید هویت برنامه‌ها، سرورها و نقاط پایانی اشتراک و رمزگذاری محتوای اعلان استفاده می‌کند.

در این برنامه، از بسته web-push npm برای تولید کلیدهای VAPID و رمزگذاری و ارسال اعلان ها استفاده می کنید.

پیاده سازی

در این مرحله یک جفت کلید VAPID برای اپلیکیشن خود ایجاد کرده و به متغیرهای محیط اضافه کنید. متغیرهای محیطی را در سرور بارگذاری کنید و کلید عمومی را به عنوان یک ثابت در کد مشتری اضافه کنید.

  1. از تابع generateVAPIDKeys کتابخانه web-push برای ایجاد یک جفت کلید VAPID استفاده کنید.

    در server.js ، نظرات را از اطراف خطوط کد زیر حذف کنید:

    server.js

    // Generate VAPID keys (only do this once).
    /*
     * const vapidKeys = webpush.generateVAPIDKeys();
     * console.log(vapidKeys);
     */
    const vapidKeys = webpush.generateVAPIDKeys();
    console.log(vapidKeys);
    
  2. پس از اینکه Glitch برنامه شما را مجدداً راه اندازی کرد، کلیدهای تولید شده را به گزارش Node.js در رابط Glitch ( نه به کنسول Chrome) خروجی می دهد. برای دیدن کلیدهای VAPID، Tools -> Logs in the Glitch Interface را انتخاب کنید.

    مطمئن شوید که کلیدهای عمومی و خصوصی خود را از یک جفت کلید کپی می کنید!

    هر بار که کد خود را ویرایش می کنید، Glitch برنامه شما را مجدداً راه اندازی می کند، بنابراین اولین جفت کلیدی که ایجاد می کنید ممکن است با خروجی بیشتر از حالت نمایش خارج شود.

  3. در env. ، کلیدهای VAPID را کپی و جایگذاری کنید. کلیدها را در دو گیومه ( "..." ) قرار دهید.

    برای VAPID_SUBJECT ، می‌توانید "mailto:test@test.test" وارد کنید.

    .env

    # process.env.SECRET
    VAPID_PUBLIC_KEY=
    VAPID_PRIVATE_KEY=
    VAPID_SUBJECT=
    VAPID_PUBLIC_KEY="BN3tWzHp3L3rBh03lGLlLlsq..."
    VAPID_PRIVATE_KEY="I_lM7JMIXRhOk6HN..."
    VAPID_SUBJECT="mailto:test@test.test"
    
  4. در server.js ، دوباره آن دو خط کد را نظر دهید، زیرا فقط یک بار باید کلیدهای VAPID را تولید کنید.

    server.js

    // Generate VAPID keys (only do this once).
    /*
    const vapidKeys = webpush.generateVAPIDKeys();
    console.log(vapidKeys);
    */
    const vapidKeys = webpush.generateVAPIDKeys();
    console.log(vapidKeys);
    
  5. در server.js ، جزئیات VAPID را از متغیرهای محیط بارگیری کنید.

    server.js

    const vapidDetails = {
      // TODO: Load VAPID details from environment variables.
      publicKey: process.env.VAPID_PUBLIC_KEY,
      privateKey: process.env.VAPID_PRIVATE_KEY,
      subject: process.env.VAPID_SUBJECT
    }
    
  6. کلید عمومی را نیز کپی کرده و در کد مشتری قرار دهید.

    در public/index.js ، همان مقداری را برای VAPID_PUBLIC_KEY که در فایل env. کپی کردید وارد کنید:

    public/index.js

    // Copy from .env
    const VAPID_PUBLIC_KEY = '';
    const VAPID_PUBLIC_KEY = 'BN3tWzHp3L3rBh03lGLlLlsq...';
    ````
    

اجرای عملکرد برای ارسال اعلان ها

زمینه

در این برنامه، از بسته web-push npm برای ارسال اعلان ها استفاده خواهید کرد.

این بسته هنگام فراخوانی webpush.sendNotification() به طور خودکار اعلان‌ها را رمزگذاری می‌کند، بنابراین نیازی نیست نگران آن باشید.

web-push گزینه‌های متعددی را برای اعلان‌ها می‌پذیرد – برای مثال، می‌توانید سرصفحه‌ها را به پیام پیوست کنید و رمزگذاری محتوا را مشخص کنید.

در این کد لبه، شما فقط از دو گزینه استفاده خواهید کرد که با خطوط کد زیر تعریف شده اند:

let options = {
  TTL: 10000; // Time-to-live. Notifications expire after this.
  vapidDetails: vapidDetails; // VAPID keys from .env
};

گزینه TTL (time-to-live) یک مهلت انقضا را در یک اعلان تنظیم می کند. این راهی برای سرور برای جلوگیری از ارسال اعلان به کاربر پس از اینکه دیگر مرتبط نیست، است.

گزینه vapidDetails حاوی کلیدهای VAPID است که از متغیرهای محیط بارگیری کرده اید.

پیاده سازی

در server.js ، تابع sendNotifications را به صورت زیر تغییر دهید:

server.js

function sendNotifications(database, endpoints) {
  // TODO: Implement functionality to send notifications.
  console.log('TODO: Implement sendNotifications()');
  console.log('Endpoints to send to: ', endpoints);
  let notification = JSON.stringify(createNotification());
  let options = {
    TTL: 10000, // Time-to-live. Notifications expire after this.
    vapidDetails: vapidDetails // VAPID keys from .env
  };
  endpoints.map(endpoint => {
    let subscription = database[endpoint];
    webpush.sendNotification(subscription, notification, options);
  });
}

از آنجایی که webpush.sendNotification() یک وعده را برمی گرداند، می توانید به راحتی مدیریت خطا را اضافه کنید.

در server.js ، تابع sendNotifications دوباره تغییر دهید:

server.js

function sendNotifications(database, endpoints) {
  let notification = JSON.stringify(createNotification());
  let options = {
    TTL: 10000; // Time-to-live. Notifications expire after this.
    vapidDetails: vapidDetails; // VAPID keys from .env
  };
  endpoints.map(endpoint => {
    let subscription = database[endpoint];
    webpush.sendNotification(subscription, notification, options);
    let id = endpoint.substr((endpoint.length - 8), endpoint.length);
    webpush.sendNotification(subscription, notification, options)
    .then(result => {
      console.log(`Endpoint ID: ${id}`);
      console.log(`Result: ${result.statusCode} `);
    })
    .catch(error => {
      console.log(`Endpoint ID: ${id}`);
      console.log(`Error: ${error.body} `);
    });
  });
}

اشتراک های جدید را مدیریت کنید

زمینه

زمانی که کاربر در اعلان‌های فشار مشترک می‌شود چه اتفاقی می‌افتد:

  1. کاربر برای فشار روی Subscribe کلیک می کند.

  2. کلاینت از ثابت VAPID_PUBLIC_KEY (کلید VAPID عمومی سرور) برای ایجاد یک شیء subscription منحصر به فرد و مخصوص سرور استفاده می کند. شی subscription به صورت زیر است:

       {
         "endpoint": "https://fcm.googleapis.com/fcm/send/cpqAgzGzkzQ:APA9...",
         "expirationTime": null,
         "keys":
         {
           "p256dh": "BNYDjQL9d5PSoeBurHy2e4d4GY0sGJXBN...",
           "auth": "0IyyvUGNJ9RxJc83poo3bA"
         }
       }
    
  3. کلاینت یک درخواست POST را به نشانی اینترنتی /add-subscription ارسال می کند، از جمله اشتراک به عنوان JSON رشته ای در بدنه.

  4. سرور subscription رشته ای را از بدنه درخواست POST بازیابی می کند، آن را به JSON تجزیه می کند و به پایگاه داده اشتراک ها اضافه می کند.

    پایگاه داده اشتراک ها را با استفاده از نقاط پایانی خود به عنوان یک کلید ذخیره می کند:

    {
      "https://fcm...1234": {
        endpoint: "https://fcm...1234",
        expirationTime: ...,
        keys: { ... }
      },
      "https://fcm...abcd": {
        endpoint: "https://fcm...abcd",
        expirationTime: ...,
        keys: { ... }
      },
      "https://fcm...zxcv": {
        endpoint: "https://fcm...zxcv",
        expirationTime: ...,
        keys: { ... }
      },
    }

اکنون، اشتراک جدید برای ارسال نوتیفیکیشن در اختیار سرور قرار گرفته است.

پیاده سازی

درخواست‌ها برای اشتراک‌های جدید به مسیر /add-subscription می‌آیند که یک URL POST است. در server.js یک کنترل کننده مسیر خرد خواهید دید:

server.js

app.post('/add-subscription', (request, response) => {
  // TODO: implement handler for /add-subscription
  console.log('TODO: Implement handler for /add-subscription');
  console.log('Request body: ', request.body);
  response.sendStatus(200);
});

در اجرای شما، این کنترل کننده باید:

  • اشتراک جدید را از متن درخواست بازیابی کنید.
  • دسترسی به پایگاه داده اشتراک های فعال
  • اشتراک جدید را به لیست اشتراک های فعال اضافه کنید.

برای رسیدگی به اشتراک های جدید:

  • در server.js ، راهنمای مسیر را برای /add-subscription به صورت زیر تغییر دهید:

    server.js

    app.post('/add-subscription', (request, response) => {
      // TODO: implement handler for /add-subscription
      console.log('TODO: Implement handler for /add-subscription');
      console.log('Request body: ', request.body);
      let subscriptions = Object.assign({}, request.session.subscriptions);
      subscriptions[request.body.endpoint] = request.body;
      request.session.subscriptions = subscriptions;
      response.sendStatus(200);
    });

رسیدگی به لغو اشتراک

زمینه

سرور همیشه نمی داند چه زمانی یک اشتراک غیرفعال می شود – برای مثال، زمانی که مرورگر سرویس کارگر را خاموش می کند، اشتراک می تواند پاک شود.

با این حال، سرور می تواند در مورد اشتراک هایی که از طریق رابط کاربری برنامه لغو شده اند مطلع شود. در این مرحله، عملکردی را برای حذف یک اشتراک از پایگاه داده پیاده سازی خواهید کرد.

به این ترتیب، سرور از ارسال یک دسته اعلان‌ها به نقاط پایانی موجود اجتناب می‌کند. بدیهی است که این واقعاً با یک برنامه آزمایشی ساده مهم نیست، اما در مقیاس بزرگتر مهم می شود.

پیاده سازی

درخواست‌های لغو اشتراک به /remove-subscription POST URL می‌آیند.

کنترل کننده مسیر خرد در server.js به شکل زیر است:

server.js

app.post('/remove-subscription', (request, response) => {
  // TODO: implement handler for /remove-subscription
  console.log('TODO: Implement handler for /remove-subscription');
  console.log('Request body: ', request.body);
  response.sendStatus(200);
});

در اجرای شما، این کنترل کننده باید:

  • نقطه پایانی اشتراک لغو شده را از متن درخواست بازیابی کنید.
  • دسترسی به پایگاه داده اشتراک های فعال
  • اشتراک لغو شده را از لیست اشتراک های فعال حذف کنید.

بدنه درخواست POST از مشتری حاوی نقطه پایانی است که باید حذف کنید:

{
  "endpoint": "https://fcm.googleapis.com/fcm/send/cpqAgzGzkzQ:APA9..."
}

برای رسیدگی به لغو اشتراک:

  • در server.js ، کنترلر مسیر را برای /remove-subscription به صورت زیر تغییر دهید:

    server.js

  app.post('/remove-subscription', (request, response) => {
    // TODO: implement handler for /remove-subscription
    console.log('TODO: Implement handler for /remove-subscription');
    console.log('Request body: ', request.body);
    let subscriptions = Object.assign({}, request.session.subscriptions);
    delete subscriptions[request.body.endpoint];
    request.session.subscriptions = subscriptions;
    response.sendStatus(200);
  });