Use db.WithTx/WithTx2 instead of TxContext when possible (#35428)

This commit is contained in:
Lunny Xiao
2025-09-09 20:15:01 -07:00
committed by GitHub
parent fb247f640e
commit e35e724e42
13 changed files with 601 additions and 694 deletions

View File

@@ -261,74 +261,67 @@ func GetRefEndNamesAndURLs(issues []*issues_model.Issue, repoLink string) (map[i
// deleteIssue deletes the issue
func deleteIssue(ctx context.Context, issue *issues_model.Issue) ([]string, error) {
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return nil, err
}
defer committer.Close()
if _, err := db.GetEngine(ctx).ID(issue.ID).NoAutoCondition().Delete(issue); err != nil {
return nil, err
}
// update the total issue numbers
if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, false); err != nil {
return nil, err
}
// if the issue is closed, update the closed issue numbers
if issue.IsClosed {
if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, true); err != nil {
return db.WithTx2(ctx, func(ctx context.Context) ([]string, error) {
if _, err := db.GetEngine(ctx).ID(issue.ID).NoAutoCondition().Delete(issue); err != nil {
return nil, err
}
}
if err := issues_model.UpdateMilestoneCounters(ctx, issue.MilestoneID); err != nil {
return nil, fmt.Errorf("error updating counters for milestone id %d: %w",
issue.MilestoneID, err)
}
// update the total issue numbers
if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, false); err != nil {
return nil, err
}
// if the issue is closed, update the closed issue numbers
if issue.IsClosed {
if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, true); err != nil {
return nil, err
}
}
if err := activities_model.DeleteIssueActions(ctx, issue.RepoID, issue.ID, issue.Index); err != nil {
return nil, err
}
if err := issues_model.UpdateMilestoneCounters(ctx, issue.MilestoneID); err != nil {
return nil, fmt.Errorf("error updating counters for milestone id %d: %w",
issue.MilestoneID, err)
}
// find attachments related to this issue and remove them
if err := issue.LoadAttachments(ctx); err != nil {
return nil, err
}
if err := activities_model.DeleteIssueActions(ctx, issue.RepoID, issue.ID, issue.Index); err != nil {
return nil, err
}
var attachmentPaths []string
for i := range issue.Attachments {
attachmentPaths = append(attachmentPaths, issue.Attachments[i].RelativePath())
}
// find attachments related to this issue and remove them
if err := issue.LoadAttachments(ctx); err != nil {
return nil, err
}
// delete all database data still assigned to this issue
if err := db.DeleteBeans(ctx,
&issues_model.ContentHistory{IssueID: issue.ID},
&issues_model.Comment{IssueID: issue.ID},
&issues_model.IssueLabel{IssueID: issue.ID},
&issues_model.IssueDependency{IssueID: issue.ID},
&issues_model.IssueAssignees{IssueID: issue.ID},
&issues_model.IssueUser{IssueID: issue.ID},
&activities_model.Notification{IssueID: issue.ID},
&issues_model.Reaction{IssueID: issue.ID},
&issues_model.IssueWatch{IssueID: issue.ID},
&issues_model.Stopwatch{IssueID: issue.ID},
&issues_model.TrackedTime{IssueID: issue.ID},
&project_model.ProjectIssue{IssueID: issue.ID},
&repo_model.Attachment{IssueID: issue.ID},
&issues_model.PullRequest{IssueID: issue.ID},
&issues_model.Comment{RefIssueID: issue.ID},
&issues_model.IssueDependency{DependencyID: issue.ID},
&issues_model.Comment{DependentIssueID: issue.ID},
&issues_model.IssuePin{IssueID: issue.ID},
); err != nil {
return nil, err
}
var attachmentPaths []string
for i := range issue.Attachments {
attachmentPaths = append(attachmentPaths, issue.Attachments[i].RelativePath())
}
if err := committer.Commit(); err != nil {
return nil, err
}
return attachmentPaths, nil
// delete all database data still assigned to this issue
if err := db.DeleteBeans(ctx,
&issues_model.ContentHistory{IssueID: issue.ID},
&issues_model.Comment{IssueID: issue.ID},
&issues_model.IssueLabel{IssueID: issue.ID},
&issues_model.IssueDependency{IssueID: issue.ID},
&issues_model.IssueAssignees{IssueID: issue.ID},
&issues_model.IssueUser{IssueID: issue.ID},
&activities_model.Notification{IssueID: issue.ID},
&issues_model.Reaction{IssueID: issue.ID},
&issues_model.IssueWatch{IssueID: issue.ID},
&issues_model.Stopwatch{IssueID: issue.ID},
&issues_model.TrackedTime{IssueID: issue.ID},
&project_model.ProjectIssue{IssueID: issue.ID},
&repo_model.Attachment{IssueID: issue.ID},
&issues_model.PullRequest{IssueID: issue.ID},
&issues_model.Comment{RefIssueID: issue.ID},
&issues_model.IssueDependency{DependencyID: issue.ID},
&issues_model.Comment{DependentIssueID: issue.ID},
&issues_model.IssuePin{IssueID: issue.ID},
); err != nil {
return nil, err
}
return attachmentPaths, nil
})
}
// DeleteOrphanedIssues delete issues without a repo

