راهنمای ارسال پوش نوتیفیکیشن از اپلیکیشن‌های Django
راهنمای ارسال پوش نوتیفیکیشن از اپلیکیشن‌های Django

دنیای وب به‌طور مداوم درحال‌توسعه و تکامل است و حالا دیگر به کارکردهایی در آن امکان‌پذیر شده که پیش‌ازاین تنها در دستگاه‌های موبایل قابل‌دسترسی بود. توانایی‌های تازه وب مخصوصاً با معرفی سرویس ورکرهای جاوا اسکریپت (JavaScript Service Worker) افزایش یافت و حالا می‌تواند درزمینهٔ همگام‌سازی پس‌زمینه، کش کردن آفلاین و ارسال پوش نوتیفیکیشن هم از آن بهره گرفت.

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

در این راهنما یک اپلیکیشن Django روی اوبونتو ۱۸.۰۴ می‌سازیم تا هرگاه فعالیتی نیازمند بازدید کاربر از اپلیکیشن‌ها وجود داشته باشد، پوش نوتیفیکیشن‌ها را برای او ارسال کند. برای ایجاد این نوع نوتیفیکیشن‌ها و اعلان‌های سریع باید از بسته Django-Webposh استفاده کرده و یک سرویس ورکر برای نمایش نوتیفیکیشن‌ها روی کلاینت ثبت و راه‌اندازی کنیم. اپلیکیشن فعال با چنین نوتیفیکیشن‌هایی باید به شکل زیر ظاهر شود:

Django-Webposh-final

پیش‌نیازها برای ارسال push notification

پیش از آغاز این راهنما باید موارد زیر را آماده کنیم:

  • یک سرور اوبونتو ۱۸.۰۴ با کاربر غیر روت (non-root) و فایروال فعال.
  • نصب pip و venv.
  • ایجاد یک پروژه Django به نام djangopush در دایرکتوری home و همچنین افزودن آدرس IP به دایرکتیو ALLOWED-HOSTS در فایل py.

مرحله اول – نصب Django-Webpush و دریافت Vapid Key

Django-Webpush بسته‌ای است که توسعه‌دهندگان را قادر می‌سازد پوش نوتیفیکیشن‌ها را با اپلیکیشن‌های Django ادغام و ارسال کنند. ما هم از این بسته برای تلنگر زدن و ارسال نوتیفیکیشن‌ها در اپلیکیشن خود استفاده می‌کنیم. در این مرحله باید Django-Webpush را نصب‌کرده و کلیدهای شناسایی داوطلبانه سرور اپلیکیشن (VAPID) را دریافت کنیم. این کلیدها برای شناسایی سرور و تضمین یکتا بودن هر درخواست ضروری هستند.

اطمینان حاصل کنید درون همان دایرکتوری پروژه ~/ djangopush که در بخش پیش‌نیازها ایجاد کردید، قرار دارید:

 $ cd ~/djangopush

محیط مجازی را فعال کنید:

source my_env/bin/activate

Pip را به تازه‌ترین نسخه به‌روزرسانی کنید:

(my_env) $ pip install --upgrade pip

Django-Webpush را نصب کنید:

(my_env)pip install django-webpush

پس از نصب بسته، آن را به فهرست اپلیکیشن‌های فایل settings.py خود اضافه کنید. ابتدا settings.py را بازکنید:

(my_env)nano ~/djangopush/djangopush/settings.py

Webpush را به فهرست INSTALLED_APPS اضافه کنید:

~/djangopush/djangopush/settings.py
...

INSTALLED_APPS = [
    ...,
    'webpush',
]
...

فایل را ذخیره کنید و از ویرایشگر خارج شوید

حالا migrations را روی اپلیکیشن به اجرا درآورید تا تغییرات روی الگوی پایگاه داده‌ها (Schema) اعمال شوند:

(my_env)python manage.py migrate

خروجی اجرای migration موفقیت‌آمیز بدین شکل خواهد بود:

Output
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, webpush
Running migrations:
  Applying webpush.0001_initial... OK

مرحله بعدی در تنظیم پوش نوتیفیکیشن وب دریافت کلیدهای VAPID است. این کلیدها سرور اپلیکیشن را شناسایی می‌کنند و می‌توان از آن‌ها برای کاهش پنهان‌کاری در پوش نوتیفیکیشن‌های آدرس‌های URL استفاده کرد؛ زیرا تعداد ثبت‌نام و عضویت را در هر سرور به‌خصوص محدود می‌کنند.

برای کسب کلیدهای VAPID به مسیر اپلیکیشن وب web-push-codelab بروید. در این بخش می‌توانید کلیدهایی را که به‌صورت خودکار ایجاد می‌شوند دریافت کنید. کلیدهای عمومی و اختصاصی را کپی کنید.

حالا برای اطلاعات VAPID خود یک ورودی تازه در settings.py بسازید. ابتدا این فایل را بازکنید:

(my_env)nano ~/djangopush/djangopush/settings.py

سپس دایرکتیو تازه‌ای را به نام WEBPUSH_SETTINGS با کلیدهای عمومی و اختصاصی VAPID اضافه کنید و ایمیل خود را هم زیر AUTH_PASSWORD_VALIDATORS درج کنید:

~/djangopush/djangopush/settings.py
...

AUTH_PASSWORD_VALIDATORS = [
    ...
]

WEBPUSH_SETTINGS = {
   "VAPID_PUBLIC_KEY": "your_vapid_public_key",
   "VAPID_PRIVATE_KEY": "your_vapid_private_key",
   "VAPID_ADMIN_EMAIL": "admin@example.com"
}

# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/

...

فراموش نکنید که باید مقادیر پیش‌فرض your_vapid_public_key، your_vapid_privaye_key و admin@example.com را با مقادیر و اطلاعات خودتان تغییر دهید. ایمیل خود را به این منظور وارد می‌کنید تا هر زمان سرورهای پوش با مشکلی مواجه شدند، مطلع شوید.

در مرحله بعدی view‌هایی ایجاد می‌کنیم تا صفحه اصلی اپلیکیشن را برای کاربر به نمایش درآورند و پوش نوتیفیکیشن‌ها را هم به کاربران عضو (subscribed) ارسال کنند.

مرحله دوم – راه‌اندازی View

