Replace Web Push and its service worker with page notifications over SSE
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
|
||||
"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)
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
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()
|
||||
}
|
||||
@@ -315,6 +315,9 @@ CREATE TABLE task_inputs (
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX task_inputs_task ON task_inputs(task_id, id);
|
||||
`}, {"drop-web-push", `
|
||||
DROP TABLE push_subscriptions;
|
||||
DROP TABLE vapid_keys;
|
||||
`}}
|
||||
|
||||
const (
|
||||
|
||||
Reference in New Issue
Block a user