View File

@@ -52,39 +52,34 @@ func deleteOrganization(ctx context.Context, org *org_model.Organization) error
// DeleteOrganization completely and permanently deletes everything of organization.
func DeleteOrganization(ctx context.Context, org *org_model.Organization, purge bool) error {
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
}
defer committer.Close()
if purge {
err := repo_service.DeleteOwnerRepositoriesDirectly(ctx, org.AsUser())
if err != nil {
return err
if err := db.WithTx(ctx, func(ctx context.Context) error {
if purge {
err := repo_service.DeleteOwnerRepositoriesDirectly(ctx, org.AsUser())
if err != nil {
return err
}
}
}
// Check ownership of repository.
count, err := repo_model.CountRepositories(ctx, repo_model.CountRepositoryOptions{OwnerID: org.ID})
if err != nil {
return fmt.Errorf("GetRepositoryCount: %w", err)
} else if count > 0 {
return repo_model.ErrUserOwnRepos{UID: org.ID}
}
// Check ownership of repository.
count, err := repo_model.CountRepositories(ctx, repo_model.CountRepositoryOptions{OwnerID: org.ID})
if err != nil {
return fmt.Errorf("GetRepositoryCount: %w", err)
} else if count > 0 {
return repo_model.ErrUserOwnRepos{UID: org.ID}
}
// Check ownership of packages.
if ownsPackages, err := packages_model.HasOwnerPackages(ctx, org.ID); err != nil {
return fmt.Errorf("HasOwnerPackages: %w", err)
} else if ownsPackages {
return packages_model.ErrUserOwnPackages{UID: org.ID}
}
// Check ownership of packages.
if ownsPackages, err := packages_model.HasOwnerPackages(ctx, org.ID); err != nil {
return fmt.Errorf("HasOwnerPackages: %w", err)
} else if ownsPackages {
return packages_model.ErrUserOwnPackages{UID: org.ID}
}
if err := deleteOrganization(ctx, org); err != nil {
return fmt.Errorf("DeleteOrganization: %w", err)
}
if err := committer.Commit(); err != nil {
if err := deleteOrganization(ctx, org); err != nil {
return fmt.Errorf("DeleteOrganization: %w", err)
}
return nil
}); err != nil {
return err
}

View File

@@ -47,57 +47,52 @@ func RemoveOrgUser(ctx context.Context, org *organization.Organization, user *us
}
}
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
}
defer committer.Close()
return db.WithTx(ctx, func(ctx context.Context) error {
if _, err := db.DeleteByID[organization.OrgUser](ctx, ou.ID); err != nil {
return err
} else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members=num_members-1 WHERE id=?", org.ID); err != nil {
return err
}
if _, err := db.DeleteByID[organization.OrgUser](ctx, ou.ID); err != nil {
return err
} else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members=num_members-1 WHERE id=?", org.ID); err != nil {
return err
}
// Delete all repository accesses and unwatch them.
env, err := repo_model.AccessibleReposEnv(ctx, org, user.ID)
if err != nil {
return fmt.Errorf("AccessibleReposEnv: %w", err)
}
repoIDs, err := env.RepoIDs(ctx)
if err != nil {
return fmt.Errorf("GetUserRepositories [%d]: %w", user.ID, err)
}
// Delete all repository accesses and unwatch them.
env, err := repo_model.AccessibleReposEnv(ctx, org, user.ID)
if err != nil {
return fmt.Errorf("AccessibleReposEnv: %w", err)
}
repoIDs, err := env.RepoIDs(ctx)
if err != nil {
return fmt.Errorf("GetUserRepositories [%d]: %w", user.ID, err)
}
for _, repoID := range repoIDs {
repo, err := repo_model.GetRepositoryByID(ctx, repoID)
if err != nil {
return err
}
if err = repo_model.WatchRepo(ctx, user, repo, false); err != nil {
return err
}
}
for _, repoID := range repoIDs {
repo, err := repo_model.GetRepositoryByID(ctx, repoID)
if len(repoIDs) > 0 {
if _, err = db.GetEngine(ctx).
Where("user_id = ?", user.ID).
In("repo_id", repoIDs).
Delete(new(access_model.Access)); err != nil {
return err
}
}
// Delete member in their teams.
teams, err := organization.GetUserOrgTeams(ctx, org.ID, user.ID)
if err != nil {
return err
}
if err = repo_model.WatchRepo(ctx, user, repo, false); err != nil {
return err
for _, t := range teams {
if err = removeTeamMember(ctx, t, user); err != nil {
return err
}
}
}
if len(repoIDs) > 0 {
if _, err = db.GetEngine(ctx).
Where("user_id = ?", user.ID).
In("repo_id", repoIDs).
Delete(new(access_model.Access)); err != nil {
return err
}
}
// Delete member in their teams.
teams, err := organization.GetUserOrgTeams(ctx, org.ID, user.ID)
if err != nil {
return err
}
for _, t := range teams {
if err = removeTeamMember(ctx, t, user); err != nil {
return err
}
}
return committer.Commit()
return nil
})
}

