195 lines
5.4 KiB
Go
195 lines
5.4 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,
|
|
SpecificType: "linstrom",
|
|
Version: shared.Version,
|
|
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(shared.ServerActorName)
|
|
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: shared.ServerActorName,
|
|
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 {
|
|
roles := []models.Role{models.DefaultUserRole, models.ServerActorRole, models.FullAdminRole}
|
|
for _, role := range roles {
|
|
_, err := dbgen.UserToRole.Where(dbgen.UserToRole.UserId.Eq(user.ID)).
|
|
Where(dbgen.UserToRole.RoleId.Eq(role.ID)).
|
|
First()
|
|
switch err {
|
|
case nil:
|
|
continue
|
|
case gorm.ErrRecordNotFound:
|
|
u2r := models.UserToRole{
|
|
User: *user,
|
|
UserId: user.ID,
|
|
Role: role,
|
|
RoleId: role.ID,
|
|
}
|
|
if err = dbgen.UserToRole.Save(&u2r); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|