linstrom/storage-new/self.go
2025-04-10 16:40:06 +02:00

187 lines
5.1 KiB
Go

package storage
import (
"crypto/rand"
"database/sql"
"git.mstar.dev/mstar/goutils/other"
"github.com/google/uuid"
"gorm.io/gorm"
"git.mstar.dev/mstar/linstrom/config"
"git.mstar.dev/mstar/linstrom/shared"
"git.mstar.dev/mstar/linstrom/storage-new/dbgen"
"git.mstar.dev/mstar/linstrom/storage-new/models"
)
func InsertSelf() error {
if err := insertRoles(); err != nil {
return other.Error("storage", "failed to save/update default roles", err)
}
duck, err := insertDefaultDuck()
if err != nil {
return other.Error("storage", "failed to save/update default duck", err)
}
server, err := insertServer(duck)
if err != nil {
return other.Error("storage", "failed to save/update self server", err)
}
user, err := insertUser(server, duck)
if err != nil {
return other.Error("storage", "failed to save/update self user", err)
}
if err = insertUserPronoun(user); err != nil {
return other.Error("storage", "failed to save/update self user pronoun", err)
}
if err = attachUserToRole(user); err != nil {
return other.Error("storage", "failed to save/update self user to full admin role", err)
}
return nil
}
func insertRoles() error {
return dbgen.Role.Save(models.AllDefaultRoles...)
}
func insertDefaultDuck() (*models.MediaMetadata, error) {
dbDuck, err := dbgen.MediaMetadata.Where(dbgen.MediaMetadata.Location.Eq(models.DefaultDuckLocationName)).
First()
if err == nil {
return dbDuck, nil
} else if err != gorm.ErrRecordNotFound {
return nil, err
}
duck := models.MediaMetadata{
ID: uuid.NewString(),
// NOTE: Default duck technically belongs to the server
// but to avoid cyclic dependency, declare it as unowned
OwnedById: sql.NullString{Valid: false},
Remote: false,
Location: models.DefaultDuckLocationName,
Type: "image/webp",
Name: "default-duck.webp",
AltText: "a greyscale image of a pidgeon on some smooth surface, likely a liquid. It is captioned with the text \"Duck\"",
Blurred: false,
}
if err := dbgen.MediaMetadata.Create(&duck); err != nil {
return nil, err
}
return &duck, nil
}
func insertServer(duck *models.MediaMetadata) (*models.RemoteServer, error) {
dbServer, err := dbgen.RemoteServer.Where(dbgen.RemoteServer.ID.Eq(1)).First()
if err == nil {
return dbServer, nil
} else if err != gorm.ErrRecordNotFound {
return nil, err
}
server := models.RemoteServer{
Model: gorm.Model{
ID: 1,
},
ServerType: models.ServerSoftwareLinstrom,
Domain: config.GlobalConfig.General.GetFullDomain(),
Name: config.GlobalConfig.Self.ServerDisplayName,
Icon: duck,
IconId: sql.NullString{String: duck.ID, Valid: true},
IsSelf: true,
}
err = dbgen.RemoteServer.Save(&server)
if err != nil {
return nil, err
}
return &server, nil
}
func insertUser(
server *models.RemoteServer,
duckMedia *models.MediaMetadata,
) (*models.User, error) {
dbUser, err := dbgen.User.GetByUsername("linstrom")
if err == nil {
return dbUser, nil
}
if err != gorm.ErrRecordNotFound {
return nil, err
}
publicEdKeyBytes, privateEdKeyBytes, err := shared.GenerateKeypair(true)
if err != nil {
return nil, err
}
publicRsaKeyBytes, privateRsaKeyBytes, err := shared.GenerateKeypair(false)
if err != nil {
return nil, err
}
pkeyId := make([]byte, 64)
_, err = rand.Read(pkeyId)
if err != nil {
return nil, other.Error(
"storage",
"failed to generate passkey ID for linstrom account",
err,
)
}
user := models.User{
Username: "linstrom",
Server: *server,
ServerId: server.ID,
DisplayName: config.GlobalConfig.Self.ServerActorDisplayName,
Description: "The default linstrom server user",
IsBot: true,
Icon: duckMedia,
IconId: sql.NullString{Valid: true, String: duckMedia.ID},
Background: nil,
BackgroundId: sql.NullString{Valid: false},
Banner: nil,
BannerId: sql.NullString{Valid: false},
Indexable: false,
PublicKeyEd: publicEdKeyBytes,
PrivateKeyEd: privateEdKeyBytes,
PublicKeyRsa: publicRsaKeyBytes,
PrivateKeyRsa: privateRsaKeyBytes,
Verified: true,
FinishedRegistration: true,
PasskeyId: pkeyId,
}
err = dbgen.User.Save(&user)
if err != nil {
return nil, err
}
return &user, err
}
func insertUserPronoun(user *models.User) error {
_, err := dbgen.UserToPronoun.Where(dbgen.UserToPronoun.UserId.Eq(user.ID)).
Where(dbgen.UserToPronoun.Pronoun.Eq("Yes")).First()
switch err {
case nil:
return nil
case gorm.ErrRecordNotFound:
return dbgen.UserToPronoun.Create(
&models.UserToPronoun{UserId: user.ID, User: *user, Pronoun: "Yes"},
)
default:
return err
}
}
func attachUserToRole(user *models.User) error {
_, 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
}
}