Parcourir la source

Standardize HTTP status codes (#7851)

Co-authored-by: Joe Chen <jc@unknwon.io>
Sino il y a 1 semaine
Parent
commit
87c8faaf08

+ 2 - 2
internal/context/context.go

@@ -147,13 +147,13 @@ func (c *Context) RedirectSubpath(location string, status ...int) {
 }
 
 // RenderWithErr used for page has form validation but need to prompt error to users.
-func (c *Context) RenderWithErr(msg, tpl string, f any) {
+func (c *Context) RenderWithErr(msg string, status int, tpl string, f any) {
 	if f != nil {
 		form.Assign(f, c.Data)
 	}
 	c.Flash.ErrorMsg = msg
 	c.Data["Flash"] = c.Flash
-	c.HTML(http.StatusOK, tpl)
+	c.HTML(status, tpl)
 }
 
 // NotFound renders the 404 page.

+ 3 - 3
internal/route/admin/auths.go

@@ -151,7 +151,7 @@ func NewAuthSourcePost(c *context.Context, f form.Authentication) {
 	c.Data["HasTLS"] = hasTLS
 
 	if c.HasError() {
-		c.Success(tmplAdminAuthNew)
+		c.HTML(http.StatusBadRequest, tmplAdminAuthNew)
 		return
 	}
 
@@ -167,7 +167,7 @@ func NewAuthSourcePost(c *context.Context, f form.Authentication) {
 	if err != nil {
 		if database.IsErrLoginSourceAlreadyExist(err) {
 			c.FormErr("Name")
-			c.RenderWithErr(c.Tr("admin.auths.login_source_exist", f.Name), tmplAdminAuthNew, f)
+			c.RenderWithErr(c.Tr("admin.auths.login_source_exist", f.Name), http.StatusUnprocessableEntity, tmplAdminAuthNew, f)
 		} else {
 			c.Error(err, "create login source")
 		}
@@ -223,7 +223,7 @@ func EditAuthSourcePost(c *context.Context, f form.Authentication) {
 	c.Data["HasTLS"] = source.Provider.HasTLS()
 
 	if c.HasError() {
-		c.Success(tmplAdminAuthEdit)
+		c.HTML(http.StatusBadRequest, tmplAdminAuthEdit)
 		return
 	}
 

+ 7 - 6
internal/route/admin/users.go

@@ -1,6 +1,7 @@
 package admin
 
 import (
+	"net/http"
 	"strconv"
 	"strings"
 
@@ -68,7 +69,7 @@ func NewUserPost(c *context.Context, f form.AdminCrateUser) {
 	c.Data["CanSendEmail"] = conf.Email.Enabled
 
 	if c.HasError() {
-		c.Success(tmplAdminUserNew)
+		c.HTML(http.StatusBadRequest, tmplAdminUserNew)
 		return
 	}
 
@@ -89,13 +90,13 @@ func NewUserPost(c *context.Context, f form.AdminCrateUser) {
 		switch {
 		case database.IsErrUserAlreadyExist(err):
 			c.Data["Err_UserName"] = true
-			c.RenderWithErr(c.Tr("form.username_been_taken"), tmplAdminUserNew, &f)
+			c.RenderWithErr(c.Tr("form.username_been_taken"), http.StatusUnprocessableEntity, tmplAdminUserNew, &f)
 		case database.IsErrEmailAlreadyUsed(err):
 			c.Data["Err_Email"] = true
-			c.RenderWithErr(c.Tr("form.email_been_used"), tmplAdminUserNew, &f)
+			c.RenderWithErr(c.Tr("form.email_been_used"), http.StatusUnprocessableEntity, tmplAdminUserNew, &f)
 		case database.IsErrNameNotAllowed(err):
 			c.Data["Err_UserName"] = true
-			c.RenderWithErr(c.Tr("user.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), tmplAdminUserNew, &f)
+			c.RenderWithErr(c.Tr("user.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), http.StatusBadRequest, tmplAdminUserNew, &f)
 		default:
 			c.Error(err, "create user")
 		}
@@ -166,7 +167,7 @@ func EditUserPost(c *context.Context, f form.AdminEditUser) {
 	}
 
 	if c.HasError() {
-		c.Success(tmplAdminUserEdit)
+		c.HTML(http.StatusBadRequest, tmplAdminUserEdit)
 		return
 	}
 
@@ -203,7 +204,7 @@ func EditUserPost(c *context.Context, f form.AdminEditUser) {
 	if err != nil {
 		if database.IsErrEmailAlreadyUsed(err) {
 			c.Data["Err_Email"] = true
-			c.RenderWithErr(c.Tr("form.email_been_used"), tmplAdminUserEdit, &f)
+			c.RenderWithErr(c.Tr("form.email_been_used"), http.StatusUnprocessableEntity, tmplAdminUserEdit, &f)
 		} else {
 			c.Error(err, "update user")
 		}

+ 18 - 18
internal/route/install.go

@@ -1,6 +1,7 @@
 package route
 
 import (
+	"net/http"
 	"net/mail"
 	"os"
 	"os/exec"
@@ -194,13 +195,12 @@ func InstallPost(c *context.Context, f form.Install) {
 			c.HasValue("Err_AdminEmail") {
 			c.FormErr("Admin")
 		}
-
-		c.Success(INSTALL)
+		c.HTML(http.StatusBadRequest, INSTALL)
 		return
 	}
 
 	if _, err := exec.LookPath("git"); err != nil {
-		c.RenderWithErr(c.Tr("install.test_git_failed", err), INSTALL, &f)
+		c.RenderWithErr(c.Tr("install.test_git_failed", err), http.StatusInternalServerError, INSTALL, &f)
 		return
 	}
 
@@ -222,7 +222,7 @@ func InstallPost(c *context.Context, f form.Install) {
 
 	if conf.Database.Type == "sqlite3" && conf.Database.Path == "" {
 		c.FormErr("DbPath")
-		c.RenderWithErr(c.Tr("install.err_empty_db_path"), INSTALL, &f)
+		c.RenderWithErr(c.Tr("install.err_empty_db_path"), http.StatusBadRequest, INSTALL, &f)
 		return
 	}
 
@@ -230,10 +230,10 @@ func InstallPost(c *context.Context, f form.Install) {
 	if err := database.NewTestEngine(); err != nil {
 		if strings.Contains(err.Error(), `Unknown database type: sqlite3`) {
 			c.FormErr("DbType")
-			c.RenderWithErr(c.Tr("install.sqlite3_not_available", "https://gogs.io/docs/installation/install_from_binary.html"), INSTALL, &f)
+			c.RenderWithErr(c.Tr("install.sqlite3_not_available", "https://gogs.io/docs/installation/install_from_binary.html"), http.StatusInternalServerError, INSTALL, &f)
 		} else {
 			c.FormErr("DbSetting")
-			c.RenderWithErr(c.Tr("install.invalid_db_setting", err), INSTALL, &f)
+			c.RenderWithErr(c.Tr("install.invalid_db_setting", err), http.StatusBadRequest, INSTALL, &f)
 		}
 		return
 	}
@@ -242,7 +242,7 @@ func InstallPost(c *context.Context, f form.Install) {
 	f.RepoRootPath = strings.ReplaceAll(f.RepoRootPath, "\\", "/")
 	if err := os.MkdirAll(f.RepoRootPath, os.ModePerm); err != nil {
 		c.FormErr("RepoRootPath")
-		c.RenderWithErr(c.Tr("install.invalid_repo_path", err), INSTALL, &f)
+		c.RenderWithErr(c.Tr("install.invalid_repo_path", err), http.StatusBadRequest, INSTALL, &f)
 		return
 	}
 
@@ -250,21 +250,21 @@ func InstallPost(c *context.Context, f form.Install) {
 	f.LogRootPath = strings.ReplaceAll(f.LogRootPath, "\\", "/")
 	if err := os.MkdirAll(f.LogRootPath, os.ModePerm); err != nil {
 		c.FormErr("LogRootPath")
-		c.RenderWithErr(c.Tr("install.invalid_log_root_path", err), INSTALL, &f)
+		c.RenderWithErr(c.Tr("install.invalid_log_root_path", err), http.StatusBadRequest, INSTALL, &f)
 		return
 	}
 
 	currentUser, match := conf.CheckRunUser(f.RunUser)
 	if !match {
 		c.FormErr("RunUser")
-		c.RenderWithErr(c.Tr("install.run_user_not_match", f.RunUser, currentUser), INSTALL, &f)
+		c.RenderWithErr(c.Tr("install.run_user_not_match", f.RunUser, currentUser), http.StatusForbidden, INSTALL, &f)
 		return
 	}
 
 	// Check host address and port
 	if len(f.SMTPHost) > 0 && !strings.Contains(f.SMTPHost, ":") {
 		c.FormErr("SMTP", "SMTPHost")
-		c.RenderWithErr(c.Tr("install.smtp_host_missing_port"), INSTALL, &f)
+		c.RenderWithErr(c.Tr("install.smtp_host_missing_port"), http.StatusBadRequest, INSTALL, &f)
 		return
 	}
 
@@ -273,7 +273,7 @@ func InstallPost(c *context.Context, f form.Install) {
 		_, err := mail.ParseAddress(f.SMTPFrom)
 		if err != nil {
 			c.FormErr("SMTP", "SMTPFrom")
-			c.RenderWithErr(c.Tr("install.invalid_smtp_from", err), INSTALL, &f)
+			c.RenderWithErr(c.Tr("install.invalid_smtp_from", err), http.StatusBadRequest, INSTALL, &f)
 			return
 		}
 	}
@@ -281,19 +281,19 @@ func InstallPost(c *context.Context, f form.Install) {
 	// Check logic loophole between disable self-registration and no admin account.
 	if f.DisableRegistration && f.AdminName == "" {
 		c.FormErr("Services", "Admin")
-		c.RenderWithErr(c.Tr("install.no_admin_and_disable_registration"), INSTALL, f)
+		c.RenderWithErr(c.Tr("install.no_admin_and_disable_registration"), http.StatusUnprocessableEntity, INSTALL, f)
 		return
 	}
 
 	// Check admin password.
 	if len(f.AdminName) > 0 && f.AdminPasswd == "" {
 		c.FormErr("Admin", "AdminPasswd")
-		c.RenderWithErr(c.Tr("install.err_empty_admin_password"), INSTALL, f)
+		c.RenderWithErr(c.Tr("install.err_empty_admin_password"), http.StatusBadRequest, INSTALL, f)
 		return
 	}
 	if f.AdminPasswd != f.AdminConfirmPasswd {
 		c.FormErr("Admin", "AdminPasswd")
-		c.RenderWithErr(c.Tr("form.password_not_match"), INSTALL, f)
+		c.RenderWithErr(c.Tr("form.password_not_match"), http.StatusBadRequest, INSTALL, f)
 		return
 	}
 
@@ -367,21 +367,21 @@ func InstallPost(c *context.Context, f form.Install) {
 	cfg.Section("security").Key("INSTALL_LOCK").SetValue("true")
 	secretKey, err := strutil.RandomChars(15)
 	if err != nil {
-		c.RenderWithErr(c.Tr("install.secret_key_failed", err), INSTALL, &f)
+		c.RenderWithErr(c.Tr("install.secret_key_failed", err), http.StatusInternalServerError, INSTALL, &f)
 		return
 	}
 	cfg.Section("security").Key("SECRET_KEY").SetValue(secretKey)
 
 	_ = os.MkdirAll(filepath.Dir(conf.CustomConf), os.ModePerm)
 	if err := cfg.SaveTo(conf.CustomConf); err != nil {
-		c.RenderWithErr(c.Tr("install.save_config_failed", err), INSTALL, &f)
+		c.RenderWithErr(c.Tr("install.save_config_failed", err), http.StatusInternalServerError, INSTALL, &f)
 		return
 	}
 
 	// NOTE: We reuse the current value because this handler does not have access to CLI flags.
 	err = GlobalInit(conf.CustomConf)
 	if err != nil {
-		c.RenderWithErr(c.Tr("install.init_failed", err), INSTALL, &f)
+		c.RenderWithErr(c.Tr("install.init_failed", err), http.StatusInternalServerError, INSTALL, &f)
 		return
 	}
 
@@ -401,7 +401,7 @@ func InstallPost(c *context.Context, f form.Install) {
 			if !database.IsErrUserAlreadyExist(err) {
 				conf.Security.InstallLock = false
 				c.FormErr("AdminName", "AdminEmail")
-				c.RenderWithErr(c.Tr("install.invalid_admin_setting", err), INSTALL, &f)
+				c.RenderWithErr(c.Tr("install.invalid_admin_setting", err), http.StatusBadRequest, INSTALL, &f)
 				return
 			}
 

+ 5 - 3
internal/route/org/org.go

@@ -1,6 +1,8 @@
 package org
 
 import (
+	"net/http"
+
 	log "unknwon.dev/clog/v2"
 
 	"gogs.io/gogs/internal/context"
@@ -21,7 +23,7 @@ func CreatePost(c *context.Context, f form.CreateOrg) {
 	c.Title("new_org")
 
 	if c.HasError() {
-		c.Success(CREATE)
+		c.HTML(http.StatusBadRequest, CREATE)
 		return
 	}
 
@@ -35,9 +37,9 @@ func CreatePost(c *context.Context, f form.CreateOrg) {
 		c.Data["Err_OrgName"] = true
 		switch {
 		case database.IsErrUserAlreadyExist(err):
-			c.RenderWithErr(c.Tr("form.org_name_been_taken"), CREATE, &f)
+			c.RenderWithErr(c.Tr("form.org_name_been_taken"), http.StatusUnprocessableEntity, CREATE, &f)
 		case database.IsErrNameNotAllowed(err):
-			c.RenderWithErr(c.Tr("org.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), CREATE, &f)
+			c.RenderWithErr(c.Tr("org.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), http.StatusBadRequest, CREATE, &f)
 		default:
 			c.Error(err, "create organization")
 		}

+ 6 - 8
internal/route/org/setting.go

@@ -1,6 +1,8 @@
 package org
 
 import (
+	"net/http"
+
 	log "unknwon.dev/clog/v2"
 
 	"gogs.io/gogs/internal/auth"
@@ -27,7 +29,7 @@ func SettingsPost(c *context.Context, f form.UpdateOrgSetting) {
 	c.Data["PageIsSettingsOptions"] = true
 
 	if c.HasError() {
-		c.Success(tmplOrgSettingsOptions)
+		c.HTML(http.StatusBadRequest, tmplOrgSettingsOptions)
 		return
 	}
 
@@ -38,18 +40,14 @@ func SettingsPost(c *context.Context, f form.UpdateOrgSetting) {
 		err := database.Handle.Users().ChangeUsername(c.Req.Context(), c.Org.Organization.ID, f.Name)
 		if err != nil {
 			c.Data["OrgName"] = true
-			var msg string
 			switch {
 			case database.IsErrUserAlreadyExist(err):
-				msg = c.Tr("form.username_been_taken")
+				c.RenderWithErr(c.Tr("form.username_been_taken"), http.StatusUnprocessableEntity, tmplOrgSettingsOptions, &f)
 			case database.IsErrNameNotAllowed(err):
-				msg = c.Tr("user.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value())
+				c.RenderWithErr(c.Tr("user.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), http.StatusBadRequest, tmplOrgSettingsOptions, &f)
 			default:
 				c.Error(err, "change organization name")
-				return
 			}
-
-			c.RenderWithErr(msg, tmplOrgSettingsOptions, &f)
 			return
 		}
 
@@ -104,7 +102,7 @@ func SettingsDelete(c *context.Context) {
 	if c.Req.Method == "POST" {
 		if _, err := database.Handle.Users().Authenticate(c.Req.Context(), c.User.Name, c.Query("password"), c.User.LoginSource); err != nil {
 			if auth.IsErrBadCredentials(err) {
-				c.RenderWithErr(c.Tr("form.enterred_invalid_password"), tmplOrgSettingsDelete, nil)
+				c.RenderWithErr(c.Tr("form.enterred_invalid_password"), http.StatusUnauthorized, tmplOrgSettingsDelete, nil)
 			} else {
 				c.Error(err, "authenticate user")
 			}

+ 5 - 5
internal/route/org/teams.go

@@ -159,7 +159,7 @@ func NewTeamPost(c *context.Context, f form.CreateTeam) {
 	c.Data["Team"] = t
 
 	if c.HasError() {
-		c.Success(tmplOrgTeamNew)
+		c.HTML(http.StatusBadRequest, tmplOrgTeamNew)
 		return
 	}
 
@@ -167,9 +167,9 @@ func NewTeamPost(c *context.Context, f form.CreateTeam) {
 		c.Data["Err_TeamName"] = true
 		switch {
 		case database.IsErrTeamAlreadyExist(err):
-			c.RenderWithErr(c.Tr("form.team_name_been_taken"), tmplOrgTeamNew, &f)
+			c.RenderWithErr(c.Tr("form.team_name_been_taken"), http.StatusUnprocessableEntity, tmplOrgTeamNew, &f)
 		case database.IsErrNameNotAllowed(err):
-			c.RenderWithErr(c.Tr("org.form.team_name_not_allowed", err.(database.ErrNameNotAllowed).Value()), tmplOrgTeamNew, &f)
+			c.RenderWithErr(c.Tr("org.form.team_name_not_allowed", err.(database.ErrNameNotAllowed).Value()), http.StatusBadRequest, tmplOrgTeamNew, &f)
 		default:
 			c.Error(err, "new team")
 		}
@@ -214,7 +214,7 @@ func EditTeamPost(c *context.Context, f form.CreateTeam) {
 	c.Data["Team"] = t
 
 	if c.HasError() {
-		c.Success(tmplOrgTeamNew)
+		c.HTML(http.StatusBadRequest, tmplOrgTeamNew)
 		return
 	}
 
@@ -245,7 +245,7 @@ func EditTeamPost(c *context.Context, f form.CreateTeam) {
 		c.Data["Err_TeamName"] = true
 		switch {
 		case database.IsErrTeamAlreadyExist(err):
-			c.RenderWithErr(c.Tr("form.team_name_been_taken"), tmplOrgTeamNew, &f)
+			c.RenderWithErr(c.Tr("form.team_name_been_taken"), http.StatusUnprocessableEntity, tmplOrgTeamNew, &f)
 		default:
 			c.Error(err, "update team")
 		}

+ 18 - 18
internal/route/repo/editor.go

@@ -153,20 +153,20 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
 	c.Data["PreviewableFileModes"] = strings.Join(conf.Repository.Editor.PreviewableFileModes, ",")
 
 	if c.HasError() {
-		c.Success(tmplEditorEdit)
+		c.HTML(http.StatusBadRequest, tmplEditorEdit)
 		return
 	}
 
 	if f.TreePath == "" {
 		c.FormErr("TreePath")
-		c.RenderWithErr(c.Tr("repo.editor.filename_cannot_be_empty"), tmplEditorEdit, &f)
+		c.RenderWithErr(c.Tr("repo.editor.filename_cannot_be_empty"), http.StatusBadRequest, tmplEditorEdit, &f)
 		return
 	}
 
 	if oldBranchName != branchName {
 		if _, err := c.Repo.Repository.GetBranch(branchName); err == nil {
 			c.FormErr("NewBranchName")
-			c.RenderWithErr(c.Tr("repo.editor.branch_already_exists", branchName), tmplEditorEdit, &f)
+			c.RenderWithErr(c.Tr("repo.editor.branch_already_exists", branchName), http.StatusUnprocessableEntity, tmplEditorEdit, &f)
 			return
 		}
 	}
@@ -187,18 +187,18 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
 		if index != len(treeNames)-1 {
 			if !entry.IsTree() {
 				c.FormErr("TreePath")
-				c.RenderWithErr(c.Tr("repo.editor.directory_is_a_file", part), tmplEditorEdit, &f)
+				c.RenderWithErr(c.Tr("repo.editor.directory_is_a_file", part), http.StatusUnprocessableEntity, tmplEditorEdit, &f)
 				return
 			}
 		} else {
 			// 🚨 SECURITY: Do not allow editing if the target file is a symlink.
 			if entry.IsSymlink() {
 				c.FormErr("TreePath")
-				c.RenderWithErr(c.Tr("repo.editor.file_is_a_symlink", part), tmplEditorEdit, &f)
+				c.RenderWithErr(c.Tr("repo.editor.file_is_a_symlink", part), http.StatusUnprocessableEntity, tmplEditorEdit, &f)
 				return
 			} else if entry.IsTree() {
 				c.FormErr("TreePath")
-				c.RenderWithErr(c.Tr("repo.editor.filename_is_a_directory", part), tmplEditorEdit, &f)
+				c.RenderWithErr(c.Tr("repo.editor.filename_is_a_directory", part), http.StatusUnprocessableEntity, tmplEditorEdit, &f)
 				return
 			}
 		}
@@ -209,7 +209,7 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
 		if err != nil {
 			if gitutil.IsErrRevisionNotExist(err) {
 				c.FormErr("TreePath")
-				c.RenderWithErr(c.Tr("repo.editor.file_editing_no_longer_exists", oldTreePath), tmplEditorEdit, &f)
+				c.RenderWithErr(c.Tr("repo.editor.file_editing_no_longer_exists", oldTreePath), http.StatusNotFound, tmplEditorEdit, &f)
 			} else {
 				c.Error(err, "get tree entry")
 			}
@@ -219,7 +219,7 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
 		// 🚨 SECURITY: Do not allow editing if the old file is a symlink.
 		if entry.IsSymlink() {
 			c.FormErr("TreePath")
-			c.RenderWithErr(c.Tr("repo.editor.file_is_a_symlink", oldTreePath), tmplEditorEdit, &f)
+			c.RenderWithErr(c.Tr("repo.editor.file_is_a_symlink", oldTreePath), http.StatusUnprocessableEntity, tmplEditorEdit, &f)
 			return
 		}
 
@@ -232,7 +232,7 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
 
 			for _, file := range files {
 				if file == f.TreePath {
-					c.RenderWithErr(c.Tr("repo.editor.file_changed_while_editing", c.Repo.RepoLink+"/compare/"+lastCommit+"..."+c.Repo.CommitID), tmplEditorEdit, &f)
+					c.RenderWithErr(c.Tr("repo.editor.file_changed_while_editing", c.Repo.RepoLink+"/compare/"+lastCommit+"..."+c.Repo.CommitID), http.StatusConflict, tmplEditorEdit, &f)
 					return
 				}
 			}
@@ -250,7 +250,7 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
 		}
 		if entry != nil {
 			c.FormErr("TreePath")
-			c.RenderWithErr(c.Tr("repo.editor.file_already_exists", f.TreePath), tmplEditorEdit, &f)
+			c.RenderWithErr(c.Tr("repo.editor.file_already_exists", f.TreePath), http.StatusUnprocessableEntity, tmplEditorEdit, &f)
 			return
 		}
 	}
@@ -280,7 +280,7 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
 	}); err != nil {
 		log.Error("Failed to update repo file: %v", err)
 		c.FormErr("TreePath")
-		c.RenderWithErr(c.Tr("repo.editor.fail_to_update_file", f.TreePath, errInternalServerError), tmplEditorEdit, &f)
+		c.RenderWithErr(c.Tr("repo.editor.fail_to_update_file", f.TreePath, errInternalServerError), http.StatusInternalServerError, tmplEditorEdit, &f)
 		return
 	}
 
@@ -358,14 +358,14 @@ func DeleteFilePost(c *context.Context, f form.DeleteRepoFile) {
 	c.Data["new_branch_name"] = branchName
 
 	if c.HasError() {
-		c.Success(tmplEditorDelete)
+		c.HTML(http.StatusBadRequest, tmplEditorDelete)
 		return
 	}
 
 	if oldBranchName != branchName {
 		if _, err := c.Repo.Repository.GetBranch(branchName); err == nil {
 			c.FormErr("NewBranchName")
-			c.RenderWithErr(c.Tr("repo.editor.branch_already_exists", branchName), tmplEditorDelete, &f)
+			c.RenderWithErr(c.Tr("repo.editor.branch_already_exists", branchName), http.StatusUnprocessableEntity, tmplEditorDelete, &f)
 			return
 		}
 	}
@@ -388,7 +388,7 @@ func DeleteFilePost(c *context.Context, f form.DeleteRepoFile) {
 		Message:      message,
 	}); err != nil {
 		log.Error("Failed to delete repo file: %v", err)
-		c.RenderWithErr(c.Tr("repo.editor.fail_to_delete_file", c.Repo.TreePath, errInternalServerError), tmplEditorDelete, &f)
+		c.RenderWithErr(c.Tr("repo.editor.fail_to_delete_file", c.Repo.TreePath, errInternalServerError), http.StatusInternalServerError, tmplEditorDelete, &f)
 		return
 	}
 
@@ -456,14 +456,14 @@ func UploadFilePost(c *context.Context, f form.UploadRepoFile) {
 	c.Data["new_branch_name"] = branchName
 
 	if c.HasError() {
-		c.Success(tmplEditorUpload)
+		c.HTML(http.StatusBadRequest, tmplEditorUpload)
 		return
 	}
 
 	if oldBranchName != branchName {
 		if _, err := c.Repo.Repository.GetBranch(branchName); err == nil {
 			c.FormErr("NewBranchName")
-			c.RenderWithErr(c.Tr("repo.editor.branch_already_exists", branchName), tmplEditorUpload, &f)
+			c.RenderWithErr(c.Tr("repo.editor.branch_already_exists", branchName), http.StatusUnprocessableEntity, tmplEditorUpload, &f)
 			return
 		}
 	}
@@ -485,7 +485,7 @@ func UploadFilePost(c *context.Context, f form.UploadRepoFile) {
 		// User can only upload files to a directory.
 		if !entry.IsTree() {
 			c.FormErr("TreePath")
-			c.RenderWithErr(c.Tr("repo.editor.directory_is_a_file", part), tmplEditorUpload, &f)
+			c.RenderWithErr(c.Tr("repo.editor.directory_is_a_file", part), http.StatusUnprocessableEntity, tmplEditorUpload, &f)
 			return
 		}
 	}
@@ -510,7 +510,7 @@ func UploadFilePost(c *context.Context, f form.UploadRepoFile) {
 	}); err != nil {
 		log.Error("Failed to upload files: %v", err)
 		c.FormErr("TreePath")
-		c.RenderWithErr(c.Tr("repo.editor.unable_to_upload_files", f.TreePath, errInternalServerError), tmplEditorUpload, &f)
+		c.RenderWithErr(c.Tr("repo.editor.unable_to_upload_files", f.TreePath, errInternalServerError), http.StatusInternalServerError, tmplEditorUpload, &f)
 		return
 	}
 

+ 5 - 5
internal/route/repo/issue.go

@@ -409,7 +409,7 @@ func NewIssuePost(c *context.Context, f form.NewIssue) {
 	}
 
 	if c.HasError() {
-		c.Success(tmplRepoIssueNew)
+		c.HTML(http.StatusBadRequest, tmplRepoIssueNew)
 		return
 	}
 
@@ -1148,7 +1148,7 @@ func NewMilestonePost(c *context.Context, f form.CreateMilestone) {
 	c.Data["DateLang"] = conf.I18n.DateLang(c.Language())
 
 	if c.HasError() {
-		c.Success(tmplRepoIssueMilestoneNew)
+		c.HTML(http.StatusBadRequest, tmplRepoIssueMilestoneNew)
 		return
 	}
 
@@ -1158,7 +1158,7 @@ func NewMilestonePost(c *context.Context, f form.CreateMilestone) {
 	deadline, err := time.ParseInLocation("2006-01-02", f.Deadline, time.Local)
 	if err != nil {
 		c.Data["Err_Deadline"] = true
-		c.RenderWithErr(c.Tr("repo.milestones.invalid_due_date_format"), tmplRepoIssueMilestoneNew, &f)
+		c.RenderWithErr(c.Tr("repo.milestones.invalid_due_date_format"), http.StatusBadRequest, tmplRepoIssueMilestoneNew, &f)
 		return
 	}
 
@@ -1204,7 +1204,7 @@ func EditMilestonePost(c *context.Context, f form.CreateMilestone) {
 	c.Data["DateLang"] = conf.I18n.DateLang(c.Language())
 
 	if c.HasError() {
-		c.Success(tmplRepoIssueMilestoneNew)
+		c.HTML(http.StatusBadRequest, tmplRepoIssueMilestoneNew)
 		return
 	}
 
@@ -1214,7 +1214,7 @@ func EditMilestonePost(c *context.Context, f form.CreateMilestone) {
 	deadline, err := time.ParseInLocation("2006-01-02", f.Deadline, time.Local)
 	if err != nil {
 		c.Data["Err_Deadline"] = true
-		c.RenderWithErr(c.Tr("repo.milestones.invalid_due_date_format"), tmplRepoIssueMilestoneNew, &f)
+		c.RenderWithErr(c.Tr("repo.milestones.invalid_due_date_format"), http.StatusBadRequest, tmplRepoIssueMilestoneNew, &f)
 		return
 	}
 

+ 6 - 6
internal/route/repo/pull.go

@@ -108,7 +108,7 @@ func ForkPost(c *context.Context, f form.CreateRepo) {
 	c.Data["ContextUser"] = ctxUser
 
 	if c.HasError() {
-		c.Success(tmplRepoPullsFork)
+		c.HTML(http.StatusBadRequest, tmplRepoPullsFork)
 		return
 	}
 
@@ -129,7 +129,7 @@ func ForkPost(c *context.Context, f form.CreateRepo) {
 
 	// Cannot fork to same owner
 	if ctxUser.ID == baseRepo.OwnerID {
-		c.RenderWithErr(c.Tr("repo.settings.cannot_fork_to_same_owner"), tmplRepoPullsFork, &f)
+		c.RenderWithErr(c.Tr("repo.settings.cannot_fork_to_same_owner"), http.StatusUnprocessableEntity, tmplRepoPullsFork, &f)
 		return
 	}
 
@@ -138,11 +138,11 @@ func ForkPost(c *context.Context, f form.CreateRepo) {
 		c.Data["Err_RepoName"] = true
 		switch {
 		case database.IsErrReachLimitOfRepo(err):
-			c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", err.(database.ErrReachLimitOfRepo).Limit), tmplRepoPullsFork, &f)
+			c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", err.(database.ErrReachLimitOfRepo).Limit), http.StatusForbidden, tmplRepoPullsFork, &f)
 		case database.IsErrRepoAlreadyExist(err):
-			c.RenderWithErr(c.Tr("repo.settings.new_owner_has_same_repo"), tmplRepoPullsFork, &f)
+			c.RenderWithErr(c.Tr("repo.settings.new_owner_has_same_repo"), http.StatusUnprocessableEntity, tmplRepoPullsFork, &f)
 		case database.IsErrNameNotAllowed(err):
-			c.RenderWithErr(c.Tr("repo.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), tmplRepoPullsFork, &f)
+			c.RenderWithErr(c.Tr("repo.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), http.StatusBadRequest, tmplRepoPullsFork, &f)
 		default:
 			c.Error(err, "fork repository")
 		}
@@ -709,7 +709,7 @@ func CompareAndPullRequestPost(c *context.Context, f form.NewIssue) {
 			return
 		}
 
-		c.Success(tmplRepoPullsCompare)
+		c.HTML(http.StatusBadRequest, tmplRepoPullsCompare)
 		return
 	}
 

+ 6 - 5
internal/route/repo/release.go

@@ -1,6 +1,7 @@
 package repo
 
 import (
+	"net/http"
 	"strings"
 
 	"github.com/cockroachdb/errors"
@@ -169,12 +170,12 @@ func NewReleasePost(c *context.Context, f form.NewRelease) {
 	renderReleaseAttachmentSettings(c)
 
 	if c.HasError() {
-		c.Success(tmplRepoReleaseNew)
+		c.HTML(http.StatusBadRequest, tmplRepoReleaseNew)
 		return
 	}
 
 	if !c.Repo.GitRepo.HasBranch(f.Target) {
-		c.RenderWithErr(c.Tr("form.target_branch_not_exist"), tmplRepoReleaseNew, &f)
+		c.RenderWithErr(c.Tr("form.target_branch_not_exist"), http.StatusUnprocessableEntity, tmplRepoReleaseNew, &f)
 		return
 	}
 
@@ -222,9 +223,9 @@ func NewReleasePost(c *context.Context, f form.NewRelease) {
 		c.Data["Err_TagName"] = true
 		switch {
 		case database.IsErrReleaseAlreadyExist(err):
-			c.RenderWithErr(c.Tr("repo.release.tag_name_already_exist"), tmplRepoReleaseNew, &f)
+			c.RenderWithErr(c.Tr("repo.release.tag_name_already_exist"), http.StatusUnprocessableEntity, tmplRepoReleaseNew, &f)
 		case database.IsErrInvalidTagName(err):
-			c.RenderWithErr(c.Tr("repo.release.tag_name_invalid"), tmplRepoReleaseNew, &f)
+			c.RenderWithErr(c.Tr("repo.release.tag_name_invalid"), http.StatusBadRequest, tmplRepoReleaseNew, &f)
 		default:
 			c.Error(err, "new release")
 		}
@@ -280,7 +281,7 @@ func EditReleasePost(c *context.Context, f form.EditRelease) {
 	c.Data["IsDraft"] = rel.IsDraft
 
 	if c.HasError() {
-		c.Success(tmplRepoReleaseNew)
+		c.HTML(http.StatusBadRequest, tmplRepoReleaseNew)
 		return
 	}
 

+ 11 - 11
internal/route/repo/repo.go

@@ -86,13 +86,13 @@ func Create(c *context.Context) {
 func handleCreateError(c *context.Context, err error, name, tpl string, form any) {
 	switch {
 	case database.IsErrReachLimitOfRepo(err):
-		c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", err.(database.ErrReachLimitOfRepo).Limit), tpl, form)
+		c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", err.(database.ErrReachLimitOfRepo).Limit), http.StatusForbidden, tpl, form)
 	case database.IsErrRepoAlreadyExist(err):
 		c.Data["Err_RepoName"] = true
-		c.RenderWithErr(c.Tr("form.repo_name_been_taken"), tpl, form)
+		c.RenderWithErr(c.Tr("form.repo_name_been_taken"), http.StatusUnprocessableEntity, tpl, form)
 	case database.IsErrNameNotAllowed(err):
 		c.Data["Err_RepoName"] = true
-		c.RenderWithErr(c.Tr("repo.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), tpl, form)
+		c.RenderWithErr(c.Tr("repo.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), http.StatusBadRequest, tpl, form)
 	default:
 		c.Error(err, name)
 	}
@@ -112,7 +112,7 @@ func CreatePost(c *context.Context, f form.CreateRepo) {
 	c.Data["ContextUser"] = ctxUser
 
 	if c.HasError() {
-		c.Success(CREATE)
+		c.HTML(http.StatusBadRequest, CREATE)
 		return
 	}
 
@@ -166,7 +166,7 @@ func MigratePost(c *context.Context, f form.MigrateRepo) {
 	c.Data["ContextUser"] = ctxUser
 
 	if c.HasError() {
-		c.Success(MIGRATE)
+		c.HTML(http.StatusBadRequest, MIGRATE)
 		return
 	}
 
@@ -177,13 +177,13 @@ func MigratePost(c *context.Context, f form.MigrateRepo) {
 			addrErr := err.(database.ErrInvalidCloneAddr)
 			switch {
 			case addrErr.IsURLError:
-				c.RenderWithErr(c.Tr("repo.migrate.clone_address")+c.Tr("form.url_error"), MIGRATE, &f)
+				c.RenderWithErr(c.Tr("repo.migrate.clone_address")+c.Tr("form.url_error"), http.StatusBadRequest, MIGRATE, &f)
 			case addrErr.IsPermissionDenied:
-				c.RenderWithErr(c.Tr("repo.migrate.permission_denied"), MIGRATE, &f)
+				c.RenderWithErr(c.Tr("repo.migrate.permission_denied"), http.StatusForbidden, MIGRATE, &f)
 			case addrErr.IsInvalidPath:
-				c.RenderWithErr(c.Tr("repo.migrate.invalid_local_path"), MIGRATE, &f)
+				c.RenderWithErr(c.Tr("repo.migrate.invalid_local_path"), http.StatusBadRequest, MIGRATE, &f)
 			case addrErr.IsBlockedLocalAddress:
-				c.RenderWithErr(c.Tr("repo.migrate.clone_address_resolved_to_blocked_local_address"), MIGRATE, &f)
+				c.RenderWithErr(c.Tr("repo.migrate.clone_address_resolved_to_blocked_local_address"), http.StatusForbidden, MIGRATE, &f)
 			default:
 				c.Error(err, "unexpected error")
 			}
@@ -216,11 +216,11 @@ func MigratePost(c *context.Context, f form.MigrateRepo) {
 	if strings.Contains(err.Error(), "Authentication failed") ||
 		strings.Contains(err.Error(), "could not read Username") {
 		c.Data["Err_Auth"] = true
-		c.RenderWithErr(c.Tr("form.auth_failed", database.HandleMirrorCredentials(err.Error(), true)), MIGRATE, &f)
+		c.RenderWithErr(c.Tr("form.auth_failed", database.HandleMirrorCredentials(err.Error(), true)), http.StatusUnauthorized, MIGRATE, &f)
 		return
 	} else if strings.Contains(err.Error(), "fatal:") {
 		c.Data["Err_CloneAddr"] = true
-		c.RenderWithErr(c.Tr("repo.migrate.failed", database.HandleMirrorCredentials(err.Error(), true)), MIGRATE, &f)
+		c.RenderWithErr(c.Tr("repo.migrate.failed", database.HandleMirrorCredentials(err.Error(), true)), http.StatusInternalServerError, MIGRATE, &f)
 		return
 	}
 

+ 14 - 13
internal/route/repo/setting.go

@@ -3,6 +3,7 @@ package repo
 import (
 	"fmt"
 	"io"
+	"net/http"
 	"strings"
 	"time"
 
@@ -49,7 +50,7 @@ func SettingsPost(c *context.Context, f form.RepoSetting) {
 	switch c.Query("action") {
 	case "update":
 		if c.HasError() {
-			c.Success(tmplRepoSettingsOptions)
+			c.HTML(http.StatusBadRequest, tmplRepoSettingsOptions)
 			return
 		}
 
@@ -63,9 +64,9 @@ func SettingsPost(c *context.Context, f form.RepoSetting) {
 				c.FormErr("RepoName")
 				switch {
 				case database.IsErrRepoAlreadyExist(err):
-					c.RenderWithErr(c.Tr("form.repo_name_been_taken"), tmplRepoSettingsOptions, &f)
+					c.RenderWithErr(c.Tr("form.repo_name_been_taken"), http.StatusUnprocessableEntity, tmplRepoSettingsOptions, &f)
 				case database.IsErrNameNotAllowed(err):
-					c.RenderWithErr(c.Tr("repo.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), tmplRepoSettingsOptions, &f)
+					c.RenderWithErr(c.Tr("repo.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), http.StatusBadRequest, tmplRepoSettingsOptions, &f)
 				default:
 					c.Error(err, "change repository name")
 				}
@@ -175,7 +176,7 @@ func SettingsPost(c *context.Context, f form.RepoSetting) {
 			return
 		}
 		if repo.Name != f.RepoName {
-			c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), tmplRepoSettingsOptions, nil)
+			c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), http.StatusBadRequest, tmplRepoSettingsOptions, nil)
 			return
 		}
 
@@ -209,7 +210,7 @@ func SettingsPost(c *context.Context, f form.RepoSetting) {
 			return
 		}
 		if repo.Name != f.RepoName {
-			c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), tmplRepoSettingsOptions, nil)
+			c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), http.StatusBadRequest, tmplRepoSettingsOptions, nil)
 			return
 		}
 
@@ -222,13 +223,13 @@ func SettingsPost(c *context.Context, f form.RepoSetting) {
 
 		newOwner := c.Query("new_owner_name")
 		if !database.Handle.Users().IsUsernameUsed(c.Req.Context(), newOwner, c.Repo.Owner.ID) {
-			c.RenderWithErr(c.Tr("form.enterred_invalid_owner_name"), tmplRepoSettingsOptions, nil)
+			c.RenderWithErr(c.Tr("form.enterred_invalid_owner_name"), http.StatusBadRequest, tmplRepoSettingsOptions, nil)
 			return
 		}
 
 		if err := database.TransferOwnership(c.User, newOwner, repo); err != nil {
 			if database.IsErrRepoAlreadyExist(err) {
-				c.RenderWithErr(c.Tr("repo.settings.new_owner_has_same_repo"), tmplRepoSettingsOptions, nil)
+				c.RenderWithErr(c.Tr("repo.settings.new_owner_has_same_repo"), http.StatusUnprocessableEntity, tmplRepoSettingsOptions, nil)
 			} else {
 				c.Error(err, "transfer ownership")
 			}
@@ -244,7 +245,7 @@ func SettingsPost(c *context.Context, f form.RepoSetting) {
 			return
 		}
 		if repo.Name != f.RepoName {
-			c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), tmplRepoSettingsOptions, nil)
+			c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), http.StatusBadRequest, tmplRepoSettingsOptions, nil)
 			return
 		}
 
@@ -270,7 +271,7 @@ func SettingsPost(c *context.Context, f form.RepoSetting) {
 			return
 		}
 		if repo.Name != f.RepoName {
-			c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), tmplRepoSettingsOptions, nil)
+			c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), http.StatusBadRequest, tmplRepoSettingsOptions, nil)
 			return
 		}
 
@@ -436,7 +437,7 @@ func SettingsBranches(c *context.Context) {
 
 	if c.Repo.Repository.IsBare {
 		c.Flash.Info(c.Tr("repo.settings.branches_bare"), true)
-		c.Success(tmplRepoSettingsBranches)
+		c.HTML(http.StatusUnprocessableEntity, tmplRepoSettingsBranches)
 		return
 	}
 
@@ -652,7 +653,7 @@ func SettingsDeployKeysPost(c *context.Context, f form.AddSSHKey) {
 	c.Data["Deploykeys"] = keys
 
 	if c.HasError() {
-		c.Success(tmplRepoSettingsDeployKeys)
+		c.HTML(http.StatusBadRequest, tmplRepoSettingsDeployKeys)
 		return
 	}
 
@@ -675,10 +676,10 @@ func SettingsDeployKeysPost(c *context.Context, f form.AddSSHKey) {
 		switch {
 		case database.IsErrKeyAlreadyExist(err):
 			c.Data["Err_Content"] = true
-			c.RenderWithErr(c.Tr("repo.settings.key_been_used"), tmplRepoSettingsDeployKeys, &f)
+			c.RenderWithErr(c.Tr("repo.settings.key_been_used"), http.StatusUnprocessableEntity, tmplRepoSettingsDeployKeys, &f)
 		case database.IsErrKeyNameAlreadyUsed(err):
 			c.Data["Err_Title"] = true
-			c.RenderWithErr(c.Tr("repo.settings.key_name_used"), tmplRepoSettingsDeployKeys, &f)
+			c.RenderWithErr(c.Tr("repo.settings.key_name_used"), http.StatusUnprocessableEntity, tmplRepoSettingsDeployKeys, &f)
 		default:
 			c.Error(err, "add deploy key")
 		}

+ 12 - 12
internal/route/repo/webhook.go

@@ -116,32 +116,32 @@ func WebhooksNew(c *context.Context, orCtx *orgRepoContext) {
 	c.Success(orCtx.TmplNew)
 }
 
-func validateWebhook(l macaron.Locale, w *database.Webhook) (field, msg string, ok bool) {
+func validateWebhook(l macaron.Locale, w *database.Webhook) (field, msg string, status int) {
 	// 🚨 SECURITY: Local addresses must not be allowed by non-admins to prevent SSRF,
 	// see https://github.com/gogs/gogs/issues/5366 for details.
 	payloadURL, err := url.Parse(w.URL)
 	if err != nil {
-		return "PayloadURL", l.Tr("repo.settings.webhook.err_cannot_parse_payload_url", err), false
+		return "PayloadURL", l.Tr("repo.settings.webhook.err_cannot_parse_payload_url", err), http.StatusBadRequest
 	}
 
 	if netutil.IsBlockedLocalHostname(payloadURL.Hostname(), conf.Security.LocalNetworkAllowlist) {
-		return "PayloadURL", l.Tr("repo.settings.webhook.url_resolved_to_blocked_local_address"), false
+		return "PayloadURL", l.Tr("repo.settings.webhook.url_resolved_to_blocked_local_address"), http.StatusForbidden
 	}
-	return "", "", true
+	return "", "", http.StatusOK
 }
 
 func validateAndCreateWebhook(c *context.Context, orCtx *orgRepoContext, w *database.Webhook) {
 	c.Data["Webhook"] = w
 
 	if c.HasError() {
-		c.Success(orCtx.TmplNew)
+		c.HTML(http.StatusBadRequest, orCtx.TmplNew)
 		return
 	}
 
-	field, msg, ok := validateWebhook(c.Locale, w)
-	if !ok {
+	field, msg, status := validateWebhook(c.Locale, w)
+	if status != http.StatusOK {
 		c.FormErr(field)
-		c.RenderWithErr(msg, orCtx.TmplNew, nil)
+		c.RenderWithErr(msg, status, orCtx.TmplNew, nil)
 		return
 	}
 
@@ -338,14 +338,14 @@ func validateAndUpdateWebhook(c *context.Context, orCtx *orgRepoContext, w *data
 	c.Data["Webhook"] = w
 
 	if c.HasError() {
-		c.Success(orCtx.TmplNew)
+		c.HTML(http.StatusBadRequest, orCtx.TmplNew)
 		return
 	}
 
-	field, msg, ok := validateWebhook(c.Locale, w)
-	if !ok {
+	field, msg, status := validateWebhook(c.Locale, w)
+	if status != http.StatusOK {
 		c.FormErr(field)
-		c.RenderWithErr(msg, orCtx.TmplNew, nil)
+		c.RenderWithErr(msg, status, orCtx.TmplNew, nil)
 		return
 	}
 

+ 20 - 19
internal/route/repo/webhook_test.go

@@ -1,6 +1,7 @@
 package repo
 
 import (
+	"net/http"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -9,7 +10,7 @@ import (
 	"gogs.io/gogs/internal/mocks"
 )
 
-func Test_validateWebhook(t *testing.T) {
+func TestValidateWebhook(t *testing.T) {
 	l := &mocks.Locale{
 		MockLang: "en",
 		MockTr: func(s string, _ ...any) string {
@@ -18,33 +19,33 @@ func Test_validateWebhook(t *testing.T) {
 	}
 
 	tests := []struct {
-		name     string
-		actor    *database.User
-		webhook  *database.Webhook
-		expField string
-		expMsg   string
-		expOK    bool
+		name       string
+		actor      *database.User
+		webhook    *database.Webhook
+		wantField  string
+		wantMsg    string
+		wantStatus int
 	}{
 		{
-			name:    "admin bypass local address check",
-			webhook: &database.Webhook{URL: "https://www.google.com"},
-			expOK:   true,
+			name:       "admin bypass local address check",
+			webhook:    &database.Webhook{URL: "https://www.google.com"},
+			wantStatus: http.StatusOK,
 		},
 
 		{
-			name:     "local address not allowed",
-			webhook:  &database.Webhook{URL: "http://localhost:3306"},
-			expField: "PayloadURL",
-			expMsg:   "repo.settings.webhook.url_resolved_to_blocked_local_address",
-			expOK:    false,
+			name:       "local address not allowed",
+			webhook:    &database.Webhook{URL: "http://localhost:3306"},
+			wantField:  "PayloadURL",
+			wantMsg:    "repo.settings.webhook.url_resolved_to_blocked_local_address",
+			wantStatus: http.StatusForbidden,
 		},
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			field, msg, ok := validateWebhook(l, test.webhook)
-			assert.Equal(t, test.expOK, ok)
-			assert.Equal(t, test.expMsg, msg)
-			assert.Equal(t, test.expField, field)
+			field, msg, status := validateWebhook(l, test.webhook)
+			assert.Equal(t, test.wantStatus, status)
+			assert.Equal(t, test.wantMsg, msg)
+			assert.Equal(t, test.wantField, field)
 		})
 	}
 }

+ 4 - 3
internal/route/repo/wiki.go

@@ -1,6 +1,7 @@
 package repo
 
 import (
+	"net/http"
 	"strings"
 	"time"
 
@@ -199,14 +200,14 @@ func NewWikiPost(c *context.Context, f form.NewWiki) {
 	c.Data["RequireSimpleMDE"] = true
 
 	if c.HasError() {
-		c.Success(tmplRepoWikiNew)
+		c.HTML(http.StatusBadRequest, tmplRepoWikiNew)
 		return
 	}
 
 	if err := c.Repo.Repository.AddWikiPage(c.User, f.Title, f.Content, f.Message); err != nil {
 		if database.IsErrWikiAlreadyExist(err) {
 			c.Data["Err_Title"] = true
-			c.RenderWithErr(c.Tr("repo.wiki.page_already_exists"), tmplRepoWikiNew, &f)
+			c.RenderWithErr(c.Tr("repo.wiki.page_already_exists"), http.StatusUnprocessableEntity, tmplRepoWikiNew, &f)
 		} else {
 			c.Error(err, "add wiki page")
 		}
@@ -240,7 +241,7 @@ func EditWikiPost(c *context.Context, f form.NewWiki) {
 	c.Data["RequireSimpleMDE"] = true
 
 	if c.HasError() {
-		c.Success(tmplRepoWikiNew)
+		c.HTML(http.StatusBadRequest, tmplRepoWikiNew)
 		return
 	}
 

+ 11 - 11
internal/route/user/auth.go

@@ -158,7 +158,7 @@ func LoginPost(c *context.Context, f form.SignIn) {
 	c.Data["LoginSources"] = loginSources
 
 	if c.HasError() {
-		c.Success(tmplUserAuthLogin)
+		c.HTML(http.StatusBadRequest, tmplUserAuthLogin)
 		return
 	}
 
@@ -167,10 +167,10 @@ func LoginPost(c *context.Context, f form.SignIn) {
 		switch {
 		case auth.IsErrBadCredentials(err):
 			c.FormErr("UserName", "Password")
-			c.RenderWithErr(c.Tr("form.username_password_incorrect"), tmplUserAuthLogin, &f)
+			c.RenderWithErr(c.Tr("form.username_password_incorrect"), http.StatusUnauthorized, tmplUserAuthLogin, &f)
 		case database.IsErrLoginSourceMismatch(err):
 			c.FormErr("LoginSource")
-			c.RenderWithErr(c.Tr("form.auth_source_mismatch"), tmplUserAuthLogin, &f)
+			c.RenderWithErr(c.Tr("form.auth_source_mismatch"), http.StatusUnprocessableEntity, tmplUserAuthLogin, &f)
 
 		default:
 			c.Error(err, "authenticate user")
@@ -320,19 +320,19 @@ func SignUpPost(c *context.Context, cpt *captcha.Captcha, f form.Register) {
 	}
 
 	if c.HasError() {
-		c.Success(tmplUserAuthSignup)
+		c.HTML(http.StatusBadRequest, tmplUserAuthSignup)
 		return
 	}
 
 	if conf.Auth.EnableRegistrationCaptcha && !cpt.VerifyReq(c.Req) {
 		c.FormErr("Captcha")
-		c.RenderWithErr(c.Tr("form.captcha_incorrect"), tmplUserAuthSignup, &f)
+		c.RenderWithErr(c.Tr("form.captcha_incorrect"), http.StatusUnauthorized, tmplUserAuthSignup, &f)
 		return
 	}
 
 	if f.Password != f.Retype {
 		c.FormErr("Password")
-		c.RenderWithErr(c.Tr("form.password_not_match"), tmplUserAuthSignup, &f)
+		c.RenderWithErr(c.Tr("form.password_not_match"), http.StatusBadRequest, tmplUserAuthSignup, &f)
 		return
 	}
 
@@ -349,13 +349,13 @@ func SignUpPost(c *context.Context, cpt *captcha.Captcha, f form.Register) {
 		switch {
 		case database.IsErrUserAlreadyExist(err):
 			c.FormErr("UserName")
-			c.RenderWithErr(c.Tr("form.username_been_taken"), tmplUserAuthSignup, &f)
+			c.RenderWithErr(c.Tr("form.username_been_taken"), http.StatusUnprocessableEntity, tmplUserAuthSignup, &f)
 		case database.IsErrEmailAlreadyUsed(err):
 			c.FormErr("Email")
-			c.RenderWithErr(c.Tr("form.email_been_used"), tmplUserAuthSignup, &f)
+			c.RenderWithErr(c.Tr("form.email_been_used"), http.StatusUnprocessableEntity, tmplUserAuthSignup, &f)
 		case database.IsErrNameNotAllowed(err):
 			c.FormErr("UserName")
-			c.RenderWithErr(c.Tr("user.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), tmplUserAuthSignup, &f)
+			c.RenderWithErr(c.Tr("user.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), http.StatusBadRequest, tmplUserAuthSignup, &f)
 		default:
 			c.Error(err, "create user")
 		}
@@ -569,7 +569,7 @@ func ForgotPasswdPost(c *context.Context) {
 
 	if !u.IsLocal() {
 		c.FormErr("Email")
-		c.RenderWithErr(c.Tr("auth.non_local_account"), tmplUserAuthForgotPassword, nil)
+		c.RenderWithErr(c.Tr("auth.non_local_account"), http.StatusForbidden, tmplUserAuthForgotPassword, nil)
 		return
 	}
 
@@ -618,7 +618,7 @@ func ResetPasswdPost(c *context.Context) {
 		if len(password) < 6 {
 			c.Data["IsResetForm"] = true
 			c.Data["Err_Password"] = true
-			c.RenderWithErr(c.Tr("auth.password_too_short"), tmplUserAuthResetPassword, nil)
+			c.RenderWithErr(c.Tr("auth.password_too_short"), http.StatusBadRequest, tmplUserAuthResetPassword, nil)
 			return
 		}
 

+ 12 - 15
internal/route/user/setting.go

@@ -8,6 +8,7 @@ import (
 	"html/template"
 	"image/png"
 	"io"
+	"net/http"
 
 	"github.com/cockroachdb/errors"
 	"github.com/pquerna/otp"
@@ -72,7 +73,7 @@ func SettingsPost(c *context.Context, f form.UpdateProfile) {
 	c.Data["origin_name"] = c.User.Name
 
 	if c.HasError() {
-		c.Success(tmplUserSettingsProfile)
+		c.HTML(http.StatusBadRequest, tmplUserSettingsProfile)
 		return
 	}
 
@@ -83,18 +84,14 @@ func SettingsPost(c *context.Context, f form.UpdateProfile) {
 			err := database.Handle.Users().ChangeUsername(c.Req.Context(), c.User.ID, f.Name)
 			if err != nil {
 				c.FormErr("Name")
-				var msg string
 				switch {
 				case database.IsErrUserAlreadyExist(errors.Cause(err)):
-					msg = c.Tr("form.username_been_taken")
+					c.RenderWithErr(c.Tr("form.username_been_taken"), http.StatusUnprocessableEntity, tmplUserSettingsProfile, &f)
 				case database.IsErrNameNotAllowed(errors.Cause(err)):
-					msg = c.Tr("user.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value())
+					c.RenderWithErr(c.Tr("user.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), http.StatusBadRequest, tmplUserSettingsProfile, &f)
 				default:
 					c.Error(err, "change user name")
-					return
 				}
-
-				c.RenderWithErr(msg, tmplUserSettingsProfile, &f)
 				return
 			}
 
@@ -203,7 +200,7 @@ func SettingsPasswordPost(c *context.Context, f form.ChangePassword) {
 	c.PageIs("SettingsPassword")
 
 	if c.HasError() {
-		c.Success(tmplUserSettingsPassword)
+		c.HTML(http.StatusBadRequest, tmplUserSettingsPassword)
 		return
 	}
 
@@ -267,14 +264,14 @@ func SettingsEmailPost(c *context.Context, f form.AddEmail) {
 	c.Data["Emails"] = emails
 
 	if c.HasError() {
-		c.Success(tmplUserSettingsEmail)
+		c.HTML(http.StatusBadRequest, tmplUserSettingsEmail)
 		return
 	}
 
 	err = database.Handle.Users().AddEmail(c.Req.Context(), c.User.ID, f.Email, !conf.Auth.RequireEmailConfirmation)
 	if err != nil {
 		if database.IsErrEmailAlreadyUsed(err) {
-			c.RenderWithErr(c.Tr("form.email_been_used"), tmplUserSettingsEmail, &f)
+			c.RenderWithErr(c.Tr("form.email_been_used"), http.StatusUnprocessableEntity, tmplUserSettingsEmail, &f)
 		} else {
 			c.Errorf(err, "add email address")
 		}
@@ -344,7 +341,7 @@ func SettingsSSHKeysPost(c *context.Context, f form.AddSSHKey) {
 	c.Data["Keys"] = keys
 
 	if c.HasError() {
-		c.Success(tmplUserSettingsSSHKeys)
+		c.HTML(http.StatusBadRequest, tmplUserSettingsSSHKeys)
 		return
 	}
 
@@ -364,10 +361,10 @@ func SettingsSSHKeysPost(c *context.Context, f form.AddSSHKey) {
 		switch {
 		case database.IsErrKeyAlreadyExist(err):
 			c.FormErr("Content")
-			c.RenderWithErr(c.Tr("settings.ssh_key_been_used"), tmplUserSettingsSSHKeys, &f)
+			c.RenderWithErr(c.Tr("settings.ssh_key_been_used"), http.StatusUnprocessableEntity, tmplUserSettingsSSHKeys, &f)
 		case database.IsErrKeyNameAlreadyUsed(err):
 			c.FormErr("Title")
-			c.RenderWithErr(c.Tr("settings.ssh_key_name_used"), tmplUserSettingsSSHKeys, &f)
+			c.RenderWithErr(c.Tr("settings.ssh_key_name_used"), http.StatusUnprocessableEntity, tmplUserSettingsSSHKeys, &f)
 		default:
 			c.Errorf(err, "add public key")
 		}
@@ -619,7 +616,7 @@ func (h *SettingsHandler) ApplicationsPost() macaron.Handler {
 			}
 
 			c.Data["Tokens"] = tokens
-			c.Success(tmplUserSettingsApplications)
+			c.HTML(http.StatusBadRequest, tmplUserSettingsApplications)
 			return
 		}
 
@@ -661,7 +658,7 @@ func SettingsDelete(c *context.Context) {
 	if c.Req.Method == "POST" {
 		if _, err := database.Handle.Users().Authenticate(c.Req.Context(), c.User.Name, c.Query("password"), c.User.LoginSource); err != nil {
 			if auth.IsErrBadCredentials(err) {
-				c.RenderWithErr(c.Tr("form.enterred_invalid_password"), tmplUserSettingsDelete, nil)
+				c.RenderWithErr(c.Tr("form.enterred_invalid_password"), http.StatusUnauthorized, tmplUserSettingsDelete, nil)
 			} else {
 				c.Errorf(err, "authenticate user")
 			}