diff --git a/internal/db/admin.go b/internal/db/admin.go index 1f24c7932..d0da54e31 100644 --- a/internal/db/admin.go +++ b/internal/db/admin.go @@ -68,6 +68,10 @@ type Admin interface { // the number of pending sign-ups sitting in the backlog. CountUnhandledSignups(ctx context.Context) (int, error) + // GetOrCreateVAPIDKeyPair creates and stores a VAPID key pair, + // or retrieves the existing VAPID key pair. + GetOrCreateVAPIDKeyPair(ctx context.Context) (*gtsmodel.VAPIDKeyPair, error) + /* ACTION FUNCS */ diff --git a/internal/db/bundb/admin.go b/internal/db/bundb/admin.go index ff398fca5..dacb2cb1f 100644 --- a/internal/db/bundb/admin.go +++ b/internal/db/bundb/admin.go @@ -27,6 +27,7 @@ "strings" "time" + webpushgo "github.com/SherClockHolmes/webpush-go" "github.com/google/uuid" "github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/config" @@ -442,6 +443,38 @@ func (a *adminDB) CountUnhandledSignups(ctx context.Context) (int, error) { Count(ctx) } +func (a *adminDB) GetOrCreateVAPIDKeyPair(ctx context.Context) (*gtsmodel.VAPIDKeyPair, error) { + var err error + var vapidKeyPair *gtsmodel.VAPIDKeyPair + + // Look for previously generated keys. + if err = a.db.NewSelect(). + Model(vapidKeyPair). + Limit(1). + Scan(ctx); // nocollapse + err != nil && !errors.Is(err, db.ErrNoEntries) { + return nil, gtserror.Newf("DB error getting VAPID key pair: %w", err) + } + + if vapidKeyPair == nil { + // Generate new keys. + vapidKeyPair = >smodel.VAPIDKeyPair{} + if vapidKeyPair.Private, vapidKeyPair.Public, err = webpushgo.GenerateVAPIDKeys(); err != nil { + return nil, gtserror.Newf("error generating VAPID key pair: %w", err) + } + + // Save them to the database. + if _, err = a.db.NewInsert(). + Model(vapidKeyPair). + Exec(ctx); // nocollapse + err != nil { + return nil, gtserror.Newf("DB error saving VAPID key pair: %w", err) + } + } + + return vapidKeyPair, err +} + /* ACTION FUNCS */ diff --git a/internal/db/bundb/migrations/20241124012635_add_vapid_key_pairs.go b/internal/db/bundb/migrations/20241124012635_add_vapid_key_pairs.go new file mode 100644 index 000000000..c1a32f6be --- /dev/null +++ b/internal/db/bundb/migrations/20241124012635_add_vapid_key_pairs.go @@ -0,0 +1,51 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package migrations + +import ( + "context" + + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/uptrace/bun" +) + +func init() { + up := func(ctx context.Context, db *bun.DB) error { + return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { + if _, err := tx. + NewCreateTable(). + Model(>smodel.VAPIDKeyPair{}). + IfNotExists(). + Exec(ctx); err != nil { + return err + } + + return nil + }) + } + + down := func(ctx context.Context, db *bun.DB) error { + return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { + return nil + }) + } + + if err := Migrations.Register(up, down); err != nil { + panic(err) + } +} diff --git a/internal/gtsmodel/vapidkeypair.go b/internal/gtsmodel/vapidkeypair.go new file mode 100644 index 000000000..85883df45 --- /dev/null +++ b/internal/gtsmodel/vapidkeypair.go @@ -0,0 +1,28 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package gtsmodel + +// VAPIDKeyPair represents the instance's VAPID keys (stored as Base64 strings). +// This table should only ever have one entry, with a known ID of 0. +// +// See: https://datatracker.ietf.org/doc/html/rfc8292 +type VAPIDKeyPair struct { + ID int `bun:"pk,notnull"` + Public string `bun:"notnull,nullzero"` + Private string `bun:"notnull,nullzero"` +}