diff --git a/internal/database/org.go b/internal/database/org.go index fcbff087b..0c2f4ee91 100644 --- a/internal/database/org.go +++ b/internal/database/org.go @@ -6,8 +6,7 @@ import ( "strings" "github.com/cockroachdb/errors" - "xorm.io/builder" - "xorm.io/xorm" + "gorm.io/gorm" "gogs.io/gogs/internal/errutil" "gogs.io/gogs/internal/repoutil" @@ -26,32 +25,32 @@ func (org *User) IsOrgMember(uid int64) bool { return org.IsOrganization() && IsOrganizationMember(org.ID, uid) } -func (org *User) getTeam(e Engine, name string) (*Team, error) { - return getTeamOfOrgByName(e, org.ID, name) +func (org *User) getTeam(tx *gorm.DB, name string) (*Team, error) { + return getTeamOfOrgByName(tx, org.ID, name) } // GetTeamOfOrgByName returns named team of organization. func (org *User) GetTeam(name string) (*Team, error) { - return org.getTeam(x, name) + return org.getTeam(db, name) } -func (org *User) getOwnerTeam(e Engine) (*Team, error) { - return org.getTeam(e, ownerTeamName) +func (org *User) getOwnerTeam(tx *gorm.DB) (*Team, error) { + return org.getTeam(tx, ownerTeamName) } // GetOwnerTeam returns owner team of organization. func (org *User) GetOwnerTeam() (*Team, error) { - return org.getOwnerTeam(x) + return org.getOwnerTeam(db) } -func (org *User) getTeams(e Engine) (err error) { - org.Teams, err = getTeamsByOrgID(e, org.ID) +func (org *User) getTeams(tx *gorm.DB) (err error) { + org.Teams, err = getTeamsByOrgID(tx, org.ID) return err } // GetTeams returns all teams that belong to organization. func (org *User) GetTeams() error { - return org.getTeams(x) + return org.getTeams(db) } // TeamsHaveAccessToRepo returns all teams that have given access level to the repository. @@ -86,13 +85,13 @@ func (org *User) RemoveMember(uid int64) error { return RemoveOrgUser(org.ID, uid) } -func (org *User) removeOrgRepo(e Engine, repoID int64) error { - return removeOrgRepo(e, org.ID, repoID) +func (org *User) removeOrgRepo(tx *gorm.DB, repoID int64) error { + return removeOrgRepo(tx, org.ID, repoID) } // RemoveOrgRepo removes all team-repository relations of organization. func (org *User) RemoveOrgRepo(repoID int64) error { - return org.removeOrgRepo(x, repoID) + return org.removeOrgRepo(db, repoID) } // CreateOrganization creates record of a new organization. @@ -121,52 +120,48 @@ func CreateOrganization(org, owner *User) (err error) { org.NumTeams = 1 org.NumMembers = 1 - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return err - } + return db.Transaction(func(tx *gorm.DB) error { + if err := tx.Create(org).Error; err != nil { + return errors.Newf("insert organization: %v", err) + } + _ = userutil.GenerateRandomAvatar(org.ID, org.Name, org.Email) - if _, err = sess.Insert(org); err != nil { - return errors.Newf("insert organization: %v", err) - } - _ = userutil.GenerateRandomAvatar(org.ID, org.Name, org.Email) + // Add initial creator to organization and owner team. + if err := tx.Create(&OrgUser{ + UID: owner.ID, + OrgID: org.ID, + IsOwner: true, + NumTeams: 1, + }).Error; err != nil { + return errors.Newf("insert org-user relation: %v", err) + } - // Add initial creator to organization and owner team. - if _, err = sess.Insert(&OrgUser{ - UID: owner.ID, - OrgID: org.ID, - IsOwner: true, - NumTeams: 1, - }); err != nil { - return errors.Newf("insert org-user relation: %v", err) - } + // Create default owner team. + t := &Team{ + OrgID: org.ID, + LowerName: strings.ToLower(ownerTeamName), + Name: ownerTeamName, + Authorize: AccessModeOwner, + NumMembers: 1, + } + if err := tx.Create(t).Error; err != nil { + return errors.Newf("insert owner team: %v", err) + } - // Create default owner team. - t := &Team{ - OrgID: org.ID, - LowerName: strings.ToLower(ownerTeamName), - Name: ownerTeamName, - Authorize: AccessModeOwner, - NumMembers: 1, - } - if _, err = sess.Insert(t); err != nil { - return errors.Newf("insert owner team: %v", err) - } + if err := tx.Create(&TeamUser{ + UID: owner.ID, + OrgID: org.ID, + TeamID: t.ID, + }).Error; err != nil { + return errors.Newf("insert team-user relation: %v", err) + } - if _, err = sess.Insert(&TeamUser{ - UID: owner.ID, - OrgID: org.ID, - TeamID: t.ID, - }); err != nil { - return errors.Newf("insert team-user relation: %v", err) - } + if err := os.MkdirAll(repoutil.UserPath(org.Name), os.ModePerm); err != nil { + return errors.Newf("create directory: %v", err) + } - if err = os.MkdirAll(repoutil.UserPath(org.Name), os.ModePerm); err != nil { - return errors.Newf("create directory: %v", err) - } - - return sess.Commit() + return nil + }) } // GetOrgByName returns organization by given name. @@ -174,35 +169,36 @@ func GetOrgByName(name string) (*User, error) { if name == "" { return nil, ErrOrgNotExist } - u := &User{ - LowerName: strings.ToLower(name), - Type: UserTypeOrganization, - } - has, err := x.Get(u) + u := &User{} + err := db.Where("lower_name = ? AND type = ?", strings.ToLower(name), UserTypeOrganization).First(u).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, ErrOrgNotExist + } return nil, err - } else if !has { - return nil, ErrOrgNotExist } return u, nil } // CountOrganizations returns number of organizations. func CountOrganizations() int64 { - count, _ := x.Where("type=1").Count(new(User)) + var count int64 + db.Model(new(User)).Where("type = ?", UserTypeOrganization).Count(&count) return count } // Organizations returns number of organizations in given page. func Organizations(page, pageSize int) ([]*User, error) { orgs := make([]*User, 0, pageSize) - return orgs, x.Limit(pageSize, (page-1)*pageSize).Where("type=1").Asc("id").Find(&orgs) + return orgs, db.Where("type = ?", UserTypeOrganization). + Offset((page - 1) * pageSize).Limit(pageSize). + Order("id ASC").Find(&orgs).Error } // deleteBeans deletes all given beans, beans should contain delete conditions. -func deleteBeans(e Engine, beans ...any) (err error) { +func deleteBeans(tx *gorm.DB, beans ...any) (err error) { for i := range beans { - if _, err = e.Delete(beans[i]); err != nil { + if err = tx.Delete(beans[i]).Error; err != nil { return err } } @@ -216,20 +212,13 @@ func DeleteOrganization(org *User) error { return err } - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return err - } - - if err = deleteBeans(sess, - &Team{OrgID: org.ID}, - &OrgUser{OrgID: org.ID}, - &TeamUser{OrgID: org.ID}, - ); err != nil { - return errors.Newf("deleteBeans: %v", err) - } - return sess.Commit() + return db.Transaction(func(tx *gorm.DB) error { + return deleteBeans(tx, + &Team{OrgID: org.ID}, + &OrgUser{OrgID: org.ID}, + &TeamUser{OrgID: org.ID}, + ) + }) } // ________ ____ ___ @@ -251,84 +240,94 @@ type OrgUser struct { // IsOrganizationOwner returns true if given user is in the owner team. func IsOrganizationOwner(orgID, userID int64) bool { - has, _ := x.Where("is_owner = ?", true).And("uid = ?", userID).And("org_id = ?", orgID).Get(new(OrgUser)) - return has + var count int64 + db.Model(new(OrgUser)).Where("is_owner = ? AND uid = ? AND org_id = ?", true, userID, orgID).Count(&count) + return count > 0 } // IsOrganizationMember returns true if given user is member of organization. func IsOrganizationMember(orgID, uid int64) bool { - has, _ := x.Where("uid=?", uid).And("org_id=?", orgID).Get(new(OrgUser)) - return has + var count int64 + db.Model(new(OrgUser)).Where("uid = ? AND org_id = ?", uid, orgID).Count(&count) + return count > 0 } // IsPublicMembership returns true if given user public his/her membership. func IsPublicMembership(orgID, uid int64) bool { - has, _ := x.Where("uid=?", uid).And("org_id=?", orgID).And("is_public=?", true).Get(new(OrgUser)) - return has + var count int64 + db.Model(new(OrgUser)).Where("uid = ? AND org_id = ? AND is_public = ?", uid, orgID, true).Count(&count) + return count > 0 } -func getOrgsByUserID(sess *xorm.Session, userID int64, showAll bool) ([]*User, error) { +func getOrgsByUserID(tx *gorm.DB, userID int64, showAll bool) ([]*User, error) { orgs := make([]*User, 0, 10) + query := tx.Table("`user`"). + Joins("INNER JOIN `org_user` ON `org_user`.org_id = `user`.id"). + Where("`org_user`.uid = ?", userID) if !showAll { - sess.And("`org_user`.is_public=?", true) + query = query.Where("`org_user`.is_public = ?", true) } - return orgs, sess.And("`org_user`.uid=?", userID). - Join("INNER", "`org_user`", "`org_user`.org_id=`user`.id").Find(&orgs) + return orgs, query.Find(&orgs).Error } // GetOrgsByUserID returns a list of organizations that the given user ID // has joined. func GetOrgsByUserID(userID int64, showAll bool) ([]*User, error) { - return getOrgsByUserID(x.NewSession(), userID, showAll) + return getOrgsByUserID(db, userID, showAll) } -func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) { +func getOwnedOrgsByUserID(tx *gorm.DB, userID int64) ([]*User, error) { orgs := make([]*User, 0, 10) - return orgs, sess.Where("`org_user`.uid=?", userID).And("`org_user`.is_owner=?", true). - Join("INNER", "`org_user`", "`org_user`.org_id=`user`.id").Find(&orgs) + return orgs, tx.Table("`user`"). + Joins("INNER JOIN `org_user` ON `org_user`.org_id = `user`.id"). + Where("`org_user`.uid = ? AND `org_user`.is_owner = ?", userID, true). + Find(&orgs).Error } // GetOwnedOrgsByUserID returns a list of organizations are owned by given user ID. func GetOwnedOrgsByUserID(userID int64) ([]*User, error) { - sess := x.NewSession() - return getOwnedOrgsByUserID(sess, userID) + return getOwnedOrgsByUserID(db, userID) } // GetOwnedOrganizationsByUserIDDesc returns a list of organizations are owned by // given user ID, ordered descending by the given condition. func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) { - sess := x.NewSession() - return getOwnedOrgsByUserID(sess.Desc(desc), userID) + orgs := make([]*User, 0, 10) + return orgs, db.Table("`user`"). + Joins("INNER JOIN `org_user` ON `org_user`.org_id = `user`.id"). + Where("`org_user`.uid = ? AND `org_user`.is_owner = ?", userID, true). + Order(desc + " DESC"). + Find(&orgs).Error } -func getOrgUsersByOrgID(e Engine, orgID int64, limit int) ([]*OrgUser, error) { +func getOrgUsersByOrgID(tx *gorm.DB, orgID int64, limit int) ([]*OrgUser, error) { orgUsers := make([]*OrgUser, 0, 10) - sess := e.Where("org_id=?", orgID) + query := tx.Where("org_id = ?", orgID) if limit > 0 { - sess = sess.Limit(limit) + query = query.Limit(limit) } - return orgUsers, sess.Find(&orgUsers) + return orgUsers, query.Find(&orgUsers).Error } // GetOrgUsersByOrgID returns all organization-user relations by organization ID. func GetOrgUsersByOrgID(orgID int64, limit int) ([]*OrgUser, error) { - return getOrgUsersByOrgID(x, orgID, limit) + return getOrgUsersByOrgID(db, orgID, limit) } // ChangeOrgUserStatus changes public or private membership status. func ChangeOrgUserStatus(orgID, uid int64, public bool) error { ou := new(OrgUser) - has, err := x.Where("uid=?", uid).And("org_id=?", orgID).Get(ou) + err := db.Where("uid = ? AND org_id = ?", uid, orgID).First(ou).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil + } return err - } else if !has { - return nil } ou.IsPublic = public - _, err = x.Id(ou.ID).AllCols().Update(ou) - return err + return db.Model(ou).Where("id = ?", ou.ID).Updates(ou).Error } // AddOrgUser adds new user to given organization. @@ -337,35 +336,33 @@ func AddOrgUser(orgID, uid int64) error { return nil } - sess := x.NewSession() - defer sess.Close() - if err := sess.Begin(); err != nil { - return err - } + return db.Transaction(func(tx *gorm.DB) error { + ou := &OrgUser{ + UID: uid, + OrgID: orgID, + } - ou := &OrgUser{ - UID: uid, - OrgID: orgID, - } + if err := tx.Create(ou).Error; err != nil { + return err + } + if err := tx.Exec("UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID).Error; err != nil { + return err + } - if _, err := sess.Insert(ou); err != nil { - return err - } else if _, err = sess.Exec("UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID); err != nil { - return err - } - - return sess.Commit() + return nil + }) } // RemoveOrgUser removes user from given organization. func RemoveOrgUser(orgID, userID int64) error { ou := new(OrgUser) - has, err := x.Where("uid=?", userID).And("org_id=?", orgID).Get(ou) + err := db.Where("uid = ? AND org_id = ?", userID, orgID).First(ou).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil + } return errors.Newf("get org-user: %v", err) - } else if !has { - return nil } user, err := Handle.Users().GetByID(context.TODO(), userID) @@ -394,71 +391,69 @@ func RemoveOrgUser(orgID, userID int64) error { } } - sess := x.NewSession() - defer sess.Close() - if err := sess.Begin(); err != nil { - return err - } - - if _, err := sess.ID(ou.ID).Delete(ou); err != nil { - return err - } else if _, err = sess.Exec("UPDATE `user` SET num_members=num_members-1 WHERE id=?", orgID); err != nil { - return err - } - - // Delete all repository accesses and unwatch them. - repoIDs := make([]int64, 0, len(repos)) - for i := range repos { - repoIDs = append(repoIDs, repos[i].ID) - if err = watchRepo(sess, user.ID, repos[i].ID, false); err != nil { + return db.Transaction(func(tx *gorm.DB) error { + if err := tx.Where("id = ?", ou.ID).Delete(ou).Error; err != nil { return err } - } - - if len(repoIDs) > 0 { - if _, err = sess.Where("user_id = ?", user.ID).In("repo_id", repoIDs).Delete(new(Access)); err != nil { + if err := tx.Exec("UPDATE `user` SET num_members = num_members - 1 WHERE id = ?", orgID).Error; err != nil { return err } - } - // Delete member in his/her teams. - teams, err := getUserTeams(sess, org.ID, user.ID) - if err != nil { - return err - } - for _, t := range teams { - if err = removeTeamMember(sess, org.ID, t.ID, user.ID); err != nil { + // Delete all repository accesses and unwatch them. + repoIDs := make([]int64, 0, len(repos)) + for i := range repos { + repoIDs = append(repoIDs, repos[i].ID) + if err = watchRepo(tx, user.ID, repos[i].ID, false); err != nil { + return err + } + } + + if len(repoIDs) > 0 { + if err := tx.Where("user_id = ? AND repo_id IN ?", user.ID, repoIDs).Delete(new(Access)).Error; err != nil { + return err + } + } + + // Delete member in his/her teams. + teams, err := getUserTeams(tx, org.ID, user.ID) + if err != nil { return err } - } + for _, t := range teams { + if err = removeTeamMember(tx, org.ID, t.ID, user.ID); err != nil { + return err + } + } - return sess.Commit() + return nil + }) } -func removeOrgRepo(e Engine, orgID, repoID int64) error { - _, err := e.Delete(&TeamRepo{ - OrgID: orgID, - RepoID: repoID, - }) - return err +func removeOrgRepo(tx *gorm.DB, orgID, repoID int64) error { + return tx.Where("org_id = ? AND repo_id = ?", orgID, repoID).Delete(&TeamRepo{}).Error } // RemoveOrgRepo removes all team-repository relations of given organization. func RemoveOrgRepo(orgID, repoID int64) error { - return removeOrgRepo(x, orgID, repoID) + return removeOrgRepo(db, orgID, repoID) } -func (org *User) getUserTeams(e Engine, userID int64, cols ...string) ([]*Team, error) { +func (org *User) getUserTeams(tx *gorm.DB, userID int64, cols ...string) ([]*Team, error) { teams := make([]*Team, 0, org.NumTeams) - return teams, e.Where("team_user.org_id = ?", org.ID). - And("team_user.uid = ?", userID). - Join("INNER", "team_user", "team_user.team_id = team.id"). - Cols(cols...).Find(&teams) + query := tx.Table("team"). + Joins("INNER JOIN team_user ON team_user.team_id = team.id"). + Where("team_user.org_id = ? AND team_user.uid = ?", org.ID, userID) + + if len(cols) > 0 { + query = query.Select(cols) + } + + return teams, query.Find(&teams).Error } // GetUserTeamIDs returns of all team IDs of the organization that user is member of. func (org *User) GetUserTeamIDs(userID int64) ([]int64, error) { - teams, err := org.getUserTeams(x, userID, "team.id") + teams, err := org.getUserTeams(db, userID, "team.id") if err != nil { return nil, errors.Newf("getUserTeams [%d]: %v", userID, err) } @@ -473,7 +468,7 @@ func (org *User) GetUserTeamIDs(userID int64) ([]int64, error) { // GetTeams returns all teams that belong to organization, // and that the user has joined. func (org *User) GetUserTeams(userID int64) ([]*Team, error) { - return org.getUserTeams(x, userID) + return org.getUserTeams(db, userID) } // GetUserRepositories returns a range of repositories in organization which the user has access to,