feat: realtime status, mentions, push notifications
This commit is contained in:
@@ -183,6 +183,77 @@ func (h *Handler) SendPush(ctx context.Context, userID string, payload map[strin
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// SendChannelNotification sends a push notification to all server members (except the author)
|
||||
// when a new message is posted in a channel.
|
||||
func (h *Handler) SendChannelNotification(ctx context.Context, channelID, authorID, channelName, content string) {
|
||||
if h.vapidPub == "" || h.vapidPriv == "" {
|
||||
return
|
||||
}
|
||||
|
||||
var serverID string
|
||||
err := h.db.QueryRowContext(ctx, `SELECT server_id FROM channels WHERE id = $1`, channelID).Scan(&serverID)
|
||||
if err != nil {
|
||||
h.logger.Error("failed to find channel server", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
body := content
|
||||
if len(body) > 200 {
|
||||
body = body[:200] + "..."
|
||||
}
|
||||
|
||||
payload, _ := json.Marshal(map[string]interface{}{
|
||||
"title": "New message in #" + channelName,
|
||||
"body": body,
|
||||
"url": "/channels/" + channelID,
|
||||
})
|
||||
|
||||
rows, err := h.db.QueryContext(ctx, `
|
||||
SELECT DISTINCT ps.user_id, ps.endpoint, ps.p256dh, ps.auth
|
||||
FROM push_subscriptions ps
|
||||
JOIN members m ON m.user_id = ps.user_id
|
||||
WHERE m.server_id = $1 AND ps.user_id != $2
|
||||
`, serverID, authorID)
|
||||
if err != nil {
|
||||
h.logger.Error("failed to query push subscriptions", "error", err)
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var userID, endpoint, p256dh, auth string
|
||||
if err := rows.Scan(&userID, &endpoint, &p256dh, &auth); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
sub := &webpush.Subscription{
|
||||
Endpoint: endpoint,
|
||||
Keys: webpush.Keys{
|
||||
P256dh: p256dh,
|
||||
Auth: auth,
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := webpush.SendNotification(payload, sub, &webpush.Options{
|
||||
Subscriber: h.vapidSubj,
|
||||
VAPIDPublicKey: h.vapidPub,
|
||||
VAPIDPrivateKey: h.vapidPriv,
|
||||
TTL: 30,
|
||||
})
|
||||
if err != nil {
|
||||
h.logger.Error("failed to send push", "error", err, "user_id", userID)
|
||||
if resp != nil && resp.StatusCode == 410 {
|
||||
h.db.ExecContext(ctx, `DELETE FROM push_subscriptions WHERE endpoint = $1`, endpoint)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
// GenerateVAPIDKeys generates a new VAPID key pair.
|
||||
func GenerateVAPIDKeys() (publicKey, privateKey string, err error) {
|
||||
privateKeyBytes := make([]byte, 32)
|
||||
|
||||
Reference in New Issue
Block a user