View File

@@ -267,74 +267,69 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo
}
rel.LowerTagName = strings.ToLower(rel.TagName)
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
}
defer committer.Close()
oldRelease, err := repo_model.GetReleaseByID(ctx, rel.ID)
if err != nil {
return err
}
isConvertedFromTag := oldRelease.IsTag && !rel.IsTag
if err = repo_model.UpdateRelease(ctx, rel); err != nil {
return err
}
if err = repo_model.AddReleaseAttachments(ctx, rel.ID, addAttachmentUUIDs); err != nil {
return fmt.Errorf("AddReleaseAttachments: %w", err)
}
deletedUUIDs := make(container.Set[string])
if len(delAttachmentUUIDs) > 0 {
// Check attachments
attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, delAttachmentUUIDs)
if err != nil {
return fmt.Errorf("GetAttachmentsByUUIDs [uuids: %v]: %w", delAttachmentUUIDs, err)
if err := db.WithTx(ctx, func(ctx context.Context) error {
if err = repo_model.UpdateRelease(ctx, rel); err != nil {
return err
}
for _, attach := range attachments {
if attach.ReleaseID != rel.ID {
return util.NewPermissionDeniedErrorf("delete attachment of release permission denied")
if err = repo_model.AddReleaseAttachments(ctx, rel.ID, addAttachmentUUIDs); err != nil {
return fmt.Errorf("AddReleaseAttachments: %w", err)
}
deletedUUIDs := make(container.Set[string])
if len(delAttachmentUUIDs) > 0 {
// Check attachments
attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, delAttachmentUUIDs)
if err != nil {
return fmt.Errorf("GetAttachmentsByUUIDs [uuids: %v]: %w", delAttachmentUUIDs, err)
}
for _, attach := range attachments {
if attach.ReleaseID != rel.ID {
return util.NewPermissionDeniedErrorf("delete attachment of release permission denied")
}
deletedUUIDs.Add(attach.UUID)
}
deletedUUIDs.Add(attach.UUID)
}
if _, err := repo_model.DeleteAttachments(ctx, attachments, true); err != nil {
return fmt.Errorf("DeleteAttachments [uuids: %v]: %w", delAttachmentUUIDs, err)
}
}
if len(editAttachments) > 0 {
updateAttachmentsList := make([]string, 0, len(editAttachments))
for k := range editAttachments {
updateAttachmentsList = append(updateAttachmentsList, k)
}
// Check attachments
attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, updateAttachmentsList)
if err != nil {
return fmt.Errorf("GetAttachmentsByUUIDs [uuids: %v]: %w", updateAttachmentsList, err)
}
for _, attach := range attachments {
if attach.ReleaseID != rel.ID {
return util.NewPermissionDeniedErrorf("update attachment of release permission denied")
if _, err := repo_model.DeleteAttachments(ctx, attachments, true); err != nil {
return fmt.Errorf("DeleteAttachments [uuids: %v]: %w", delAttachmentUUIDs, err)
}
}
for uuid, newName := range editAttachments {
if !deletedUUIDs.Contains(uuid) {
if err = repo_model.UpdateAttachmentByUUID(ctx, &repo_model.Attachment{
UUID: uuid,
Name: newName,
}, "name"); err != nil {
return err
if len(editAttachments) > 0 {
updateAttachmentsList := make([]string, 0, len(editAttachments))
for k := range editAttachments {
updateAttachmentsList = append(updateAttachmentsList, k)
}
// Check attachments
attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, updateAttachmentsList)
if err != nil {
return fmt.Errorf("GetAttachmentsByUUIDs [uuids: %v]: %w", updateAttachmentsList, err)
}
for _, attach := range attachments {
if attach.ReleaseID != rel.ID {
return util.NewPermissionDeniedErrorf("update attachment of release permission denied")
}
}
for uuid, newName := range editAttachments {
if !deletedUUIDs.Contains(uuid) {
if err = repo_model.UpdateAttachmentByUUID(ctx, &repo_model.Attachment{
UUID: uuid,
Name: newName,
}, "name"); err != nil {
return err
}
}
}
}
}
if err := committer.Commit(); err != nil {
return nil
}); err != nil {
return err
}

View File

@@ -173,92 +173,88 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
}
}
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return nil, err
}
defer committer.Close()
if opts.Mirror {
remoteAddress, err := util.SanitizeURL(opts.CloneAddr)
if err != nil {
return repo, err
}
mirrorModel := repo_model.Mirror{
RepoID: repo.ID,
Interval: setting.Mirror.DefaultInterval,
EnablePrune: true,
NextUpdateUnix: timeutil.TimeStampNow().AddDuration(setting.Mirror.DefaultInterval),
LFS: opts.LFS,
RemoteAddress: remoteAddress,
}
if opts.LFS {
mirrorModel.LFSEndpoint = opts.LFSEndpoint
}
if opts.MirrorInterval != "" {
parsedInterval, err := time.ParseDuration(opts.MirrorInterval)
return db.WithTx2(ctx, func(ctx context.Context) (*repo_model.Repository, error) {
if opts.Mirror {
remoteAddress, err := util.SanitizeURL(opts.CloneAddr)
if err != nil {
log.Error("Failed to set Interval: %v", err)
return repo, err
}
if parsedInterval == 0 {
mirrorModel.Interval = 0
mirrorModel.NextUpdateUnix = 0
} else if parsedInterval < setting.Mirror.MinInterval {
err := fmt.Errorf("interval %s is set below Minimum Interval of %s", parsedInterval, setting.Mirror.MinInterval)
log.Error("Interval: %s is too frequent", opts.MirrorInterval)
return repo, err
} else {
mirrorModel.Interval = parsedInterval
mirrorModel.NextUpdateUnix = timeutil.TimeStampNow().AddDuration(parsedInterval)
mirrorModel := repo_model.Mirror{
RepoID: repo.ID,
Interval: setting.Mirror.DefaultInterval,
EnablePrune: true,
NextUpdateUnix: timeutil.TimeStampNow().AddDuration(setting.Mirror.DefaultInterval),
LFS: opts.LFS,
RemoteAddress: remoteAddress,
}
if opts.LFS {
mirrorModel.LFSEndpoint = opts.LFSEndpoint
}
if opts.MirrorInterval != "" {
parsedInterval, err := time.ParseDuration(opts.MirrorInterval)
if err != nil {
log.Error("Failed to set Interval: %v", err)
return repo, err
}
if parsedInterval == 0 {
mirrorModel.Interval = 0
mirrorModel.NextUpdateUnix = 0
} else if parsedInterval < setting.Mirror.MinInterval {
err := fmt.Errorf("interval %s is set below Minimum Interval of %s", parsedInterval, setting.Mirror.MinInterval)
log.Error("Interval: %s is too frequent", opts.MirrorInterval)
return repo, err
} else {
mirrorModel.Interval = parsedInterval
mirrorModel.NextUpdateUnix = timeutil.TimeStampNow().AddDuration(parsedInterval)
}
}
if err = repo_model.InsertMirror(ctx, &mirrorModel); err != nil {
return repo, fmt.Errorf("InsertOne: %w", err)
}
repo.IsMirror = true
if err = repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "num_watches", "is_empty", "default_branch", "default_wiki_branch", "is_mirror"); err != nil {
return nil, err
}
if err = repo_module.UpdateRepoSize(ctx, repo); err != nil {
log.Error("Failed to update size for repository: %v", err)
}
// this is necessary for sync local tags from remote
configName := fmt.Sprintf("remote.%s.fetch", mirrorModel.GetRemoteName())
if stdout, _, err := git.NewCommand("config").
AddOptionValues("--add", configName, `+refs/tags/*:refs/tags/*`).
RunStdString(ctx, &git.RunOpts{Dir: repoPath}); err != nil {
log.Error("MigrateRepositoryGitData(git config --add <remote> +refs/tags/*:refs/tags/*) in %v: Stdout: %s\nError: %v", repo, stdout, err)
return repo, fmt.Errorf("error in MigrateRepositoryGitData(git config --add <remote> +refs/tags/*:refs/tags/*): %w", err)
}
} else {
if err = repo_module.UpdateRepoSize(ctx, repo); err != nil {
log.Error("Failed to update size for repository: %v", err)
}
if repo, err = CleanUpMigrateInfo(ctx, repo); err != nil {
return nil, err
}
}
if err = repo_model.InsertMirror(ctx, &mirrorModel); err != nil {
return repo, fmt.Errorf("InsertOne: %w", err)
var enableRepoUnits []repo_model.RepoUnit
if opts.Releases && !unit_model.TypeReleases.UnitGlobalDisabled() {
enableRepoUnits = append(enableRepoUnits, repo_model.RepoUnit{RepoID: repo.ID, Type: unit_model.TypeReleases})
}
repo.IsMirror = true
if err = repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "num_watches", "is_empty", "default_branch", "default_wiki_branch", "is_mirror"); err != nil {
return nil, err
if opts.Wiki && !unit_model.TypeWiki.UnitGlobalDisabled() {
enableRepoUnits = append(enableRepoUnits, repo_model.RepoUnit{RepoID: repo.ID, Type: unit_model.TypeWiki})
}
if err = repo_module.UpdateRepoSize(ctx, repo); err != nil {
log.Error("Failed to update size for repository: %v", err)
if len(enableRepoUnits) > 0 {
err = UpdateRepositoryUnits(ctx, repo, enableRepoUnits, nil)
if err != nil {
return nil, err
}
}
// this is necessary for sync local tags from remote
configName := fmt.Sprintf("remote.%s.fetch", mirrorModel.GetRemoteName())
if stdout, _, err := git.NewCommand("config").
AddOptionValues("--add", configName, `+refs/tags/*:refs/tags/*`).
RunStdString(ctx, &git.RunOpts{Dir: repoPath}); err != nil {
log.Error("MigrateRepositoryGitData(git config --add <remote> +refs/tags/*:refs/tags/*) in %v: Stdout: %s\nError: %v", repo, stdout, err)
return repo, fmt.Errorf("error in MigrateRepositoryGitData(git config --add <remote> +refs/tags/*:refs/tags/*): %w", err)
}
} else {
if err = repo_module.UpdateRepoSize(ctx, repo); err != nil {
log.Error("Failed to update size for repository: %v", err)
}
if repo, err = CleanUpMigrateInfo(ctx, repo); err != nil {
return nil, err
}
}
var enableRepoUnits []repo_model.RepoUnit
if opts.Releases && !unit_model.TypeReleases.UnitGlobalDisabled() {
enableRepoUnits = append(enableRepoUnits, repo_model.RepoUnit{RepoID: repo.ID, Type: unit_model.TypeReleases})
}
if opts.Wiki && !unit_model.TypeWiki.UnitGlobalDisabled() {
enableRepoUnits = append(enableRepoUnits, repo_model.RepoUnit{RepoID: repo.ID, Type: unit_model.TypeWiki})
}
if len(enableRepoUnits) > 0 {
err = UpdateRepositoryUnits(ctx, repo, enableRepoUnits, nil)
if err != nil {
return nil, err
}
}
return repo, committer.Commit()
return repo, nil
})
}
// CleanUpMigrateInfo finishes migrating repository and/or wiki with things that don't need to be done for mirrors.

View File

@@ -210,65 +210,59 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error {
}
}
ctx, committer, err := db.TxContext(ctx)
if err != nil {
if err := db.WithTx(ctx, func(ctx context.Context) error {
// Note: A user owns any repository or belongs to any organization
// cannot perform delete operation. This causes a race with the purge above
// however consistency requires that we ensure that this is the case
// Check ownership of repository.
count, err := repo_model.CountRepositories(ctx, repo_model.CountRepositoryOptions{OwnerID: u.ID})
if err != nil {
return fmt.Errorf("GetRepositoryCount: %w", err)
} else if count > 0 {
return repo_model.ErrUserOwnRepos{UID: u.ID}
}
// Check membership of organization.
count, err = organization.GetOrganizationCount(ctx, u)
if err != nil {
return fmt.Errorf("GetOrganizationCount: %w", err)
} else if count > 0 {
return organization.ErrUserHasOrgs{UID: u.ID}
}
// Check ownership of packages.
if ownsPackages, err := packages_model.HasOwnerPackages(ctx, u.ID); err != nil {
return fmt.Errorf("HasOwnerPackages: %w", err)
} else if ownsPackages {
return packages_model.ErrUserOwnPackages{UID: u.ID}
}
if err := deleteUser(ctx, u, purge); err != nil {
return fmt.Errorf("DeleteUser: %w", err)
}
return nil
}); err != nil {
return err
}
defer committer.Close()
// Note: A user owns any repository or belongs to any organization
// cannot perform delete operation. This causes a race with the purge above
// however consistency requires that we ensure that this is the case
// Check ownership of repository.
count, err := repo_model.CountRepositories(ctx, repo_model.CountRepositoryOptions{OwnerID: u.ID})
if err != nil {
return fmt.Errorf("GetRepositoryCount: %w", err)
} else if count > 0 {
return repo_model.ErrUserOwnRepos{UID: u.ID}
}
// Check membership of organization.
count, err = organization.GetOrganizationCount(ctx, u)
if err != nil {
return fmt.Errorf("GetOrganizationCount: %w", err)
} else if count > 0 {
return organization.ErrUserHasOrgs{UID: u.ID}
}
// Check ownership of packages.
if ownsPackages, err := packages_model.HasOwnerPackages(ctx, u.ID); err != nil {
return fmt.Errorf("HasOwnerPackages: %w", err)
} else if ownsPackages {
return packages_model.ErrUserOwnPackages{UID: u.ID}
}
if err := deleteUser(ctx, u, purge); err != nil {
return fmt.Errorf("DeleteUser: %w", err)
}
if err := committer.Commit(); err != nil {
if err := asymkey_service.RewriteAllPublicKeys(ctx); err != nil {
return err
}
_ = committer.Close()
if err = asymkey_service.RewriteAllPublicKeys(ctx); err != nil {
return err
}
if err = asymkey_service.RewriteAllPrincipalKeys(ctx); err != nil {
if err := asymkey_service.RewriteAllPrincipalKeys(ctx); err != nil {
return err
}
// Note: There are something just cannot be roll back, so just keep error logs of those operations.
path := user_model.UserPath(u.Name)
if err = util.RemoveAll(path); err != nil {
if err := util.RemoveAll(path); err != nil {
err = fmt.Errorf("failed to RemoveAll %s: %w", path, err)
_ = system_model.CreateNotice(ctx, system_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err))
}
if u.Avatar != "" {
avatarPath := u.CustomAvatarRelativePath()
if err = storage.Avatars.Delete(avatarPath); err != nil {
if err := storage.Avatars.Delete(avatarPath); err != nil {
err = fmt.Errorf("failed to remove %s: %w", avatarPath, err)
_ = system_model.CreateNotice(ctx, system_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err))
}