@@ -79,6 +79,27 @@ CREATE TABLE ReadReceipt (
7979 FOREIGN KEY(network) REFERENCES Network(id),
8080 UNIQUE(network, target)
8181);
82+
83+ CREATE TABLE WebPushConfig (
84+ id INTEGER PRIMARY KEY,
85+ created_at TEXT NOT NULL,
86+ vapid_key_public TEXT NOT NULL,
87+ vapid_key_private TEXT NOT NULL,
88+ UNIQUE(vapid_key_public)
89+ );
90+
91+ CREATE TABLE WebPushSubscription (
92+ id INTEGER PRIMARY KEY,
93+ created_at TEXT NOT NULL,
94+ updated_at TEXT NOT NULL,
95+ network INTEGER NOT NULL,
96+ endpoint TEXT NOT NULL,
97+ key_vapid TEXT,
98+ key_auth TEXT,
99+ key_p256dh TEXT,
100+ FOREIGN KEY(network) REFERENCES Network(id),
101+ UNIQUE(network, endpoint)
102+ );
82103`
83104
84105var sqliteMigrations = []string {
@@ -189,6 +210,28 @@ var sqliteMigrations = []string{
189210 UNIQUE(network, target)
190211 );
191212 ` ,
213+ `
214+ CREATE TABLE WebPushConfig (
215+ id INTEGER PRIMARY KEY,
216+ created_at TEXT NOT NULL,
217+ vapid_key_public TEXT NOT NULL,
218+ vapid_key_private TEXT NOT NULL,
219+ UNIQUE(vapid_key_public)
220+ );
221+
222+ CREATE TABLE WebPushSubscription (
223+ id INTEGER PRIMARY KEY,
224+ created_at TEXT NOT NULL,
225+ updated_at TEXT NOT NULL,
226+ network INTEGER NOT NULL,
227+ endpoint TEXT NOT NULL,
228+ key_vapid TEXT,
229+ key_auth TEXT,
230+ key_p256dh TEXT,
231+ FOREIGN KEY(network) REFERENCES Network(id),
232+ UNIQUE(network, endpoint)
233+ );
234+ ` ,
192235}
193236
194237type SqliteDB struct {
@@ -570,6 +613,11 @@ func (db *SqliteDB) DeleteNetwork(ctx context.Context, id int64) error {
570613 }
571614 defer tx .Rollback ()
572615
616+ _ , err = tx .ExecContext (ctx , "DELETE FROM WebPushSubscription WHERE network = ?" , id )
617+ if err != nil {
618+ return err
619+ }
620+
573621 _ , err = tx .ExecContext (ctx , "DELETE FROM DeliveryReceipt WHERE network = ?" , id )
574622 if err != nil {
575623 return err
@@ -820,3 +868,147 @@ func (db *SqliteDB) StoreReadReceipt(ctx context.Context, networkID int64, recei
820868
821869 return err
822870}
871+
872+ func (db * SqliteDB ) ListWebPushConfigs (ctx context.Context ) ([]WebPushConfig , error ) {
873+ db .lock .RLock ()
874+ defer db .lock .RUnlock ()
875+
876+ ctx , cancel := context .WithTimeout (ctx , sqliteQueryTimeout )
877+ defer cancel ()
878+
879+ rows , err := db .db .QueryContext (ctx , `
880+ SELECT id, vapid_key_public, vapid_key_private
881+ FROM WebPushConfig` )
882+ if err != nil {
883+ return nil , err
884+ }
885+ defer rows .Close ()
886+
887+ var configs []WebPushConfig
888+ for rows .Next () {
889+ var config WebPushConfig
890+ if err := rows .Scan (& config .ID , & config .VAPIDKeys .Public , & config .VAPIDKeys .Private ); err != nil {
891+ return nil , err
892+ }
893+ configs = append (configs , config )
894+ }
895+ if err := rows .Err (); err != nil {
896+ return nil , err
897+ }
898+
899+ return configs , nil
900+ }
901+
902+ func (db * SqliteDB ) StoreWebPushConfig (ctx context.Context , config * WebPushConfig ) error {
903+ db .lock .Lock ()
904+ defer db .lock .Unlock ()
905+
906+ ctx , cancel := context .WithTimeout (ctx , sqliteQueryTimeout )
907+ defer cancel ()
908+
909+ if config .ID != 0 {
910+ return fmt .Errorf ("cannot update a WebPushConfig" )
911+ }
912+
913+ args := []interface {}{
914+ sql .Named ("vapid_key_public" , config .VAPIDKeys .Public ),
915+ sql .Named ("vapid_key_private" , config .VAPIDKeys .Private ),
916+ sql .Named ("now" , formatServerTime (time .Now ())),
917+ }
918+
919+ res , err := db .db .ExecContext (ctx , `
920+ INSERT INTO
921+ WebPushConfig(created_at, vapid_key_public, vapid_key_private)
922+ VALUES (:now, :vapid_key_public, :vapid_key_private)` ,
923+ args ... )
924+ if err != nil {
925+ return err
926+ }
927+ config .ID , err = res .LastInsertId ()
928+ return err
929+ }
930+
931+ func (db * SqliteDB ) ListWebPushSubscriptions (ctx context.Context , networkID int64 ) ([]WebPushSubscription , error ) {
932+ db .lock .RLock ()
933+ defer db .lock .RUnlock ()
934+
935+ ctx , cancel := context .WithTimeout (ctx , sqliteQueryTimeout )
936+ defer cancel ()
937+
938+ rows , err := db .db .QueryContext (ctx , `
939+ SELECT id, endpoint, key_auth, key_p256dh, key_vapid
940+ FROM WebPushSubscription
941+ WHERE network = ?` , networkID )
942+ if err != nil {
943+ return nil , err
944+ }
945+ defer rows .Close ()
946+
947+ var subs []WebPushSubscription
948+ for rows .Next () {
949+ var sub WebPushSubscription
950+ if err := rows .Scan (& sub .ID , & sub .Endpoint , & sub .Keys .Auth , & sub .Keys .P256DH ); err != nil {
951+ return nil , err
952+ }
953+ subs = append (subs , sub )
954+ }
955+ if err := rows .Err (); err != nil {
956+ return nil , err
957+ }
958+
959+ return subs , nil
960+ }
961+
962+ func (db * SqliteDB ) StoreWebPushSubscription (ctx context.Context , networkID int64 , sub * WebPushSubscription ) error {
963+ db .lock .Lock ()
964+ defer db .lock .Unlock ()
965+
966+ ctx , cancel := context .WithTimeout (ctx , sqliteQueryTimeout )
967+ defer cancel ()
968+
969+ args := []interface {}{
970+ sql .Named ("id" , sub .ID ),
971+ sql .Named ("network" , networkID ),
972+ sql .Named ("now" , formatServerTime (time .Now ())),
973+ sql .Named ("endpoint" , sub .Endpoint ),
974+ sql .Named ("key_auth" , sub .Keys .Auth ),
975+ sql .Named ("key_p256dh" , sub .Keys .P256DH ),
976+ sql .Named ("key_vapid" , sub .Keys .VAPID ),
977+ }
978+
979+ var err error
980+ if sub .ID != 0 {
981+ _ , err = db .db .ExecContext (ctx , `
982+ UPDATE WebPushSubscription
983+ SET updated_at = :now, key_auth = :key_auth, key_p256dh = :key_p256dh,
984+ key_vapid = :key_vapid
985+ WHERE id = :id` ,
986+ args ... )
987+ } else {
988+ var res sql.Result
989+ res , err = db .db .ExecContext (ctx , `
990+ INSERT INTO
991+ WebPushSubscription(created_at, updated_at, network, endpoint,
992+ key_auth, key_p256dh, key_vapid)
993+ VALUES (:now, :now, :network, :endpoint, :key_auth, :key_p256dh,
994+ :key_vapid)` ,
995+ args ... )
996+ if err != nil {
997+ return err
998+ }
999+ sub .ID , err = res .LastInsertId ()
1000+ }
1001+
1002+ return err
1003+ }
1004+
1005+ func (db * SqliteDB ) DeleteWebPushSubscription (ctx context.Context , id int64 ) error {
1006+ db .lock .Lock ()
1007+ defer db .lock .Unlock ()
1008+
1009+ ctx , cancel := context .WithTimeout (ctx , sqliteQueryTimeout )
1010+ defer cancel ()
1011+
1012+ _ , err := db .db .ExecContext (ctx , "DELETE FROM WebPushSubscription WHERE id = ?" , id )
1013+ return err
1014+ }
0 commit comments