در این مرحله یک view ابتدای home با HttpResponse برای صفحه اصلی و همچنین یک view به نام send_push می‌سازیم. View ها درواقع تابع‌هایی هستند که پاسخ‌ها را از درخواست‌های وب بازگشت می‌دهند. Send_push از کتابخانه درون Django-Webpush برای ارسال نوتیفیکیشن‌هایی استفاده می‌کند که شامل داده‌های واردشده توسط کاربر درون صفحه خانه هستند.

به پوشه ~/ djangopush/djangopush بروید:

(my_env) $cd ~/djangopush/djangopush

اجرا دستور ۱s درون این پوشه فایل‌های اصلی درون آن را برایتان به نمایش درمی‌آورد:

Output
/__init__.py
/settings.py
/urls.py
/wsgi.py

فایل‌های درون این پوشه به‌صورت خودکار توسط ابزار Django-admin که برای ایجاد پروژه در مرحله پیش‌نیازها از آن استفاده کردید، ایجاد می‌شوند. فایل settings.py شامل پیکربندی‌های پروژه مانند اپلیکیشن‌های نصب‌شده و پوشه استاتیک روت می‌شود. فایل urls,py شامل پیکربندی‌های URL درون پروژه است. در همین بخش باید مسیرهای مناسب را مطابق با view های ایجادشده تنظیم کنید.

یک فایل تازه در دایرکتوری ~/ djangopush/djangopush به نام views.py ایجاد کنید. این فایل باید شامل view های درون پروژه شود:

(my_env) $ nano ~/djangopush/djangopush/views.py

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

~/djangopush/djangopush/views.py
from django.http.response import HttpResponse
from django.views.decorators.http import require_GET

@require_GET
def home(request):
    return HttpResponse('<h1>Home Page<h1>')

Home با decorator require_GET دکوریت شده است که view را به درخواست‌های GET محدود می‌سازد. View به‌طورکلی پاسخ را به هر درخواست مربوط ارجاع می‌دهد. این view یک تگ ساده HTML را به‌عنوان پاسخ بازمی‌گرداند.

View بعدی که باید ایجاد کنیم، send_push است که پوش نوتیفیکیشن‌های ارسال‌شده را به کمک بسته Django-webpush مدیریت می‌کند. این view هم به درخواست‌های POST محدودشده و به همین خاطر از محافظت در مقابل Cross Site Request Forgery) CSRF) معاف شده است. ایجاد این view شمارا قادر می‌سازد بتوانید آن را به کمک Postman یا هر خدمات RESTful دیگری مورد آزمون قرار دهید. البته در محیط production حتماً باید این decorators را حذف کنید تا view در مقابل CSRF آسیب‌پذیر نشود.

برای ایجاد send_push ابتدا ورودی زیر را اضافه کنید تا پاسخ‌های JSON فعال شوند و به تابع send_user_notification در کتابخانه webpush دسترسی پیدا کنید:

~/djangopush/djangopush/views.py
from django.http.response import JsonResponse, HttpResponse
from django.views.decorators.http import require_GET, require_POST
from django.shortcuts import get_object_or_404
from django.contrib.auth.models import User
from django.views.decorators.csrf import csrf_exempt
from webpush import send_user_notification
import json

سپس decorators require_POST را اضافه کنید. این decorator از بدنه (متن) درخواست ارسالی توسط کاربر برای ایجاد و تلنگر زدن به پوش نوتیفیکیشن استفاده می‌کند.

~/djangopush/djangopush/views.py
@require_GET
def home(request):
    ...


@require_POST
@csrf_exempt
def send_push(request):
    try:
        body = request.body
        data = json.loads(body)

        if 'head' not in data or 'body' not in data or 'id' not in data:
            return JsonResponse(status=400, data={"message": "Invalid data format"})

        user_id = data['id']
        user = get_object_or_404(User, pk=user_id)
        payload = {'head': data['head'], 'body': data['body']}
        send_user_notification(user=user, payload=payload, ttl=1000)

        return JsonResponse(status=200, data={"message": "Web push successful"})
    except TypeError:
        return JsonResponse(status=500, data={"message": "An error occurred"})

ما از دو decorator برای send_push استفاده می‌کنیم: require_POST که view را تنها به درخواست‌های POST محدود می‌کند و csrf_exempt که view را از حفاظت CSRF معاف می‌کند.

این view داده‌های POST را می‌پذیرد و موارد زیر را به اجرا درمی‌آورد:

  • head: The title of the push notification.
  • body: The body of the notification.
  • id: The id of the request user.

Body درخواست را می‌پذیرد و با استفاده از بسته json، سند JSON را با استفاده از json.loads به‌صورت یک آبجکت پایتون سریال زدایی می‌کند (تبدیل رشته بایتی به رشته‌های بیتی). Json.loads یک سند ساختاریافته JSON را دریافت کرده و آن را به آبجکت پایتون تبدیل می‌کند.

این view از متن درخواست سه انتظار عمده زیر را دارد:

head: عنوان پوش نوتیفیکیشن

body: بدنه یا متن نوتیفیکیشن

id: id یا نشانه کاربر درخواست دهنده

اگر هرکدام از این مشخصه‌های موردتقاضا موجود نباشند، view یک JSONResponse را با وضعیت ۴۰۴ یعنی «یافت نشد» بازمی‌گرداند. اگر کاربر با کلید اصلی وجود داشته باشد، view یک user به کمک کتابخانه Django.shortcuts بازمی‌گرداند. اگر کاربر وجود نداشته باشد، خطای ۴۰۴ بازگردانده می‌شود.

این view همچنین از تابع send_user_notification درون کتابخانه webpush استفاده می‌کند. این تابع شامل مقادیر زیر می‌شود:

User: دریافت‌کننده پوش نوتیفیکیشن

Payload: اطلاعات نوتیفیکیشن که شامل head و body می‌شود.

Ttl: بیشینه مدت‌زمانی که نوتیفیکیشن باید در صورت آفلاین بودن کاربر ذخیره شود (برحسب ثانیه)

اگر هیچ خطایی رخ ندهد، view یک JSONResponse را با وضعیت ۲۰۰ «موفقیت‌آمیز» و به آبجکت داده بازمی‌گرداند. اگر خطای KeyError رخ دهد، view وضعیت ۵۰۰ «خطای داخلی سرور» را بازمی‌گرداند. خطای KeyError زمانی رخ می‌دهد که کلید درخواست شده از آبجکت به خصوصی اصلاً وجود نداشته باشد.

در مرحله بعدی مسیرهای متناظر URL برای مطابقت با view ایجاد می‌کنیم.

