refactor(api): convert bot accounts through the admin user edit endpoint (#39355)

Follow-up to https://github.com/go-gitea/gitea/pull/38966. Replaces the
unreleased `POST /admin/users/{username}/convert-type` endpoint with a
`type` field on `PATCH /admin/users/{username}`.
This commit is contained in:
silverwind
2026-09-18 17:11:56 +02:00
committed by GitHub
parent 3bec08f998
commit 85eaf5c71c
12 changed files with 57 additions and 203 deletions
+15 -43
View File
@@ -190,6 +190,16 @@ func EditUser(ctx *context.APIContext) {
form := web.GetForm[*api.EditUserOption](ctx)
var userType optional.Option[user_model.UserType]
if form.Type != "" && form.Type != convert.UserTypeToString(ctx.ContextUser.Type) {
newType, err := convert.UserTypeFromString(form.Type)
if err != nil {
ctx.APIErrorAuto(err)
return
}
userType = optional.Some(newType)
}
authOpts := &user_service.UpdateAuthOptions{
LoginSource: optional.FromNonDefault(form.SourceID),
LoginName: optional.FromPtr(form.LoginName),
@@ -197,6 +207,10 @@ func EditUser(ctx *context.APIContext) {
MustChangePassword: optional.FromPtr(form.MustChangePassword),
ProhibitLogin: optional.FromPtr(form.ProhibitLogin),
}
if userType.Value() == user_model.UserTypeBot && (authOpts.Password.Has() || authOpts.LoginSource.Value() != 0 || authOpts.LoginName.Value() != "") {
ctx.APIError(http.StatusBadRequest, "a bot account cannot have a password or authentication source")
return
}
if err := user_service.UpdateAuth(ctx, ctx.ContextUser, authOpts); err != nil {
switch {
case errors.Is(err, password.ErrMinLength):
@@ -231,6 +245,7 @@ func EditUser(ctx *context.APIContext) {
MaxRepoCreation: optional.FromPtr(form.MaxRepoCreation),
AllowCreateOrganization: optional.FromPtr(form.AllowCreateOrganization),
IsRestricted: optional.FromPtr(form.Restricted),
UserType: userType,
}
if err := user_service.UpdateUser(ctx, ctx.ContextUser, opts); err != nil {
@@ -552,46 +567,3 @@ func RenameUser(ctx *context.APIContext) {
}
ctx.Status(http.StatusNoContent)
}
// ConvertUserType converts an account between the user and bot types
func ConvertUserType(ctx *context.APIContext) {
// swagger:operation POST /admin/users/{username}/convert-type admin adminConvertUserType
// ---
// summary: Convert an account between the user and bot types
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: username
// in: path
// description: username of the user to convert
// type: string
// required: true
// - name: body
// in: body
// required: true
// schema:
// "$ref": "#/definitions/ConvertUserTypeOption"
// responses:
// "204":
// "$ref": "#/responses/empty"
// "400":
// "$ref": "#/responses/error"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
targetType, err := convert.UserTypeFromString(web.GetForm[*api.ConvertUserTypeOption](ctx).UserType)
if err != nil {
ctx.APIErrorAuto(err)
return
}
if err := user_service.UpdateUser(ctx, ctx.ContextUser, &user_service.UpdateOptions{UserType: optional.Some(targetType)}); err != nil {
ctx.APIErrorAuto(err)
return
}
ctx.Status(http.StatusNoContent)
}
-1
View File
@@ -1905,7 +1905,6 @@ func Routes() *web.Router {
m.Post("/orgs", bind(api.CreateOrgOption{}), admin.CreateOrg)
m.Post("/repos", bind(api.CreateRepoOption{}), admin.CreateRepo)
m.Post("/rename", bind(api.RenameUserOption{}), admin.RenameUser)
m.Post("/convert-type", bind(api.ConvertUserTypeOption{}), admin.ConvertUserType)
m.Get("/badges", admin.ListUserBadges)
m.Post("/badges", bind(api.UserBadgeOption{}), admin.AddUserBadges)
m.Delete("/badges", bind(api.UserBadgeOption{}), admin.DeleteUserBadges)
-3
View File
@@ -59,9 +59,6 @@ type swaggerParameterBodies struct {
// in:body
RenameUserOption api.RenameUserOption
// in:body
ConvertUserTypeOption api.ConvertUserTypeOption
// in:body
CreateLabelOption api.CreateLabelOption
// in:body
-3
View File
@@ -506,9 +506,6 @@ func EditUserPost(ctx *context.Context) {
case errors.Is(err, user_model.ErrBotCanNotBeAdmin):
ctx.Flash.Error(ctx.Tr("admin.users.convert_type.admin_not_allowed"))
ctx.Redirect(userLink)
case errors.Is(err, user_model.ErrUserTypeCanNotConvert):
ctx.Flash.Error(ctx.Tr("admin.users.convert_type.not_convertible"))
ctx.Redirect(userLink)
case errors.Is(err, util.ErrInvalidArgument):
ctx.Flash.Error(err.Error())
ctx.Redirect(userLink)