package storage import ( "crypto/rand" "crypto/rsa" "crypto/x509" "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/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) 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) (*models.User, error) { dbUser, err := dbgen.User.GetByUsername("linstrom") if err == nil { return dbUser, nil } if err != gorm.ErrRecordNotFound { return nil, err } // 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 { 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: nil, IconId: sql.NullString{Valid: false}, Background: nil, BackgroundId: sql.NullString{Valid: false}, Banner: nil, BannerId: sql.NullString{Valid: false}, Indexable: false, PublicKey: publicKeyBytes, PrivateKey: privateKeyBytes, 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 } }