مرحله سوم – نگاشت آدرس‌های URL به View ها

Django امکان ایجاد آدرس‌های URL را که با استفاده از یک ماژول پایتون به نام URLconf به view ها متصل می‌شوند، فراهم می‌آورد. این ماژول عبارت‌های مسیر URL را به تابع‌های پایتون که همان view های شما هستند، نگاشت می‌کند. فایل پیکربندی URL معمولاً در هنگام ایجاد پروژه به‌صورت خودکار به وجود می‌آید. شما در این مرحله باید این فایل را به‌روزرسانی کنید تا شامل مسیرهای تازه‌ای برای view هایی که در مرحله قبلی ایجاد کرده بودید و همچنین اپلیکیشن Django-webpush شود. اپلیکیشن Django-webpush نقاط مقصد را برای ثبت‌نام و عضویت کاربران در جهت دریافت پوش نوتیفیکیشن فراهم می‌آورد.

فایل urls.py را بازکنید:

nano ~/djangopush/djangopush/urls.py

این فایل باید این‌گونه به نظر برسد:

~/djangopush/djangopush/urls.py
"""untitled URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
    ۱. Add an import:  from my_app import views
    ۲. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    ۱. Add an import:  from other_app.views import Home
    ۲. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    ۱. Import the include() function: from django.urls import include, path
    ۲. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]

قدم بعدی نگاشت آدرس‌های URL به view هایی است که قبلاً ایجاد کرده‌اید. ابتدا ورودی include را اضافه کنید تا مطمئن شوید که همه مسیرها برای کتابخانه Django-webpush به پروژه‌تان اضافه خواهند شد:

~/djangopush/djangopush/urls.py
"""webpushdjango URL Configuration
...
"""
from django.contrib import admin
from django.urls import path, include

سپس، view هایی را که ایجاد کرده‌اید در مرحله آخر اضافه کرده و فهرست urlpatterns را برای نگاشت view ها به‌روزرسانی کنید:

~/djangopush/djangopush/urls.py
"""webpushdjango URL Configuration
...
"""
from django.contrib import admin
from django.urls import path, include

from .views import home, send_push

urlpatterns = [
                  path('admin/', admin.site.urls),
                  path('', home),
                  path('send_push', send_push),
                  path('webpush/', include('webpush.urls')),
              ]

در اینجا فهرست urlpatterns آدرس‌های URL را برای بسته Django-webpush به ثبت می‌رساند و view های شمارا به URL های /send_push و /home نگاشت می‌کند.

حالا بهتر است view /home را مورد آزمون قرار دهیم تا مطمئن شویم که به‌درستی عمل می‌کند. ابتدا باید بررسی کنید که حتماً در دایرکتوری روت پروژه باشید:

cd ~/djangopush

با اجرای دستور زیر سرور را راه‌اندازی کنید:

python manage.py runserver your_server_ip:8000

به مسیر http://your_server_ip:8000 بروید تا صفحه خانه به نمایش درآید.

django_push_home

در این مرحله می‌توانید با اجرای دستور CTRL+C سرور را متوقف کنید. در ادامه به توضیح ایجاد قالب‌های نمونه و رندر کردن آن‌ها در view به کمک تابع render می‌پردازیم.

مرحله چهارم – ایجاد قالب‌های نمونه

موتور قالب Django به شما اجازه می‌دهد تا لایه‌های اپلیکیشن را که در معرض دید کاربر و مشابه فایل‌های HTML هستند، ایجاد کنید. در این مرحله باید یک قالب نمونه را برای home view ایجاد و رندر کنید.

یک پوشه به نام templates در دایرکتوری روت پروژه خود ایجاد کنید:

mkdir ~/djangopush/templates

اگر در این مرحله دستور ۱s را در پوشه روت پروژه خود به اجرا درآورید، خروجی آن به این صورت به نمایش درمی‌آید:

Output
/djangopush
/templates
db.sqlite3
manage.py
/my_env

حالا یک فایل به نام home.html در پوشه templates ایجاد کنید:

nano ~/djangopush/templates/home.html

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

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <meta name="vapid-key" content="{{ vapid_key }}">
    {% if user.id %}
        <meta name="user_id" content="{{ user.id }}">
    {% endif %}
    <title>Web Push</title>
    <link href="https://fonts.googleapis.com/css?family=PT+Sans:400,700" rel="stylesheet">
</head>

<body>
<div>
    <form id="send-push__form">
        <h3 class="header">Send a push notification</h3>
        <p class="error"></p>
        <input type="text" name="head" placeholder="Header: Your favorite airline 😍">
        <textarea name="body" id="" cols="30" rows="10" placeholder="Body: Your flight has been cancelled 😱😱😱"></textarea>
        <button>Send Me</button>
    </form>
</div>
</body>
</html>

بخش body در این فایل شامل فرمی با دو فیلد متفاوت می‌شود. این فیلدها عناصر input و textarea هستند. input مسئول نگهداری بخش‌ head/title نوتیفیکیشن است و عنصر textarea هم body نوتیفیکیشن را در برمی‌گیرد.

در بخش head این فایل دو تگ meta وجود دارد که کلید عمومی VAPID و id کاربر را شامل می‌شود. این دو متغیر برای ثبت‌نام کاربر در جهت دریافت پوش نوتیفیکیشن الزامی هستند. بدین منظور به id کاربر نیاز دارید که می‌خواهید درخواست آژاکس را به سرور ارسال کنید و از این id برای شناسایی کاربر درروند استفاده می‌شود. اگر کاربر فعالی قبلاً ثبت‌نام کرده باشد، قالب پیش‌فرض تگ meta با id او را به‌عنوان محتوا ایجاد می‌کند.

در مرحله بعدی این روند به Django اطلاع می‌دهیم قالب‌هایمان کجا هستند. برای کار باید settings.py را ویرایش کرده و فهرست TEMPLATES را به‌روزرسانی کنید.

فایل settings.py را بازکنید:

nano ~/djangopush/djangopush/settings.py

کدهای زیر را به فهرست DIRS اضافه کنید تا مسیر دسترسی به دایرکتوری قالب‌هایتان مشخص شود:

~/djangopush/djangopush/settings.py
...
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                ...
            ],
        },
    },
]
...

سپس در فایل view.py خود home را به‌روزرسانی کنید تا قالب home.html را رندر کند. این فایل را بازکنید:

nano ~/djangpush/djangopush/views.py

ابتدا برخی ورودی‌های دیگر را شامل پیکربندی settings (شامل همه تنظیمات پروژه از فایل settings.py می‌شود) و تابع render از Django.shortcuts را اضافه کنید:

~/djangopush/djangopush/views.py
...
from django.shortcuts import render, get_object_or_404
...
import json
from django.conf import settings

...

حالا کدهایی را که به ابتدا به home اضافه کرده بودید حذف کرده و کدهای زیر را به آن اضافه کنید. این کدها مشخص می‌کنند قالبی که ایجاد کرده‌اید چگونه رندر می‌شوند:

~/djangopush/djangopush/views.py
...

@require_GET
def home(request):
   webpush_settings = getattr(settings, 'WEBPUSH_SETTINGS', {})
   vapid_key = webpush_settings.get('VAPID_PUBLIC_KEY')
   user = request.user
   return render(request, 'home.html', {user: user, 'vapid_key': vapid_key})

کد فوق متغیرهای زیر را اختصاص می‌دهد:

  • Webpush_settings: همان مقدار انتسابی WEBPUSH_SETTINGS از پیکربندی settings است.
  • Vapid_key: این متغیر مقدار VAPID_PUBLIC_KEY را از آبجکت webpush_settings برای ارسال به کلاینت دریافت می‌کند. کلید عمومی و کلید خصوصی باهم مقایسه می‌شوند تا مشخص شود که کلاینت دارای کلید عمومی مجاز است پیام‌های پوش را از سرور دریافت کند.
  • User: این متغیر از درخواست‌های ورودی ایجاد می‌شود. هرگاه یک کاربر یک درخواست را به سرور ارسال می‌کند، جزئیات درباره آن کاربر به‌خصوص در بخش user ذخیره می‌شود.

تابع render یک فایل HTML و یک آبجکت context بازمی‌گرداند که شامل کاربر فعلی و کلید عمومی VAPID سرور می‌شود. این تابع سه مقدار متفاوت دریافت می‌کند. این مقادیر request، template که باید رندر شود و آبجکتی که شامل متغیرهای مورداستفاده درون قالب هستند.

پس از ایجاد قالب نمونه و به‌روزرسانی home view می‌توانیم به مرحله بعدی برویم و Django را برای به کار گرفتن در فایل‌های استاتیک خود پیکربندی کنیم.

مرحله پنجم – عرضه فایل‌های استاتیک

اپلیکیشن‌های وب شامل جاوا اسکریپت، CSS و سایر فایل‌های تصویری می‌شوند که Django آن‌ها را فایل‌های استاتیک می‌نامد. Django شمارا قادر می‌سازد همه فایل‌های استاتیک درون هر اپلیکیشن پروژه خود را در یک لوکیشن واحد که ازآنجا عرضه می‌شوند، جمع‌آوری کنید. این راهکار Django.contrib.staticfiles نامیده شده است. در این مرحله تنظیمات را به‌روزرسانی کنید تا به Django بگویید که فایل‌های استاتیکتان کجا ذخیره می‌شوند.

Settings.py را بازکنید:

nano ~/djangopush/djangopush/settings.py

در settings.py ابتدا مطمئن شوید که STATIC_URL حتماً تعریف‌شده باشد:

~/djangopush/djangopush/settings.py
...
STATIC_URL = '/static/'

سپس فهرستی را از دایرکتوری‌های منتسب به STATICFILES_DIRS در موقعیتی که Django به دنبال فایل‌های استاتیک می‌گردد، اضافه کنید:

~/djangopush/djangopush/settings.py
...
STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]

حالا می‌توانید STATIC_URL را به فهرست مسیرهایی که در فایل urls.py شما مشخص‌شده‌اند اضافه کنید.

فایل را بازکنید:

nano ~/djangopush/djangopush/urls.py

کدهای زیر را اضافه کنید تا پیکربندی آدرس static وارد و فهرست urlpatterns هم به‌روزرسانی شود. تابع کمکی در اینجا از مشخصات STATIC_URL و STATIC_ROOT که به‌منظور عرضه در فایل‌های استاتیک پروژه در فایل settings.py فراهم کرده بودیم، استفاده می‌کند:

~/djangopush/djangopush/urls.py
...
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    ...
]  + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

حالا که تنظیمات فایل‌های استاتیک پیکربندی شدند، می‌توانیم به تنظیم سبک کلی صفحه خانه در اپلیکیشن بپردازیم.

مرحله ششم – سبک بندی صفحه خانه

پس از اعمال تنظیمات فایل‌های استاتیک، می‌توانید یک استایل شیت خارجی ایجاد کرده و آن را برای تنظیم سبک صفحه خانه به فایل home.html لینک کنید. همه فایل‌های استاتیک شما در یک دایرکتوری static درون فولدر روت (root folder) پروژه ذخیره می‌شوند.

یک پوشه static و همچنین یک پوشه css درون پوشه Static ایجاد کنید:

mkdir -p ~/djangopush/static/css

یک فایل css را به نام styles.css درون پوشه css بازکنید:

nano ~/djangopush/static/css/styles.css

سبک‌های زیر را برای صفحه خانه اضافه کنید:

~/djangopush/static/css/styles.css
body {
    height: 100%;
    background: rgba(0, 0, 0, 0.87);
    font-family: 'PT Sans', sans-serif;
}

div {
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}

form {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 35%;
    margin: 10% auto;
}

form > h3 {
    font-size: 17px;
    font-weight: bold;
    margin: 15px 0;
    color: orangered;
    text-transform: uppercase;
}

form > .error {
    margin: 0;
    font-size: 15px;
    font-weight: normal;
    color: orange;
    opacity: 0.7;
}

form > input, form > textarea {
    border: 3px solid orangered;
    box-shadow: unset;
    padding: 13px 12px;
    margin: 12px auto;
    width: 80%;
    font-size: 13px;
    font-weight: 500;
}

form > input:focus, form > textarea:focus {
    border: 3px solid orangered;
    box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.2);
    outline: unset;
}

form > button {
    justify-self: center;
    padding: 12px 25px;
    border-radius: 0;
    text-transform: uppercase;
    font-weight: 600;
    background: orangered;
    color: white;
    border: none;
    font-size: 14px;
    letter-spacing: -0.1px;
    cursor: pointer;
}

form > button:disabled {
    background: dimgrey;
    cursor: not-allowed;
}

پس‌ازآنکه استایل شیت ایجاد شد، می‌توانید آن را به کمک تگ‌های قالب static template به فایل home.html لینک کنید. فایل home.html را بازکنید:

nano ~/djangopush/templates/home.html

بخش head را به‌روزرسانی کنید تا شامل لینکی به استایل شیت خارجی شود:

~/djangopush/templates/home.html
{% load static %}
<!DOCTYPE html>
<html lang="en">

<head>
    ...
    <link href="{% static '/css/styles.css' %}" rel="stylesheet">
</head>
<body>
    ...
</body>
</html>

مطمئن شوید که در دایرکتوری اصلی پروژه خود باشید و سپس سرور را دوباره فعال کنید تا کارتان را بررسی کند:

cd ~/djangopush
python manage.py runserver your_server_ip:8000

وقتی صفحه http://your_server_ip:8000 را باز می‌کنید، باید به شکل زیر به نظر برسد:

همچنان می‌توانید با اجرای دستور CTTL+C سرور را غیرفعال کنید.

حالا که صفحه home.html را به‌طور موفقیت‌آمیز ایجاد کرده و سبکش را هم تعیین کردید، می‌توانید کاربران را برای دریافت پوش نوتیفیکیشن در هر زمان که وارد صفحه خانه می‌شوند، ثبت‌نام کنید.

مرحله هفتم – ثبت سرویس ورکر و ثبت‌نام کاربران برای دریافت پوش نوتیفیکیشن

پوش نوتیفیکیشن‌های وب می‌توانند به کاربران اطلاع دهند به‌روزرسانی‌هایی در اپلیکیشن‌هایی که در آن‌ها ثبت‌نام کرده‌اند، رخ‌داده است یا حتی از آن‌ها دعوت کنند تا از اپلیکیشنی که قبلاً استفاده کرده‌اند مجدداً بهره ببرند. این نوع نوتیفیکیشن‌ها عمدتاً بر دو تکنولوژی عمده تکیه‌دارند. این دو نوع push API و Notification API نامیده شده‌اند. هر دو تکنولوژی مذکور همچنین به حضور سرویس ورکرها وابسته هستند.

یک پوش (push) درواقع زمانی فراخوانده می‌شود که اطلاعات توسط سرور به سمت سرویس ورکر ارسال شوند و سرویس ورکر هم از API نوتیفیکیشن برای نمایش این اطلاعات استفاده می‌کند.

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

در دایرکتوری static یک پوشه به نام js ایجاد کنید:

mkdir ~/djangopush/static/js

یک فایل به نام registers.js ایجاد کنید:

nano ~/djangopush/static/js/registerSw.js

کدهای زیر را اضافه کنید تا پیش از اقدام برای ثبت، بررسی شود که آیا سرویس ورکرها توسط مرورگر کاربر پشتیبانی می‌شوند یا خیر:

~/djangopush/static/js/registerSw.js
const registerSw = async () => {
    if ('serviceWorker' in navigator) {
        const reg = await navigator.serviceWorker.register('sw.js');
        initialiseState(reg)

    } else {
        showNotAllowed("You can't send push notifications ☹️😢")
    }
};

در ابتدا تابع registers پیش از نصب بررسی می‌کند که آیا مرورگر کاربر از سرویس ورکرها پشتیبانی می‌کند یا خیر. این تابع پس از نصب سرویس ورکرها تابع initializeState را با داده‌های نصب فرامی‌خواند. اگر مرورگر از سرویس ورکرها پشتیبانی نکند، تابع showNotAllowed فراخوانده می‌شود.

سپس کد زیر را در تابع registers اضافه کنید تا پیش از ثبت‌نام کاربر بررسی کند که آیا او واجد شرایط دریافت پوش نوتیفیکیشن هست یا خیر:

~/djangopush/static/js/registerSw.js
...

const initialiseState = (reg) => {
    if (!reg.showNotification) {
        showNotAllowed('Showing notifications isn\'t supported ☹️😢');
        return
    }
    if (Notification.permission === 'denied') {
        showNotAllowed('You prevented us from showing notifications ☹️🤔');
        return
    }
    if (!'PushManager' in window) {
        showNotAllowed("Push isn't allowed in your browser 🤔");
        return
    }
    subscribe(reg);
}

const showNotAllowed = (message) => {
    const button = document.querySelector('form>button');
    button.innerHTML = `${message}`;
    button.setAttribute('disabled', 'true');
};

تابع initializeState موارد زیر را بررسی می‌کند:

  • به کمک مقدار showNotified مشخص می‌کند که کاربر پوش نوتیفیکیشن را فعال کرده است یا خیر.
  • آیا کاربر اجازه نمایش نوتیفیکیشن‌ها را برای اپلیکیشن صادر کرده است یا خیر.
  • آیا مرورگر وب از PushManager API پشتیبانی می‌کند یا خیر. اگر پاسخ به هرکدام از این موارد منفی باشد، تابع showNotAllowed فراخوانده شده و روند ثبت‌نام لغو می‌شود.

تابع showNotAllowed پیامی را در انتهای صفحه به نمایش درمی‌آورد و اگر کاربر واجد شرایط نباشد، آن را غیرفعال می‌کند. این تابع همچنین در شرایطی که کاربر اپلیکیشن را در مقابل نمایش نوتیفیکیشن‌ها محدود کرده باشد یا مرورگر از پوش نوتیفیکیشن پشتیبانی نکند، پیغام‌های مناسب به نمایش درمی‌آورد.

وقتی مطمئن شدیم که کاربری واجد شرایط دریافت پوش نوتیفیکیشن است، قدم بعدی ثبت‌نام آن کاربر یا کاربران به کمک pushManager خواهد بود. کد زیر را بعد از تابع showNotAllowed اضافه کنید:

~/djangopush/static/js/registerSw.js
...

function urlB64ToUint8Array(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);
    const outputData = outputArray.map((output, index) => rawData.charCodeAt(index));

    return outputData;
}

const subscribe = async (reg) => {
    const subscription = await reg.pushManager.getSubscription();
    if (subscription) {
        sendSubData(subscription);
        return;
    }

    const vapidMeta = document.querySelector('meta[name="vapid-key"]');
    const key = vapidMeta.content;
    const options = {
        userVisibleOnly: true,
        // if key exists, create applicationServerKey property
        ...(key && {applicationServerKey: urlB64ToUint8Array(key)})
    };

    const sub = await reg.pushManager.subscribe(options);
    sendSubData(sub)
};

فراخواندن تابع pushManager.getSubscription داده‌ها را برای ثبت‌نام و عضویت فعال بازمی‌گرداند. زمانی که ثبت‌نام فعال وجود داشته باشد، تابع sendSubData با اطلاعات ثبت‌نام فراخوانی می‌شود.

وقتی هیچ ثبت‌نام فعالی وجود ندارد، کلید عمومی VAPID که به‌صورت base64 URL و امن کدگذاری شده است با تابع urlB64ToUint8Array به‌نوعی آرایه Uin8Array تبدیل می‌شود. سپس pushManager.subscribe با کلید عمومی VAPID و مقدار userVisible به‌عنوان گزینه‌های اختیاری فراخوانی می‌شود.

پس از ثبت‌نام موفقیت‌آمیز کاربران، قدم بعدی ارسال داده‌های ثبت‌نام به سمت سرور خواهد بود. داده‌ها به نقطه پایانی webpush/save_information که توسط بسته Django-webpush فراهم‌شده است ارسال می‌شوند. کدهای زیر را پس از تابع subscribe وارد کنید:

~/djangopush/static/js/registerSw.js
...

const sendSubData = async (subscription) => {
    const browser = navigator.userAgent.match(/(firefox|msie|chrome|safari|trident)/ig)[0].toLowerCase();
    const data = {
        status_type: 'subscribe',
        subscription: subscription.toJSON(),
        browser: browser,
    };

    const res = await fetch('/webpush/save_information', {
        method: 'POST',
        body: JSON.stringify(data),
        headers: {
            'content-type': 'application/json'
        },
        credentials: "include"
    });

    handleResponse(res);
};

const handleResponse = (res) => {
    console.log(res.status);
};

registerSw();

نقطه پایانی save_information اطلاعاتی را درباره وضعیت ثبت‌نام، داده‌های ثبت‌نام و مرورگر درخواست می‌کند. در آخر تابع registers() را فراخوانی می‌کنیم تا روند ثبت‌نام کاربر را آغاز کند.

فایل کامل شده به این شکل به نظر می‌رسد:

~/djangopush/static/js/registerSw.js
const registerSw = async () => {
    if ('serviceWorker' in navigator) {
        const reg = await navigator.serviceWorker.register('sw.js');
        initialiseState(reg)

    } else {
        showNotAllowed("You can't send push notifications ☹️😢")
    }
};

const initialiseState = (reg) => {
    if (!reg.showNotification) {
        showNotAllowed('Showing notifications isn\'t supported ☹️😢');
        return
    }
    if (Notification.permission === 'denied') {
        showNotAllowed('You prevented us from showing notifications ☹️🤔');
        return
    }
    if (!'PushManager' in window) {
        showNotAllowed("Push isn't allowed in your browser 🤔");
        return
    }
    subscribe(reg);
}

const showNotAllowed = (message) => {
    const button = document.querySelector('form>button');
    button.innerHTML = `${message}`;
    button.setAttribute('disabled', 'true');
};

function urlB64ToUint8Array(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);
    const outputData = outputArray.map((output, index) => rawData.charCodeAt(index));

    return outputData;
}

const subscribe = async (reg) => {
    const subscription = await reg.pushManager.getSubscription();
    if (subscription) {
        sendSubData(subscription);
        return;
    }

    const vapidMeta = document.querySelector('meta[name="vapid-key"]');
    const key = vapidMeta.content;
    const options = {
        userVisibleOnly: true,
        // if key exists, create applicationServerKey property
        ...(key && {applicationServerKey: urlB64ToUint8Array(key)})
    };

    const sub = await reg.pushManager.subscribe(options);
    sendSubData(sub)
};

const sendSubData = async (subscription) => {
    const browser = navigator.userAgent.match(/(firefox|msie|chrome|safari|trident)/ig)[0].toLowerCase();
    const data = {
        status_type: 'subscribe',
        subscription: subscription.toJSON(),
        browser: browser,
    };

    const res = await fetch('/webpush/save_information', {
        method: 'POST',
        body: JSON.stringify(data),
        headers: {
            'content-type': 'application/json'
        },
        credentials: "include"
    });

    handleResponse(res);
};

const handleResponse = (res) => {
    console.log(res.status);
};

registerSw();

سپس یک تگ script برای فایل registers.js در home.html اضافه کنید. فایل زیر را بازکنید:

nano ~/djangopush/templates/home.html

تگ script را پیش از تگ پایانی عنصر body اضافه کنید:

~/djangopush/templates/home.html
{% load static %}
<!DOCTYPE html>
<html lang="en">

<head>
   ...
</head>
<body>
   ...
   <script src="{% static '/js/registerSw.js' %}"></script>
</body>
</html>

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

مرحله هشتم – ایجاد یک سرویس ورکر

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

ازآنجاکه می‌خواهیم حیطه سرویس ورکر همه وسعت دامنه باشد، باید آن را در روت یا ریشه اپلیکیشن نصب کنیم. رویکرد ما ایجاد فایلی به نام sw.js در پوشه templates است که بعداً آن را به‌عنوان یک view هم به ثبت می‌رسانیم.

فایل را ایجاد کنید:

nano ~/djangopush/templates/sw.js

کد زیر را که به سرویس ورکر دستور می‌دهد رویدادهای push را ردیابی کند، اضافه کنید:

~/djangopush/templates/sw.js
// Register event listener for the 'push' event.
self.addEventListener('push', function (event) {
    // Retrieve the textual payload from event.data (a PushMessageData object).
    // Other formats are supported (ArrayBuffer, Blob, JSON), check out the documentation
    // on https://developer.mozilla.org/en-US/docs/Web/API/PushMessageData.
    const eventInfo = event.data.text();
    const data = JSON.parse(eventInfo);
    const head = data.head || 'New Notification 🕺🕺';
    const body = data.body || 'This is default content. Your notification didn\'t have one 🙄🙄';

    // Keep the service worker alive until the notification is created.
    event.waitUntil(
        self.registration.showNotification(head, {
            body: body,
            icon: 'https://i.imgur.com/MZM3K5w.png'
        })
    );
});

بدین ترتیب سرویس ورکر برای بروز رویداد push منتظر می‌ماند. در تابع callback داده‌های event به متن تبدیل می‌شوند. ما از رشته‌های پیش‌فرض title و body در صورتی استفاده می‌کنیم که داده‌های رویداد فاقد آن‌ها باشد. تابع showNotification عنوان نوتیفیکیشن، header و همچنین آبجکت options را به‌عنوان مقادیر مختلف دریافت می‌کند. آبجکت options شامل چندین مشخصه برای پیکربندی گزینه‌های انتخابی بصری در نوتیفیکیشن‌ها می‌شود.

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

فایل urls.py را بازکنید:

nano ~/djangopush/djangopush/urls.py

یک گزاره ورودی و مسیر تازه در فهرست urlpatterns اضافه کنید تا view مبتنی بر کلاس ایجاد شود:

~/djangopush/djangopush/urls.py
...
from django.views.generic import TemplateView

urlpatterns = [
                  ...,
                  path('sw.js', TemplateView.as_view(template_name='sw.js', content_type='application/x-javascript'))
              ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

View های مبتنی بر کلاس مانند TemplateView شما را قادر می‌سازند view های منعطف باقابلیت استفاده مجدد ایجاد کنید. در این مورد خاص، روش TemplateView.as_view سرویس ورکر تازه ایجادشده را به‌عنوان یک قالب و Application/x-javascript را به‌عنوان content_type این قالب انتقال می‌دهد و مسیری را برای سرویس ورکرها ایجاد می‌کند.

شما در این مرحله یک سرویس ورکر ایجاد کردید و آن را به‌عنوان مسیر به ثبت رساندید. حالا باید فرم درون صفحه‌نمایش را راه‌اندازی کنید تا پوش نوتیفیکیشن را ارسال کند.

مرحله نهم – ارسال پوش نوتیفیکیشن

کاربران به‌واسطه فرم موجود د صفحه خانه می‌توانند درحالی‌که سرور شما فعال است، پوش نوتیفیکیشن ارسال کنند. شما هم می‌توانید با استفاده از هر نوع سرویس RESTful مانند Postman پوش نوتیفیکیشن ارسال کنید. وقتی‌که کاربر از فرم موجود در صفحه خانه پوش نوتیفیکیشن ارسال می‌کند، داده شامل head و body و همچنین id کاربر دریافت‌کننده خواهد بود. داده باید به شکل زیر ساختار پیدا کند:

{
    head: "Title of the notification",
    body: "Notification body",
    id: "User's id"
}

برای ردیابی رویداد submit در فرم و ارسال داده واردشده توسط کاربر به سمت سرور باید یک فایل به نام site.js در دایرکتوری ~/ djangopush/static/js ایجاد کنیم.

فایل را بازکنید:

nano ~/djangopush/static/js/site.js

ابتدا باید یک شنونده رویداد submit به فرم اضافه کنید تا امکان دریافت مقادیر ورودی فرم را فراهم آورد و همچنین id کاربر را هم در تگ meta قالب ذخیره کند:

~/djangopush/static/js/site.js
const pushForm = document.getElementById('send-push__form');
const errorMsg = document.querySelector('.error');

pushForm.addEventListener('submit', async function (e) {
    e.preventDefault();
    const input = this[0];
    const textarea = this[1];
    const button = this[2];
    errorMsg.innerText = '';

    const head = input.value;
    const body = textarea.value;
    const meta = document.querySelector('meta[name="user_id"]');
    const id = meta ? meta.content : null;
    ...
    // TODO: make an AJAX request to send notification
});

تابع pushForm در فرم مقادیر input، textarea و button را دریافت می‌کند. این تابع همچنین اطلاعات را شامل نام جایگزین user_id و id کاربر که درون content ذخیره‌شده است از تگ meta می‌گیرد. تابع مذکور قادر است با این اطلاعات یک درخواست POST به نقطه پایانی /send_push روی سرور ارسال کند.

برای ارسال درخواست روی سرور از Fetch API استفاده می‌کنیم. از Fetch کمک می‌گیریم؛ زیرا اغلب مرورگرها از آن پشتیبانی می‌کنند و برای اجرا به کتابخانه‌های خارجی نیاز ندارد. زیر کدهایی که اضافه کرده‌اید باید تابع pushForm را به‌روزرسانی کنید تا شامل کدی لازم برای درخواست AJAX شود:

~/djangopush/static/js/site.js
const pushForm = document.getElementById('send-push__form');
const errorMsg = document.querySelector('.error');

pushForm.addEventListener('submit', async function (e) {
     ...
    const id = meta ? meta.content : null;

     if (head && body && id) {
        button.innerText = 'Sending...';
        button.disabled = true;

        const res = await fetch('/send_push', {
            method: 'POST',
            body: JSON.stringify({head, body, id}),
            headers: {
                'content-type': 'application/json'
            }
        });
        if (res.status === 200) {
            button.innerText = 'Send another 😃!';
            button.disabled = false;
            input.value = '';
            textarea.value = '';
        } else {
            errorMsg.innerText = res.message;
            button.innerText = 'Something broke 😢..  Try again?';
            button.disabled = false;
        }
    }
    else {
        let error;
        if (!head || !body){
            error = 'Please ensure you complete the form 🙏🏾'
        }
        else if (!id){
            error = "Are you sure you're logged in? 🤔. Make sure! 👍🏼"
        }
        errorMsg.innerText = error;
    }
});

اگر مقادیر موردنیاز head، body و id موجود باشند، درخواست را ارسال کرده و دکمه ثبت را به‌صورت موقت غیرفعال می‌کنیم.

فایل کامل شده باید به این شکل ظاهر شود:

~/djangopush/static/js/site.js
const pushForm = document.getElementById('send-push__form');
const errorMsg = document.querySelector('.error');

pushForm.addEventListener('submit', async function (e) {
    e.preventDefault();
    const input = this[0];
    const textarea = this[1];
    const button = this[2];
    errorMsg.innerText = '';

    const head = input.value;
    const body = textarea.value;
    const meta = document.querySelector('meta[name="user_id"]');
    const id = meta ? meta.content : null;

    if (head && body && id) {
        button.innerText = 'Sending...';
        button.disabled = true;

        const res = await fetch('/send_push', {
            method: 'POST',
            body: JSON.stringify({head, body, id}),
            headers: {
                'content-type': 'application/json'
            }
        });
        if (res.status === 200) {
            button.innerText = 'Send another 😃!';
            button.disabled = false;
            input.value = '';
            textarea.value = '';
        } else {
            errorMsg.innerText = res.message;
            button.innerText = 'Something broke 😢..  Try again?';
            button.disabled = false;
        }
    }
    else {
        let error;
        if (!head || !body){
            error = 'Please ensure you complete the form 🙏🏾'
        }
        else if (!id){
            error = "Are you sure you're logged in? 🤔. Make sure! 👍🏼"
        }
        errorMsg.innerText = error;
    }
});

در آخر فایل site.js را به home.html اضافه کنید:

~/djangopush/templates/home.html

تگ script را اضافه کنید:

~/djangopush/templates/home.html
{% load static %}
<!DOCTYPE html>
<html lang="en">

<head>
   ...
</head>
<body>
   ...
   <script src="{% static '/js/site.js' %}"></script>
</body>
</html>

در این مرحله اگر اپلیکیشن را در حالت فعال ترک کنید یا بخواهید آن را دوباره باز و فعال کنید، احتمالاً با پیغام خطا مواجه خواهید شد؛ زیرا سرویس ورکرها تنها روی دامنه‌های امن یا localhost فعالیت می‌کنند. در مرحله بعدی از ngrok برای ایجاد تونلی امن به سرور وب استفاده می‌کنیم.

مرحله دهم – ایجاد تونل امن برای آزمودن اپلیکیشن

سرویس ورکرها برای فعال شدن عملکرد روی همه سایت‌ها به‌جز localhost به ارتباط امن نیاز دارند؛ زیرا در غیر این صورت ممکن است ارتباط کاربران به سرقت برود یا جعل شود. به همین خاطر، ما به کمک ngrok یک تونل امن ارتباطی ایجاد می‌کنیم.

پنجره ترمینال دوم را ایجاد کنید و بررسی کنید که حتماً در دایرکتوری home باشید:

cd ~

اگر کار خود را روی نسخه به‌روزرسانی شده و تازه ۱۸.۰۴ سرور آغاز کرده‌اید، باید unzip را نصب کنید:

sudo apt update && sudo apt install unzip

Ngrok را دانلود کنید:

wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip
unzip ngrok-stable-linux-amd64.zip

Ngrok را به /user/local/bin انتقال دهید تا به دستور ngrok از ترمینال زیر دسترسی پیدا کنید:

sudo mv ngrok /usr/local/bin

در اولین پنجره ترمینال بررسی کنید که حتماً در دایرکتوری پروژه‌تان باشید و سپس سرور را فعال کنید.

cd ~/djangopush
python manage.py runserver your_server_ip:8000

شما باید این مرحله را پیش از ایجاد تونل امن برای اپلیکیشن خود اجرا کنید.

در پنجره دوم ترمینال خود به پوشه پروژه‌تان بروید و محیط مجازی‌تان را فعال کنید:

cd ~/djangopush
source my_env/bin/activate

تونل امن به سمت اپلیکیشن خود را ایجاد کنید:

ngrok http your_server_ip:8000

حالا می‌توانید خروجی زیر را که شامل اطلاعاتی درباره آدرس URL امن ngrok می‌شود، مشاهده کنید:

Output
ngrok by @inconshreveable                                                                                                                       (Ctrl+C to quit)

Session Status                online
Session Expires               7 hours, 59 minutes
Version                       2.2.8
Region                        United States (us)
Web Interface                 http://127.0.0.1:4040
Forwarding                    http://ngrok_secure_url -> 203.0.113.0:8000
Forwarding                    https://ngrok_secure_url -> 203.0.113.0:8000

Connections                   ttl     opn     rt1     rt5     p50     p90
                              ۰       ۰       ۰.۰۰    ۰.۰۰    ۰.۰۰    ۰.۰۰

Ngrok_secure_url را از کنسول خروجی کپی کنید. حالا باید آن را به فهرست ALLOWED_HOSTS در فایل settings.py خود اضافه کنید.

یک پنجره ترمینال دیگر بازکرده و به پوشه پروژه خود بروید. حالا محیط مجازی را فعال کنید:

cd ~/djangopush
source my_env/bin/activate

فایل settings.py را بازکنید:

nano ~/djangopush/djangopush/settings.py

فهرست ALLOWED_Hosts را با تونل امن ngrok به‌روزرسانی کنید:

~/djangopush/djangopush/settings.py

...

ALLOWED_HOSTS = ['your_server_ip', 'ngrok_secure_url']
...

به صفحه امن ادمین بروید. باید وارد صفحه با آدرس https://ngrok_secure_url/admin/. این صفحه به شکل زیر خواهد بود:

ngrok_login

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

در مرورگر خود به آدرس https://ngrok_secure_url بروید. حالا اعلانی ظاهر می‌شود که از شما برای به نمایش درآوردن نوتیفیکیشن‌ها کسب اجازه می‌کند. allow را انتخاب کنید تا به مرورگرتان اجازه دهید که پوش نوتیفیکیشن‌ها را دریافت کند و به اجرا درآورد:

allow_push_two

ثبت یک فرم کامل شده باید نوتیفیکیشنی شبیه به تصویر زیر را به نمایش درآورد:

Django-Webposh-final

نکته مهم: بررسی کنید که سرور شما پیش از ارسال نوتیفیکیشن‌ها حتماً فعال باشد.

اگر در پایان این مرحله نوتیفیکیشن دریافت کردید، یعنی اپلیکیشن شما همان‌طور که انتظار می‌رفته است عمل می‌کند.

شما اپلیکیشن وبی ایجاد کرده‌اید که پوش نوتیفیکیشن را روی سرور تحریک می‌کند و به کمک سرویس ورکرها نوتیفیکیشن‌ها را دریافت می‌کند و به نمایش درمی‌آورد. شما همچنین مراحل را برای کسب کلیدهای VAPID گذرانده‌اید که برای ارسال پوش نوتیفیکیشن از سرور اپلیکیشن کاملاً ضروری هستند.

کلام آخر

در این راهنمای مفصل آموختید که چگونه می‌توان کاربران را برای دریافت پوش نوتیفیکیشن ثبت‌نام کرد؛ سرویس ورکرها را نصب کرد و همچنین پوش نوتیفیکیشن‌ها را به کمک API نوتیفیکیشن به نمایش درآورد.

شما همچنین می‌توانید فراتر عمل کرده و نوتیفیکیشن‌ها را به‌گونه‌ای پیکربندی کنید که کاربر را هنگام کلیک کردن به بخش‌های مختلف اپلیکیشن هدایت کنند.

راهنمای ارسال پوش نوتیفیکیشن از اپلیکیشن‌های Django

علی رجبی

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

  +  47  =  56