Improve repair endpoints (#5767)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Anbraten
2025-11-20 22:44:05 +01:00
committed by GitHub
parent 2d7833736d
commit 92b48f813a
2 changed files with 71 additions and 54 deletions

View File

@@ -51,7 +51,7 @@ func PostRepo(c *gin.Context) {
_forge, err := server.Config.Services.Manager.ForgeFromUser(user)
if err != nil {
log.Error().Err(err).Msg("Cannot get forge from user")
c.AbortWithStatus(http.StatusInternalServerError)
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}
@@ -378,7 +378,7 @@ func GetRepoBranches(c *gin.Context) {
_forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
if err != nil {
log.Error().Err(err).Msg("Cannot get forge from repo")
c.AbortWithStatus(http.StatusInternalServerError)
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}
@@ -388,8 +388,6 @@ func GetRepoBranches(c *gin.Context) {
return
}
forge.Refresh(c, _forge, _store, repoUser)
branches, err := _forge.Branches(c, repoUser, repo, session.Pagination(c))
if err != nil {
log.Error().Err(err).Msg("failed to load branches")
@@ -417,7 +415,7 @@ func GetRepoPullRequests(c *gin.Context) {
_forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
if err != nil {
log.Error().Err(err).Msg("Cannot get forge from repo")
c.AbortWithStatus(http.StatusInternalServerError)
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}
@@ -455,7 +453,7 @@ func DeleteRepo(c *gin.Context) {
_forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
if err != nil {
log.Error().Err(err).Msg("Cannot get forge from repo")
c.AbortWithStatus(http.StatusInternalServerError)
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}
@@ -493,10 +491,13 @@ func DeleteRepo(c *gin.Context) {
// @Param repo_id path int true "the repository id"
func RepairRepo(c *gin.Context) {
repo := session.Repo(c)
repairRepo(c, repo, true, false)
if c.Writer.Written() {
err := repairRepo(c, repo, true)
if err != nil {
log.Error().Err(err).Msgf("repair repo '%s' failed", repo.FullName)
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}
c.Status(http.StatusNoContent)
}
@@ -517,7 +518,7 @@ func MoveRepo(c *gin.Context) {
_forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
if err != nil {
log.Error().Err(err).Msg("Cannot get forge from repo")
c.AbortWithStatus(http.StatusInternalServerError)
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}
@@ -635,53 +636,55 @@ func RepairAllRepos(c *gin.Context) {
return
}
failedRepos := make([]int64, 0)
for _, r := range repos {
repairRepo(c, r, false, true)
if c.Writer.Written() {
return
// updatePermissions is false as RepoListAll does not load permissions
updatePermissions := false
err := repairRepo(c, r, updatePermissions)
if err != nil {
failedRepos = append(failedRepos, r.ID)
_ = c.Error(err)
log.Error().Err(err).Msgf("failed to repair repo '%s'", r.FullName)
}
}
c.Status(http.StatusNoContent)
if len(failedRepos) > 0 {
c.JSON(http.StatusInternalServerError, map[string]any{
"error": "failed to repair some repos",
"failed_repos": failedRepos,
})
} else {
c.Status(http.StatusNoContent)
}
}
func repairRepo(c *gin.Context, repo *model.Repo, withPerms, skipOnErr bool) {
func repairRepo(c *gin.Context, repo *model.Repo, updatePermissions bool) error {
_store := store.FromContext(c)
_forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
if err != nil {
log.Error().Err(err).Msg("Cannot get forge from repo")
c.AbortWithStatus(http.StatusInternalServerError)
return
return err
}
user, err := _store.GetUser(repo.UserID)
repoUser, err := repairRepoUser(c, repo, _store)
if err != nil {
if errors.Is(err, types.RecordNotExist) {
oldUserID := repo.UserID
user = session.User(c)
repo.UserID = user.ID
err = _store.UpdateRepo(repo)
if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
}
log.Debug().Msgf("Could not find repo user with ID %d during repo repair, set to repair request user with ID %d", oldUserID, user.ID)
} else {
_ = c.AbortWithError(http.StatusInternalServerError, err)
}
return
log.Error().Err(err).Msgf("cannot assign user to repo '%s'", repo.FullName)
return err
}
// creates the jwt token used to verify the repository
// refresh user token if necessary
forge.Refresh(c, _forge, _store, repoUser)
// creates a new jwt token used to verify webhook calls
t := token.New(token.HookToken)
t.Set("repo-forge-remote-id", string(repo.ForgeRemoteID))
t.Set("forge-id", strconv.FormatInt(repo.ForgeID, 10))
sig, err := t.Sign(repo.Hash)
if err != nil {
c.String(http.StatusInternalServerError, err.Error())
return
return err
}
// reconstruct the hook url
// reconstruct the webhook url
host := server.Config.Server.WebhookHost
hookURL := fmt.Sprintf(
"%s/api/hook?access_token=%s",
@@ -689,44 +692,58 @@ func repairRepo(c *gin.Context, repo *model.Repo, withPerms, skipOnErr bool) {
sig,
)
from, err := _forge.Repo(c, user, repo.ForgeRemoteID, repo.Owner, repo.Name)
from, err := _forge.Repo(c, repoUser, repo.ForgeRemoteID, repo.Owner, repo.Name)
if err != nil {
log.Error().Err(err).Msgf("get repo '%s/%s' from forge", repo.Owner, repo.Name)
if !skipOnErr {
c.AbortWithStatus(http.StatusInternalServerError)
}
return
return err
}
if repo.FullName != from.FullName {
// create a redirection
err = _store.CreateRedirection(&model.Redirection{RepoID: repo.ID, FullName: repo.FullName})
if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
return err
}
}
repo.Update(from)
if err := _store.UpdateRepo(repo); err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
return err
}
if withPerms {
if updatePermissions {
repo.Perm.Pull = from.Perm.Pull
repo.Perm.Push = from.Perm.Push
repo.Perm.Admin = from.Perm.Admin
if err := _store.PermUpsert(repo.Perm); err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
return err
}
}
if err := _forge.Deactivate(c, user, repo, host); err != nil {
log.Trace().Err(err).Msgf("deactivate repo '%s' to repair failed", repo.FullName)
}
if err := _forge.Activate(c, user, repo, hookURL); err != nil {
c.String(http.StatusInternalServerError, err.Error())
return
// remove webhook (deactivate) and recreate it (activate)
if err := _forge.Deactivate(c, repoUser, repo, host); err != nil {
log.Debug().Err(err).Msgf("deactivate repo '%s' to repair failed", repo.FullName)
}
return _forge.Activate(c, repoUser, repo, hookURL)
}
func repairRepoUser(c *gin.Context, repo *model.Repo, _store store.Store) (*model.User, error) {
repoUser, err := _store.GetUser(repo.UserID)
if err != nil {
if errors.Is(err, types.RecordNotExist) {
oldUserID := repo.UserID
sessionUser := session.User(c)
repo.UserID = sessionUser.ID
err = _store.UpdateRepo(repo)
if err != nil {
return nil, err
}
log.Debug().Msgf("Could not find repo user with ID %d during repo repair, set to repair request user with ID %d", oldUserID, sessionUser.ID)
return sessionUser, nil
}
return nil, err
}
return repoUser, nil
}

View File

@@ -50,10 +50,10 @@ func Refresh(c context.Context, forge Forge, _store store.Store, user *model.Use
return
}
ok, err := refresher.Refresh(c, user)
userUpdated, err := refresher.Refresh(c, user)
if err != nil {
log.Error().Err(err).Msgf("refresh oauth token of user '%s' failed", user.Login)
} else if ok {
} else if userUpdated {
if err := _store.UpdateUser(user); err != nil {
log.Error().Err(err).Msg("fail to save user to store after refresh oauth token")
}