W tym wpisie pokażę jak dodałem, już jakiś czas temu, fajną funkcje do prostej aplikacji czatu. Funkcją tą są powiadomienia, gdy ktoś coś napisze na czacie.
Jeśli jesteś zainteresowany jak napisać taki czat, polecam najpierw przeczytać artykuł „Prosty Czat w JavaScript, PHP i SQLite”. Rozwiązanie to używa technologii Server-sent events (SSE), której można używać, gdy z jakiegoś powodu nie możemy użyć gniazd (ang. Web Sockets).
W celu dodania funkcji powiadomień użyłem powiadomień typu Push (ang. Push Notifications), service workera oraz Firebase, aby uprościć sobie życie.
Wprowadzenie
Powiadomienia typu push, jest to nowe API dostępne w przeglądarkach, dzięki któremu można, dzięki service workerowi (wątku działającym w tle, także po zamknięciu strony) wysyłać wiadomość z serwera do przeglądarki i dzięki powiadomieniom w przeglądarce wyświetlić wiadomość użytkownikowi (każdemu kto wyraził na to zgodę).
Osobiście nie lubie tego typu powiadomień, szczególnie że dużo stron nadużywa tej funkcji i od razy przy wejściu, pyta o pozwolenie na powiadomienia. Nigdy się nie zgadzam. Tak też myślą programiści przeglądarek i mają zamiar wyłączyć tą opcje (ukryć), aby nie można było pytać o powiadomienia w ten sposób.
Powiadomienia mają jednak sens, tak jak w naszej aplikacji czatu, gdy damy możliwość włączenia powiadomień, dzięki czemu użytkownicy będą widzieć gdy ktoś dołącza i coś pisze oraz mogą zacząć rozmawiać miedzy sobą, a nie tylko z innym otwartym oknem, aby sprawdzić jak działa czat. Przydatne jest także w prawdziwej aplikacji gdy ktoś zadaje pytanie i zamyka zakładkę, wtedy gdy nie ma duży wiadomości dostanie powiadomienie, gdy pojawi się nowa wiadomość. Ta następna wiadomość najczęściej będzie to odpowiedź na jego pytanie.
Firebase
Firebase to usługa, której aktualnie właścicielem jest Google, a która udostępnia ciekawe funkcje m.in. bazę danych czasu rzeczywistego, można ją wykożystać także do tworzenia czatu, zamiast Server Sent Event (SSE), gdy nie możemy użyć własnych gniazd, np. na współdzielonym hostingu. Ale dla nas najważniejsze są push notifications, czyli powiadomienia typu push.
Aby skorzystać z usługi wystarczy zalogować/zarejestrować się używając konta Google. Następnie należy utworzyć projekt na stronie konsoli firebase, pod adresem console.firebase.google.com. Gdy mamy już projekt, musimy dodać aplikacje i w ustawieniach przejść do zakładki Komunikacja w chmurze (ang. Cloud Messaging).
Jeśli masz problem z utworzeniem aplikacji pozostaje Google, tego typu usługi to podstawa i każdy programista powinien umieć z nich korzystać. A jeśli ma problemy powinien sam umieć znaleźć odpowiedź w internecie.
Tam możesz pobrać token, który musisz użyć na serwerze, najlepiej zapisać do pliku,
ważne, aby zablokować dostęp z internetu np. za pomocą pliku .htaccess
.
Można też dodać go do pliku .gitignore
, aby przez przypadek nie dodać do
repozytorium, jeśli się takiego używa. Polecam do każdej aplikacji.
GitHub udostępnia także darmowe prywatne repozytoria, więc nie ma powodu,
aby nie korzystać, chociaż ja zawsze tworzę publiczne i Open Source.
Aby zainicjować bibliotekę Firebase w JavaScript musimy wkleić kod, który jest dostępny w zakładce ogólne na stronie ustawień aplikacji.
W moim przypadku to:
Użyj swojego kodu, mimo że są to w pewnym sensie klucze, to jednak są one publiczne bo inaczej nie dałoby się napisać kodu JavaScript.
Service Worker i Powiadomienia
Aby dodać powiadomienia za pomocą Firebase wystarczy taki kod na głównej stronie:
Skrypt register.php
, będzie służył do zapisania tokenu przeglądarki
w bazie danych, abyśmy mogli wysłać powiadomienie po dodaniu nowej wiadomości.
Główny kod znajduje się w pliku Notifications.php który wygląda tak:
To jest główna część logiki powiadomień po stronie serwera.
Plik Database.php
zawiera abstrakcje nad PDO do obsługi bazy danych.
Natomiast plik http.php
zawiera funkcji pomocnicze get
oraz post
wykonujące zapytania HTTP, jak nazwa wskazuje GET oraz POST za pomocą biblioteki
CURL.
zawartość pliku register.php wygląda tak:
Mając rejestracje oraz klasę Notifications
trzeba ją jeszcze wykorzystać do wysyłania
powiadomień. Do tego celu w klasie Messages wystarczy utworzyć instancje klasy oraz
wywołać jej metodę send gdy wysyłamy wiadomość z czatu.
Z klasy Messages
usunięte zostały także funkcje bazy danych i przeniesione do klasy Database
.
Jednak dzięki magicznej metodzie __call
kod działa tak samo. Jeśli czytałeś poprzedni artykuł,
polecam sprawdzenie różnicy (ang. diff) między dwoma gałęziami repozytorium (ang. branch). Link poniżej.
Co dalej
Powyższa implementacja powiadomień jest moim zdaniem wystarczająca do prawdziwej aplikacji, brakuje tylko jednej rzeczy, a mianowicie istnieje tylko jeden token per user, to znaczy że jeśli drugi użytkownik wpisze taką samą nazwę użytkownika, to skasuje token poprzedniej osobie o takim samym imieniu. Aby się zabezpieczyć można odróżnić od siebie dwóch użytkowników o tej samej nazwie (np. generując losową wartość i zapisując w przeglądarce), ale lepiej po prostu nie pozwalać na taką samą nazwę (zazwyczaj aplikacje nie pozwalają na dwóch użytkowników o takim samym loginie), można także mając system rejestracji użytkowników użyć emaila.
W naszej aplikacji, pytanie o powiadomienia, pojawia się od razu po wejściu na stronę, nie polecam takiego rozwiązanie. Zastosowane tutaj zostały tylko dla uproszczenia.
Polecam dodanie tego typu ikonki za pomocą, której można włączać i wyłączać powiadomienia.
Aby wyłączyć powiadomienia, najlepiej użyć aktualnego tokenu użytkownika, jest do niego
dostęp w przeglądarce i wykonać funkcje messaging.deleteToken(token)
(szczegóły w
dokumentacji Firebase).
Należy także usunąć token dla użytkownika z bazy danych SQLite.
Ważne jest także, aby zabezpieczyć plik bazy danych przed odczytem z internetu, ponieważ zawiera tokeny użytkowników. Nie jestem pewien czy atakujący może takie tokeny wykorzystać, ale nie warto ryzykować.
Ponieżej plik .htaccess
, który zablokuje newralgiczne pliki:
RewriteEngine on
RewriteRule firebase_token - [F]
RewriteRule messages.sqlite - [F]
RewriteRule firebase.log - [F]
firebase.log
to dodatkowy plik, w którym zapisywane są odpowiedzi Firebase po stronie serwera.
Logi są tworzone, tylko gdy wartość stałej __DEBUG__
jest równa true.
Podsumowanie
Powiadomienia mimo złej sławy, są użyteczne. Warto jednak się zastanowić kiedy ich użyć. Tak zresztą jest z każdym API, które powstało w jakimś celu.
Kod aplikacji znajduje się w tym samy repo co poprzedni czat, tylko na branchu notifications: github.com/jcubic/chat.
Aby nie zmieniać zachowania poprzedniego czatu, powiadomienia są ukryte i włączają
się, gdy dodamy zmienną Query String notification
z dowolną wartością.
Czyli wystarczy otworzyć czat poprzez adres:
jcubic.pl/chat/?notification=x.
Reszta aplikacji działa tak samo.
Komentarze
Hasło, które podasz umożliwi ponowne zalogowanie się i np. usunięcie komentarza, jest dobrowolne. Email jest szyfrowany i używany do wysyłania powiadomień o odpowiedziach do komentarzy oraz do pobierania awatara dzięki usłudze Gravatar.com.