Tmavý režim
Web Push notifikace
Atrea User API podporuje Web Push notifikace pomocí VAPID (Voluntary Application Server Identification) standardu.
Přehled
Web Push umožňuje odesílat notifikace přímo do prohlížeče uživatele, i když aplikace není otevřena. Funguje na všech moderních prohlížečích (Chrome, Firefox, Edge, Safari 16.4+).
Architektura
Backend aplikace
│
└── POST /api/internal/notifications (type s pushEnabled=true)
│
▼
PushService
│
┌───────────┴──────────────┐
│ Načte subscripce │
│ uživatele z DB │
└───────────┬──────────────┘
│
┌───────────▼──────────────┐
│ web-push library │
│ (VAPID podpis) │
└───────────┬──────────────┘
│
┌───────────▼──────────────┐
│ Push server prohlížeče │
│ (FCM, Mozilla, Apple) │
└───────────┬──────────────┘
│
┌───────────▼──────────────┐
│ Prohlížeč uživatele │
│ (Service Worker) │
└──────────────────────────┘Konfigurace VAPID klíčů
Generování klíčů
bash
npx web-push generate-vapid-keysVýstup:
Public Key:
BM8U3B-cXKbcl...
Private Key:
x7Vc3A2...Konfigurace v config/production.yaml
yaml
push:
vapidPublicKey: "BM8U3B-cXKbcl..."
vapidPrivateKey: "x7Vc3A2..."
vapidSubject: "mailto:admin@atrea.eu"VAPID klíče
Generujte VAPID klíče jednou a uložte je bezpečně. Pokud klíče změníte, všechny existující subscripce přestanou fungovat a uživatelé se musí znovu přihlásit k odběru.
Frontend integrace
1. Získání VAPID public key
javascript
const response = await fetch('/api/profile/me/push/vapid-key', {
headers: { 'Authorization': `Bearer ${token}` }
});
const { data: { vapidPublicKey } } = await response.json();2. Registrace Service Workeru
javascript
// public/sw.js
self.addEventListener('push', (event) => {
const data = event.data?.json() ?? {};
event.waitUntil(
self.registration.showNotification(data.title ?? 'Notifikace', {
body: data.body,
icon: data.icon ?? '/logo.svg',
badge: '/badge.svg',
data: data.url ? { url: data.url } : undefined,
})
);
});
self.addEventListener('notificationclick', (event) => {
event.notification.close();
if (event.notification.data?.url) {
event.waitUntil(clients.openWindow(event.notification.data.url));
}
});javascript
// app.js
const registration = await navigator.serviceWorker.register('/sw.js');3. Žádost o povolení a subscripce
javascript
async function subscribeToPush() {
// Žádost o povolení notifikací
const permission = await Notification.requestPermission();
if (permission !== 'granted') return;
// Konverze VAPID public key
const vapidKey = urlBase64ToUint8Array(vapidPublicKey);
// Vytvoření subscripce
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: vapidKey,
});
// Odeslání subscripce na server
await fetch('/api/profile/me/push/subscribe', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify({
subscription: subscription.toJSON(),
deviceLabel: navigator.userAgent,
}),
});
}
// Pomocná funkce pro konverzi VAPID key
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
const rawData = window.atob(base64);
return Uint8Array.from([...rawData].map(char => char.charCodeAt(0)));
}4. Odhlášení z odběru
javascript
async function unsubscribeFromPush() {
const subscription = await registration.pushManager.getSubscription();
if (!subscription) return;
await fetch('/api/profile/me/push/unsubscribe', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify({ endpoint: subscription.endpoint }),
});
await subscription.unsubscribe();
}API endpointy
GET /profile/me/push/vapid-key
Vrátí VAPID public key pro inicializaci.
bash
GET /api/profile/me/push/vapid-key
Authorization: Bearer <token>json
{ "data": { "vapidPublicKey": "BM8U3B..." } }POST /profile/me/push/subscribe
Registruje push subscripci pro přihlášeného uživatele.
bash
POST /api/profile/me/push/subscribe
Authorization: Bearer <token>json
{
"subscription": {
"endpoint": "https://fcm.googleapis.com/fcm/send/...",
"keys": {
"p256dh": "BMutZ...",
"auth": "Kd9..."
}
},
"deviceLabel": "Chrome na macOS"
}POST /profile/me/push/unsubscribe
Odregistruje subscripci.
bash
POST /api/profile/me/push/unsubscribe
Authorization: Bearer <token>
{ "endpoint": "https://fcm.googleapis.com/fcm/send/..." }GET /profile/me/push/subscriptions
Vrátí seznam všech subscripcí uživatele.
bash
GET /api/profile/me/push/subscriptions
Authorization: Bearer <token>json
{
"data": [
{
"id": 12,
"endpoint": "https://fcm.googleapis.com/fcm/send/...",
"deviceLabel": "Chrome na macOS",
"createdAt": "2024-01-20T10:00:00.000Z"
}
]
}Odesílání push ze serveru
Push notifikace se odesílají automaticky jako součást POST /internal/notifications, pokud:
- Typ má
pushEnabled: true - Uživatel má povolen push pro daný typ
- Uživatel má registrované push subscripce
Payload odeslaný do prohlížeče:
json
{
"title": "Renderovaný předmět šablony",
"body": "Krátký text notifikace",
"icon": "/assets/img/atrea/logo.svg",
"url": "https://app.atrea.eu/notifications"
}Zpracování expirovaných subscripcí
Pokud push endpoint vrátí 410 Gone nebo 404, subscripce se automaticky smaže z databáze (uživatel odregistroval notifikace v prohlížeči).
Podpora prohlížečů
| Prohlížeč | Podpora |
|---|---|
| Chrome (desktop + Android) | ✅ |
| Firefox | ✅ |
| Edge | ✅ |
| Safari 16.4+ (macOS + iOS) | ✅ |
| Opera | ✅ |
| Samsung Internet | ✅ |
iOS Safari
Safari na iOS podporuje Web Push od verze 16.4 (iOS 16.4+). Web app musí být přidána na plochu (PWA) pro zobrazení notifikací na iOS.