0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-05-12 00:22:59 +02:00

updated DeleteOrgRepos to delete in the background

This commit is contained in:
karthikbhandary2 2026-02-27 22:50:38 +05:30
parent ed4b335f9f
commit 5b819b42aa
5 changed files with 33 additions and 132 deletions

View File

@ -440,25 +440,3 @@ type UpdateRepoAvatarOption struct {
// image must be base64 encoded
Image string `json:"image" binding:"Required"`
}
// DeleteOrgReposResponse represents the response for deleting organization repositories
// swagger:model
type DeleteOrgReposResponse struct {
// Number of repositories successfully deleted
SuccessCount int `json:"success_count"`
// Number of repositories that failed to delete
FailureCount int `json:"failure_count"`
// List of repository names that were deleted
Deleted []string `json:"deleted"`
// Details about repositories that failed to delete
Failed []DeleteRepoFailure `json:"failed"`
}
// DeleteRepoFailure represents a repository that failed to delete
// swagger:model
type DeleteRepoFailure struct {
// Repository name
RepoName string `json:"repo_name"`
// Message to be displayed
Message string `json:"reason"`
}

View File

@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
api "code.gitea.io/gitea/modules/structs"
@ -510,8 +511,8 @@ func DeleteOrgRepos(ctx *context.APIContext) {
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/DeleteOrgReposList"
// "202":
// description: Deletion started
// "403":
// "$ref": "#/responses/forbidden"
// "404":
@ -522,22 +523,29 @@ func DeleteOrgRepos(ctx *context.APIContext) {
ctx.APIErrorInternal(err)
return
}
response := &api.DeleteOrgReposResponse{
Deleted: []string{},
Failed: []api.DeleteRepoFailure{},
}
for _, repo := range repos {
if err := repo_service.DeleteRepository(ctx, ctx.Doer, repo, true); err != nil {
log.Error("Error deleting repo %s: %v", repo.Name, err)
response.Failed = append(response.Failed, api.DeleteRepoFailure{
RepoName: repo.Name,
Message: "Failed to delete repository",
})
} else {
response.Deleted = append(response.Deleted, repo.Name)
doer := ctx.Doer
// Start deletion in background with detached context
go func() {
defer func() {
if r := recover(); r != nil {
log.Error("Panic during org repo deletion: %v", r)
}
}()
// Use HammerContext so deletion continues even if client disconnects
bgCtx := graceful.GetManager().HammerContext()
for _, repo := range repos {
if err := repo_service.DeleteRepository(bgCtx, doer, repo, true); err != nil {
log.Error("Failed to delete repository %s (ID: %d) in org %s: %v", repo.Name, repo.ID, org.Name, err)
} else {
log.Info("Successfully deleted repository %s (ID: %d) in org %s", repo.Name, repo.ID, org.Name)
}
}
}
response.SuccessCount = len(response.Deleted)
response.FailureCount = len(response.Failed)
ctx.JSON(http.StatusOK, response)
log.Info("Completed deletion of %d repositories in org %s", len(repos), org.Name)
}()
ctx.Status(http.StatusAccepted)
}

View File

@ -41,11 +41,3 @@ type swaggerResponseOrganizationPermissions struct {
// in:body
Body api.OrganizationPermissions `json:"body"`
}
// DeleteOrgReposList
// swagger:response DeleteOrgReposList
type swaggerDeleteOrgReposList struct {
// List of successfully deleted repositories and failures
//in:body
Body api.DeleteOrgReposResponse `json:"body"`
}

View File

@ -3658,8 +3658,8 @@
}
],
"responses": {
"200": {
"$ref": "#/responses/DeleteOrgReposList"
"202": {
"description": "Deletion started"
},
"403": {
"$ref": "#/responses/forbidden"
@ -24210,58 +24210,6 @@
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
"DeleteOrgReposResponse": {
"description": "DeleteOrgReposResponse represents the response for deleting organization repositories",
"type": "object",
"properties": {
"deleted": {
"description": "List of repository names that were deleted",
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "Deleted"
},
"failed": {
"description": "Details about repositories that failed to delete",
"type": "array",
"items": {
"$ref": "#/definitions/DeleteRepoFailure"
},
"x-go-name": "Failed"
},
"failure_count": {
"description": "Number of repositories that failed to delete",
"type": "integer",
"format": "int64",
"x-go-name": "FailureCount"
},
"success_count": {
"description": "Number of repositories successfully deleted",
"type": "integer",
"format": "int64",
"x-go-name": "SuccessCount"
}
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
"DeleteRepoFailure": {
"description": "DeleteRepoFailure represents a repository that failed to delete",
"type": "object",
"properties": {
"reason": {
"description": "Message to be displayed",
"type": "string",
"x-go-name": "Message"
},
"repo_name": {
"description": "Repository name",
"type": "string",
"x-go-name": "RepoName"
}
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
"DeployKey": {
"description": "DeployKey a deploy key",
"type": "object",
@ -29810,12 +29758,6 @@
}
}
},
"DeleteOrgReposList": {
"description": "DeleteOrgReposList",
"schema": {
"$ref": "#/definitions/DeleteOrgReposResponse"
}
},
"DeployKey": {
"description": "DeployKey",
"schema": {

View File

@ -275,17 +275,9 @@ func TestAPIDeleteOrgRepos(t *testing.T) {
MakeRequest(t, req, http.StatusCreated)
}
// Delete all repos
// Delete all repos - should return 202 Accepted
req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/orgs/%s/repos", orgName)).AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
var result api.DeleteOrgReposResponse
DecodeJSON(t, resp, &result)
assert.Equal(t, 60, result.SuccessCount)
assert.Equal(t, 0, result.FailureCount)
assert.Len(t, result.Deleted, 60)
assert.Empty(t, result.Failed)
MakeRequest(t, req, http.StatusAccepted)
})
t.Run("Verify response structure", func(t *testing.T) {
@ -308,20 +300,9 @@ func TestAPIDeleteOrgRepos(t *testing.T) {
MakeRequest(t, req, http.StatusCreated)
}
// Delete all repos
// Delete all repos - should return 202 Accepted
req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/orgs/%s/repos", orgName)).AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
var result api.DeleteOrgReposResponse
DecodeJSON(t, resp, &result)
// Verify response structure
assert.Equal(t, 3, result.SuccessCount)
assert.Equal(t, 0, result.FailureCount)
assert.Len(t, result.Deleted, 3)
assert.Empty(t, result.Failed)
assert.NotNil(t, result.Deleted)
assert.NotNil(t, result.Failed)
MakeRequest(t, req, http.StatusAccepted)
})
t.Run("Fail without permissions", func(t *testing.T) {