AP stuff almost works
Some checks are pending
/ test (push) Waiting to run

This commit is contained in:
Melody Becker 2025-04-09 17:35:31 +02:00
parent 98191fd098
commit d272fa90b4
Signed by: mstar
SSH key fingerprint: SHA256:9VAo09aaVNTWKzPW7Hq2LW+ox9OdwmTSHRoD4mlz1yI
20 changed files with 574 additions and 27 deletions

View file

@ -1456,6 +1456,7 @@ type IUserDo interface {
UnderlyingDB() *gorm.DB
schema.Tabler
GetByUsernameUnrestricted(username string) (result *models.User, err error)
GetByUsername(username string) (result *models.User, err error)
GetPagedTruePublic(pageNr uint) (result []models.User, err error)
GetPagedAllDeleted(pageNr uint) (result []models.User, err error)
@ -1463,10 +1464,10 @@ type IUserDo interface {
GdprUsers() (err error)
}
// Get a user by a username
// Get a user by a username, ignoring all restrictions on that user
//
// SELECT * FROM @@table WHERE username = @username AND deleted_at IS NULL LIMIT 1
func (u userDo) GetByUsername(username string) (result *models.User, err error) {
func (u userDo) GetByUsernameUnrestricted(username string) (result *models.User, err error) {
var params []interface{}
var generateSQL strings.Builder
@ -1480,6 +1481,31 @@ func (u userDo) GetByUsername(username string) (result *models.User, err error)
return
}
// Get a user by the username.
// Restricted to users visible to ActivityPub
//
// SELECT * FROM @@table WHERE
//
// username = @username AND
// deleted_at IS NULL AND
// finished_registration = true AND
// verified = true
//
// LIMIT 1
func (u userDo) GetByUsername(username string) (result *models.User, err error) {
var params []interface{}
var generateSQL strings.Builder
params = append(params, username)
generateSQL.WriteString("SELECT * FROM users WHERE username = ? AND deleted_at IS NULL AND finished_registration = true AND verified = true LIMIT 1 ")
var executeSQL *gorm.DB
executeSQL = u.UnderlyingDB().Raw(generateSQL.String(), params...).Take(&result) // ignore_security_alert
err = executeSQL.Error
return
}
// Get all true public accounts (verified & no restricted follow & indexable)
// in a paged manner, sorted by date saved
//

View file

@ -80,9 +80,20 @@ type User struct {
}
type IUser interface {
// Get a user by a username
// Get a user by a username, ignoring all restrictions on that user
//
// SELECT * FROM @@table WHERE username = @username AND deleted_at IS NULL LIMIT 1
GetByUsernameUnrestricted(username string) (*gen.T, error)
// Get a user by the username.
// Restricted to users visible to ActivityPub
//
// SELECT * FROM @@table WHERE
// username = @username AND
// deleted_at IS NULL AND
// finished_registration = true AND
// verified = true
// LIMIT 1
GetByUsername(username string) (*gen.T, error)
// Get all true public accounts (verified & no restricted follow & indexable)

View file

@ -1,8 +1,9 @@
package storage
import (
"crypto/ed25519"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"database/sql"
"git.mstar.dev/mstar/goutils/other"
@ -102,7 +103,16 @@ func insertUser(server *models.RemoteServer) (*models.User, error) {
if err != gorm.ErrRecordNotFound {
return nil, err
}
publicKey, privateKey, err := ed25519.GenerateKey(nil)
// publicKey, privateKey, err := ed25519.GenerateKey(nil)
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
if err = privateKey.Validate(); err != nil {
return nil, err
}
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
publicKeyBytes := x509.MarshalPKCS1PublicKey(&privateKey.PublicKey)
pkeyId := make([]byte, 64)
_, err = rand.Read(pkeyId)
if err != nil {
@ -126,8 +136,8 @@ func insertUser(server *models.RemoteServer) (*models.User, error) {
Banner: nil,
BannerId: sql.NullString{Valid: false},
Indexable: false,
PublicKey: publicKey,
PrivateKey: privateKey,
PublicKey: publicKeyBytes,
PrivateKey: privateKeyBytes,
Verified: true,
FinishedRegistration: true,
PasskeyId: pkeyId,
@ -155,11 +165,21 @@ func insertUserPronoun(user *models.User) error {
}
func attachUserToRole(user *models.User) error {
u2r := models.UserToRole{
User: *user,
UserId: user.ID,
Role: models.FullAdminRole,
RoleId: models.FullAdminRole.ID,
_, err := dbgen.UserToRole.Where(dbgen.UserToRole.UserId.Eq(user.ID)).
Where(dbgen.UserToRole.RoleId.Eq(models.FullAdminRole.ID)).
First()
switch err {
case nil:
return nil
case gorm.ErrRecordNotFound:
u2r := models.UserToRole{
User: *user,
UserId: user.ID,
Role: models.FullAdminRole,
RoleId: models.FullAdminRole.ID,
}
return dbgen.UserToRole.Save(&u2r)
default:
return err
}
return dbgen.UserToRole.Save(&u2r)
}

View file

@ -1,3 +1,78 @@
package storage
import (
"time"
"github.com/jackc/pgx/v5/pgconn"
"github.com/rs/zerolog/log"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"git.mstar.dev/mstar/linstrom/config"
"git.mstar.dev/mstar/linstrom/shared"
"git.mstar.dev/mstar/linstrom/storage-new/dbgen"
)
//go:generate go run ../cmd/NewRoleHelperGenerator/main.go -input ./models/Role.go -output role_generated.go -mod storage
// "Lock" to only have one active reconnection attempt going at any time
var activeReconnectionAttempt = false
// Attempt to connect to the configured DB.
// If successful, also configures dbgen to use that connection
func AttemptReconnect() {
// Ensure that only one attempt is going at the same time
// since a connection failure could easily cause multiple calls
if activeReconnectionAttempt {
log.Info().Msg("Already attempting to reconnect")
return
}
activeReconnectionAttempt = true
defer func() {
activeReconnectionAttempt = false
}()
log.Warn().Msg("DB connection failure! Attempting to reconnect")
maxAttempts := config.GlobalConfig.Storage.MaxReconnectAttempts
for i := range maxAttempts {
// If not the first attempt, sleep for 5 seconds and hope that this helps
if i > 0 {
time.Sleep(time.Second * 5)
}
log.Warn().Msg("Attempting to reconnect to db")
db, err := gorm.Open(
postgres.Open(config.GlobalConfig.Storage.BuildPostgresDSN()),
&gorm.Config{
Logger: shared.NewGormLogger(log.Logger),
},
)
// If reconnect failed, log, then enter next loop iteration
if err != nil {
log.Error().
Err(err).
Int("remaining-attempts", maxAttempts-(i+1)).
Msg("Reconnect attempt failed")
continue
}
// No errors, reconnect successful. Give dbgen the new connection and return
dbgen.SetDefault(db)
return
}
// All attempts to reconnect have failed.
// The situation is not recoverable.
// Log it and exit
// This is not a panic reason as it is expected that a db connection
// could always drop due to outside influence
log.Fatal().Msg("Failed to reconnect to the database! Exiting")
}
// HandleReconnectError checks if the given error requires
// a reconnect attempt. If it does, it also starts
// a reconnection attempt.
func HandleReconnectError(errToCheck error) bool {
if _, ok := errToCheck.(*pgconn.ConnectError); ok {
go AttemptReconnect()
return true
} else {
return false
}
}