125 lines
3.6 KiB
Go
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()
|
|
}
|