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 }