Files
byre/internal/store/push.go
T

125 lines
3.6 KiB
Go

package store
import (
"context"
"database/sql"
"encoding/json"
"errors"
"g.fc.run/misc/byre/internal/api"
)
func (s *Store) UpdateNotificationPrefs(ctx context.Context, login string, prefs api.NotificationPrefs) (api.User, error) {
b, err := json.Marshal(prefs)
if err != nil {
return api.User{}, err
}
err = s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
_, err := tx.ExecContext(ctx, `UPDATE users SET notification_prefs = ? WHERE login = ?`, string(b), login)
if err != nil {
return err
}
return emit(api.Event{Kind: "user.updated", Actor: login, Payload: map[string]any{"login": login}})
})
if err != nil {
return api.User{}, err
}
return s.GetUser(ctx, login)
}
type VAPIDKeys struct {
Public string
Private string
}
func (s *Store) GetVAPIDKeys(ctx context.Context) (VAPIDKeys, error) {
k := VAPIDKeys{}
err := s.db.QueryRowContext(ctx, `SELECT public_key, private_key FROM vapid_keys WHERE id = 1`).Scan(&k.Public, &k.Private)
if errors.Is(err, sql.ErrNoRows) {
return k, ErrNotFound
}
return k, err
}
func (s *Store) InitVAPIDKeys(ctx context.Context, k VAPIDKeys) (VAPIDKeys, error) {
err := s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
_, err := tx.ExecContext(ctx, `INSERT INTO vapid_keys (id, public_key, private_key) VALUES (1, ?, ?) ON CONFLICT (id) DO NOTHING`, k.Public, k.Private)
return err
})
if err != nil {
return VAPIDKeys{}, err
}
return s.GetVAPIDKeys(ctx)
}
type PushSubscription struct {
Endpoint string
UserLogin string
P256dh string
Auth string
Subscriber string
}
func (s *Store) AddPushSubscription(ctx context.Context, sub PushSubscription) error {
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
_, err := tx.ExecContext(ctx, `
INSERT INTO push_subscriptions (endpoint, user_login, p256dh, auth, subscriber, created_at) VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT (endpoint) DO UPDATE SET user_login = excluded.user_login, p256dh = excluded.p256dh, auth = excluded.auth,
subscriber = excluded.subscriber
`, sub.Endpoint, sub.UserLogin, sub.P256dh, sub.Auth, sub.Subscriber, now())
if err != nil {
return err
}
return emit(api.Event{Kind: "push.subscribed", Actor: sub.UserLogin, Payload: map[string]any{"login": sub.UserLogin}})
})
}
func (s *Store) RemovePushSubscription(ctx context.Context, endpoint, login, actor string) error {
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
res, err := tx.ExecContext(ctx, `DELETE FROM push_subscriptions WHERE endpoint = ? AND user_login = ?`, endpoint, login)
if err != nil {
return err
}
n, err := res.RowsAffected()
if err != nil {
return err
}
if n == 0 {
return nil
}
return emit(api.Event{Kind: "push.unsubscribed", Actor: actor, Payload: map[string]any{"login": login}})
})
}
type PushTarget struct {
Subscription PushSubscription
Prefs api.NotificationPrefs
}
func (s *Store) PushTargets(ctx context.Context) ([]PushTarget, error) {
rows, err := s.db.QueryContext(ctx, `
SELECT p.endpoint, p.user_login, p.p256dh, p.auth, p.subscriber, u.notification_prefs
FROM push_subscriptions p JOIN users u ON u.login = p.user_login ORDER BY p.created_at
`)
if err != nil {
return nil, err
}
defer rows.Close()
targets := []PushTarget{}
for rows.Next() {
t := PushTarget{}
prefs := ""
err = rows.Scan(&t.Subscription.Endpoint, &t.Subscription.UserLogin, &t.Subscription.P256dh, &t.Subscription.Auth, &t.Subscription.Subscriber, &prefs)
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(prefs), &t.Prefs)
if err != nil {
return nil, err
}
targets = append(targets, t)
}
return targets, rows.Err()
}