diff --git a/routers/web/repo/setting/avatar.go b/routers/web/repo/setting/avatar.go
new file mode 100644
index 000000000..ec673ca28
--- /dev/null
+++ b/routers/web/repo/setting/avatar.go
@@ -0,0 +1,76 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package setting
+
+import (
+	"errors"
+	"fmt"
+	"io"
+
+	"code.gitea.io/gitea/modules/context"
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/typesniffer"
+	"code.gitea.io/gitea/modules/web"
+	"code.gitea.io/gitea/services/forms"
+	repo_service "code.gitea.io/gitea/services/repository"
+)
+
+// UpdateAvatarSetting update repo's avatar
+func UpdateAvatarSetting(ctx *context.Context, form forms.AvatarForm) error {
+	ctxRepo := ctx.Repo.Repository
+
+	if form.Avatar == nil {
+		// No avatar is uploaded and we not removing it here.
+		// No random avatar generated here.
+		// Just exit, no action.
+		if ctxRepo.CustomAvatarRelativePath() == "" {
+			log.Trace("No avatar was uploaded for repo: %d. Default icon will appear instead.", ctxRepo.ID)
+		}
+		return nil
+	}
+
+	r, err := form.Avatar.Open()
+	if err != nil {
+		return fmt.Errorf("Avatar.Open: %w", err)
+	}
+	defer r.Close()
+
+	if form.Avatar.Size > setting.Avatar.MaxFileSize {
+		return errors.New(ctx.Tr("settings.uploaded_avatar_is_too_big"))
+	}
+
+	data, err := io.ReadAll(r)
+	if err != nil {
+		return fmt.Errorf("io.ReadAll: %w", err)
+	}
+	st := typesniffer.DetectContentType(data)
+	if !(st.IsImage() && !st.IsSvgImage()) {
+		return errors.New(ctx.Tr("settings.uploaded_avatar_not_a_image"))
+	}
+	if err = repo_service.UploadAvatar(ctx, ctxRepo, data); err != nil {
+		return fmt.Errorf("UploadAvatar: %w", err)
+	}
+	return nil
+}
+
+// SettingsAvatar save new POSTed repository avatar
+func SettingsAvatar(ctx *context.Context) {
+	form := web.GetForm(ctx).(*forms.AvatarForm)
+	form.Source = forms.AvatarLocal
+	if err := UpdateAvatarSetting(ctx, *form); err != nil {
+		ctx.Flash.Error(err.Error())
+	} else {
+		ctx.Flash.Success(ctx.Tr("repo.settings.update_avatar_success"))
+	}
+	ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+}
+
+// SettingsDeleteAvatar delete repository avatar
+func SettingsDeleteAvatar(ctx *context.Context) {
+	if err := repo_service.DeleteAvatar(ctx, ctx.Repo.Repository); err != nil {
+		ctx.Flash.Error(fmt.Sprintf("DeleteAvatar: %v", err))
+	}
+	ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+}
diff --git a/routers/web/repo/setting/collaboration.go b/routers/web/repo/setting/collaboration.go
new file mode 100644
index 000000000..d3a995910
--- /dev/null
+++ b/routers/web/repo/setting/collaboration.go
@@ -0,0 +1,210 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package setting
+
+import (
+	"net/http"
+	"strings"
+
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
+	"code.gitea.io/gitea/models/perm"
+	repo_model "code.gitea.io/gitea/models/repo"
+	unit_model "code.gitea.io/gitea/models/unit"
+	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/context"
+	"code.gitea.io/gitea/modules/log"
+	repo_module "code.gitea.io/gitea/modules/repository"
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/routers/utils"
+	"code.gitea.io/gitea/services/mailer"
+	org_service "code.gitea.io/gitea/services/org"
+)
+
+// Collaboration render a repository's collaboration page
+func Collaboration(ctx *context.Context) {
+	ctx.Data["Title"] = ctx.Tr("repo.settings.collaboration")
+	ctx.Data["PageIsSettingsCollaboration"] = true
+
+	users, err := repo_model.GetCollaborators(ctx, ctx.Repo.Repository.ID, db.ListOptions{})
+	if err != nil {
+		ctx.ServerError("GetCollaborators", err)
+		return
+	}
+	ctx.Data["Collaborators"] = users
+
+	teams, err := organization.GetRepoTeams(ctx, ctx.Repo.Repository)
+	if err != nil {
+		ctx.ServerError("GetRepoTeams", err)
+		return
+	}
+	ctx.Data["Teams"] = teams
+	ctx.Data["Repo"] = ctx.Repo.Repository
+	ctx.Data["OrgID"] = ctx.Repo.Repository.OwnerID
+	ctx.Data["OrgName"] = ctx.Repo.Repository.OwnerName
+	ctx.Data["Org"] = ctx.Repo.Repository.Owner
+	ctx.Data["Units"] = unit_model.Units
+
+	ctx.HTML(http.StatusOK, tplCollaboration)
+}
+
+// CollaborationPost response for actions for a collaboration of a repository
+func CollaborationPost(ctx *context.Context) {
+	name := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.FormString("collaborator")))
+	if len(name) == 0 || ctx.Repo.Owner.LowerName == name {
+		ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
+		return
+	}
+
+	u, err := user_model.GetUserByName(ctx, name)
+	if err != nil {
+		if user_model.IsErrUserNotExist(err) {
+			ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
+			ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
+		} else {
+			ctx.ServerError("GetUserByName", err)
+		}
+		return
+	}
+
+	if !u.IsActive {
+		ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_inactive_user"))
+		ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
+		return
+	}
+
+	// Organization is not allowed to be added as a collaborator.
+	if u.IsOrganization() {
+		ctx.Flash.Error(ctx.Tr("repo.settings.org_not_allowed_to_be_collaborator"))
+		ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
+		return
+	}
+
+	if got, err := repo_model.IsCollaborator(ctx, ctx.Repo.Repository.ID, u.ID); err == nil && got {
+		ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_duplicate"))
+		ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
+		return
+	}
+
+	// find the owner team of the organization the repo belongs too and
+	// check if the user we're trying to add is an owner.
+	if ctx.Repo.Repository.Owner.IsOrganization() {
+		if isOwner, err := organization.IsOrganizationOwner(ctx, ctx.Repo.Repository.Owner.ID, u.ID); err != nil {
+			ctx.ServerError("IsOrganizationOwner", err)
+			return
+		} else if isOwner {
+			ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_owner"))
+			ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
+			return
+		}
+	}
+
+	if err = repo_module.AddCollaborator(ctx, ctx.Repo.Repository, u); err != nil {
+		ctx.ServerError("AddCollaborator", err)
+		return
+	}
+
+	if setting.Service.EnableNotifyMail {
+		mailer.SendCollaboratorMail(u, ctx.Doer, ctx.Repo.Repository)
+	}
+
+	ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success"))
+	ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
+}
+
+// ChangeCollaborationAccessMode response for changing access of a collaboration
+func ChangeCollaborationAccessMode(ctx *context.Context) {
+	if err := repo_model.ChangeCollaborationAccessMode(
+		ctx,
+		ctx.Repo.Repository,
+		ctx.FormInt64("uid"),
+		perm.AccessMode(ctx.FormInt("mode"))); err != nil {
+		log.Error("ChangeCollaborationAccessMode: %v", err)
+	}
+}
+
+// DeleteCollaboration delete a collaboration for a repository
+func DeleteCollaboration(ctx *context.Context) {
+	if err := models.DeleteCollaboration(ctx.Repo.Repository, ctx.FormInt64("id")); err != nil {
+		ctx.Flash.Error("DeleteCollaboration: " + err.Error())
+	} else {
+		ctx.Flash.Success(ctx.Tr("repo.settings.remove_collaborator_success"))
+	}
+
+	ctx.JSON(http.StatusOK, map[string]interface{}{
+		"redirect": ctx.Repo.RepoLink + "/settings/collaboration",
+	})
+}
+
+// AddTeamPost response for adding a team to a repository
+func AddTeamPost(ctx *context.Context) {
+	if !ctx.Repo.Owner.RepoAdminChangeTeamAccess && !ctx.Repo.IsOwner() {
+		ctx.Flash.Error(ctx.Tr("repo.settings.change_team_access_not_allowed"))
+		ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
+		return
+	}
+
+	name := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.FormString("team")))
+	if len(name) == 0 {
+		ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
+		return
+	}
+
+	team, err := organization.OrgFromUser(ctx.Repo.Owner).GetTeam(ctx, name)
+	if err != nil {
+		if organization.IsErrTeamNotExist(err) {
+			ctx.Flash.Error(ctx.Tr("form.team_not_exist"))
+			ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
+		} else {
+			ctx.ServerError("GetTeam", err)
+		}
+		return
+	}
+
+	if team.OrgID != ctx.Repo.Repository.OwnerID {
+		ctx.Flash.Error(ctx.Tr("repo.settings.team_not_in_organization"))
+		ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
+		return
+	}
+
+	if organization.HasTeamRepo(ctx, ctx.Repo.Repository.OwnerID, team.ID, ctx.Repo.Repository.ID) {
+		ctx.Flash.Error(ctx.Tr("repo.settings.add_team_duplicate"))
+		ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
+		return
+	}
+
+	if err = org_service.TeamAddRepository(team, ctx.Repo.Repository); err != nil {
+		ctx.ServerError("TeamAddRepository", err)
+		return
+	}
+
+	ctx.Flash.Success(ctx.Tr("repo.settings.add_team_success"))
+	ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
+}
+
+// DeleteTeam response for deleting a team from a repository
+func DeleteTeam(ctx *context.Context) {
+	if !ctx.Repo.Owner.RepoAdminChangeTeamAccess && !ctx.Repo.IsOwner() {
+		ctx.Flash.Error(ctx.Tr("repo.settings.change_team_access_not_allowed"))
+		ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
+		return
+	}
+
+	team, err := organization.GetTeamByID(ctx, ctx.FormInt64("id"))
+	if err != nil {
+		ctx.ServerError("GetTeamByID", err)
+		return
+	}
+
+	if err = models.RemoveRepository(team, ctx.Repo.Repository.ID); err != nil {
+		ctx.ServerError("team.RemoveRepositorys", err)
+		return
+	}
+
+	ctx.Flash.Success(ctx.Tr("repo.settings.remove_team_success"))
+	ctx.JSON(http.StatusOK, map[string]interface{}{
+		"redirect": ctx.Repo.RepoLink + "/settings/collaboration",
+	})
+}
diff --git a/routers/web/repo/setting/deploy_key.go b/routers/web/repo/setting/deploy_key.go
new file mode 100644
index 000000000..977609567
--- /dev/null
+++ b/routers/web/repo/setting/deploy_key.go
@@ -0,0 +1,111 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package setting
+
+import (
+	"net/http"
+
+	asymkey_model "code.gitea.io/gitea/models/asymkey"
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/modules/context"
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/web"
+	asymkey_service "code.gitea.io/gitea/services/asymkey"
+	"code.gitea.io/gitea/services/forms"
+)
+
+// DeployKeys render the deploy keys list of a repository page
+func DeployKeys(ctx *context.Context) {
+	ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys") + " / " + ctx.Tr("secrets.secrets")
+	ctx.Data["PageIsSettingsKeys"] = true
+	ctx.Data["DisableSSH"] = setting.SSH.Disabled
+
+	keys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID})
+	if err != nil {
+		ctx.ServerError("ListDeployKeys", err)
+		return
+	}
+	ctx.Data["Deploykeys"] = keys
+
+	ctx.HTML(http.StatusOK, tplDeployKeys)
+}
+
+// DeployKeysPost response for adding a deploy key of a repository
+func DeployKeysPost(ctx *context.Context) {
+	form := web.GetForm(ctx).(*forms.AddKeyForm)
+	ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys")
+	ctx.Data["PageIsSettingsKeys"] = true
+	ctx.Data["DisableSSH"] = setting.SSH.Disabled
+
+	keys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID})
+	if err != nil {
+		ctx.ServerError("ListDeployKeys", err)
+		return
+	}
+	ctx.Data["Deploykeys"] = keys
+
+	if ctx.HasError() {
+		ctx.HTML(http.StatusOK, tplDeployKeys)
+		return
+	}
+
+	content, err := asymkey_model.CheckPublicKeyString(form.Content)
+	if err != nil {
+		if db.IsErrSSHDisabled(err) {
+			ctx.Flash.Info(ctx.Tr("settings.ssh_disabled"))
+		} else if asymkey_model.IsErrKeyUnableVerify(err) {
+			ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key"))
+		} else if err == asymkey_model.ErrKeyIsPrivate {
+			ctx.Data["HasError"] = true
+			ctx.Data["Err_Content"] = true
+			ctx.Flash.Error(ctx.Tr("form.must_use_public_key"))
+		} else {
+			ctx.Data["HasError"] = true
+			ctx.Data["Err_Content"] = true
+			ctx.Flash.Error(ctx.Tr("form.invalid_ssh_key", err.Error()))
+		}
+		ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys")
+		return
+	}
+
+	key, err := asymkey_model.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content, !form.IsWritable)
+	if err != nil {
+		ctx.Data["HasError"] = true
+		switch {
+		case asymkey_model.IsErrDeployKeyAlreadyExist(err):
+			ctx.Data["Err_Content"] = true
+			ctx.RenderWithErr(ctx.Tr("repo.settings.key_been_used"), tplDeployKeys, &form)
+		case asymkey_model.IsErrKeyAlreadyExist(err):
+			ctx.Data["Err_Content"] = true
+			ctx.RenderWithErr(ctx.Tr("settings.ssh_key_been_used"), tplDeployKeys, &form)
+		case asymkey_model.IsErrKeyNameAlreadyUsed(err):
+			ctx.Data["Err_Title"] = true
+			ctx.RenderWithErr(ctx.Tr("repo.settings.key_name_used"), tplDeployKeys, &form)
+		case asymkey_model.IsErrDeployKeyNameAlreadyUsed(err):
+			ctx.Data["Err_Title"] = true
+			ctx.RenderWithErr(ctx.Tr("repo.settings.key_name_used"), tplDeployKeys, &form)
+		default:
+			ctx.ServerError("AddDeployKey", err)
+		}
+		return
+	}
+
+	log.Trace("Deploy key added: %d", ctx.Repo.Repository.ID)
+	ctx.Flash.Success(ctx.Tr("repo.settings.add_key_success", key.Name))
+	ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys")
+}
+
+// DeleteDeployKey response for deleting a deploy key
+func DeleteDeployKey(ctx *context.Context) {
+	if err := asymkey_service.DeleteDeployKey(ctx.Doer, ctx.FormInt64("id")); err != nil {
+		ctx.Flash.Error("DeleteDeployKey: " + err.Error())
+	} else {
+		ctx.Flash.Success(ctx.Tr("repo.settings.deploy_key_deletion_success"))
+	}
+
+	ctx.JSON(http.StatusOK, map[string]interface{}{
+		"redirect": ctx.Repo.RepoLink + "/settings/keys",
+	})
+}
diff --git a/routers/web/repo/setting/git_hooks.go b/routers/web/repo/setting/git_hooks.go
new file mode 100644
index 000000000..551327d44
--- /dev/null
+++ b/routers/web/repo/setting/git_hooks.go
@@ -0,0 +1,65 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package setting
+
+import (
+	"net/http"
+
+	"code.gitea.io/gitea/modules/context"
+	"code.gitea.io/gitea/modules/git"
+)
+
+// GitHooks hooks of a repository
+func GitHooks(ctx *context.Context) {
+	ctx.Data["Title"] = ctx.Tr("repo.settings.githooks")
+	ctx.Data["PageIsSettingsGitHooks"] = true
+
+	hooks, err := ctx.Repo.GitRepo.Hooks()
+	if err != nil {
+		ctx.ServerError("Hooks", err)
+		return
+	}
+	ctx.Data["Hooks"] = hooks
+
+	ctx.HTML(http.StatusOK, tplGithooks)
+}
+
+// GitHooksEdit render for editing a hook of repository page
+func GitHooksEdit(ctx *context.Context) {
+	ctx.Data["Title"] = ctx.Tr("repo.settings.githooks")
+	ctx.Data["PageIsSettingsGitHooks"] = true
+
+	name := ctx.Params(":name")
+	hook, err := ctx.Repo.GitRepo.GetHook(name)
+	if err != nil {
+		if err == git.ErrNotValidHook {
+			ctx.NotFound("GetHook", err)
+		} else {
+			ctx.ServerError("GetHook", err)
+		}
+		return
+	}
+	ctx.Data["Hook"] = hook
+	ctx.HTML(http.StatusOK, tplGithookEdit)
+}
+
+// GitHooksEditPost response for editing a git hook of a repository
+func GitHooksEditPost(ctx *context.Context) {
+	name := ctx.Params(":name")
+	hook, err := ctx.Repo.GitRepo.GetHook(name)
+	if err != nil {
+		if err == git.ErrNotValidHook {
+			ctx.NotFound("GetHook", err)
+		} else {
+			ctx.ServerError("GetHook", err)
+		}
+		return
+	}
+	hook.Content = ctx.FormString("content")
+	if err = hook.Update(); err != nil {
+		ctx.ServerError("hook.Update", err)
+		return
+	}
+	ctx.Redirect(ctx.Repo.RepoLink + "/settings/hooks/git")
+}
diff --git a/routers/web/repo/lfs.go b/routers/web/repo/setting/lfs.go
similarity index 99%
rename from routers/web/repo/lfs.go
rename to routers/web/repo/setting/lfs.go
index 9957869c9..d478acdde 100644
--- a/routers/web/repo/lfs.go
+++ b/routers/web/repo/setting/lfs.go
@@ -1,7 +1,7 @@
 // Copyright 2019 The Gitea Authors. All rights reserved.
 // SPDX-License-Identifier: MIT
 
-package repo
+package setting
 
 import (
 	"bytes"
diff --git a/routers/web/repo/setting/main_test.go b/routers/web/repo/setting/main_test.go
new file mode 100644
index 000000000..5a6fa5621
--- /dev/null
+++ b/routers/web/repo/setting/main_test.go
@@ -0,0 +1,17 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package setting
+
+import (
+	"path/filepath"
+	"testing"
+
+	"code.gitea.io/gitea/models/unittest"
+)
+
+func TestMain(m *testing.M) {
+	unittest.MainTest(m, &unittest.TestOptions{
+		GiteaRootPath: filepath.Join("..", "..", "..", ".."),
+	})
+}
diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting/protected_branch.go
similarity index 99%
rename from routers/web/repo/setting_protected_branch.go
rename to routers/web/repo/setting/protected_branch.go
index ae8a799f0..962b85d0c 100644
--- a/routers/web/repo/setting_protected_branch.go
+++ b/routers/web/repo/setting/protected_branch.go
@@ -1,7 +1,7 @@
 // Copyright 2017 The Gitea Authors. All rights reserved.
 // SPDX-License-Identifier: MIT
 
-package repo
+package setting
 
 import (
 	"fmt"
diff --git a/routers/web/repo/tag.go b/routers/web/repo/setting/protected_tag.go
similarity index 97%
rename from routers/web/repo/tag.go
rename to routers/web/repo/setting/protected_tag.go
index 95bc6dfce..aafbd19e8 100644
--- a/routers/web/repo/tag.go
+++ b/routers/web/repo/setting/protected_tag.go
@@ -1,7 +1,7 @@
 // Copyright 2021 The Gitea Authors. All rights reserved.
 // SPDX-License-Identifier: MIT
 
-package repo
+package setting
 
 import (
 	"fmt"
@@ -19,8 +19,12 @@ import (
 	"code.gitea.io/gitea/services/forms"
 )
 
+const (
+	tplTags base.TplName = "repo/settings/tags"
+)
+
 // Tags render the page to protect tags
-func Tags(ctx *context.Context) {
+func ProtectedTags(ctx *context.Context) {
 	if setTagsContext(ctx) != nil {
 		return
 	}
diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting/setting.go
similarity index 70%
rename from routers/web/repo/setting.go
rename to routers/web/repo/setting/setting.go
index 4481ff4f0..b33660ffc 100644
--- a/routers/web/repo/setting.go
+++ b/routers/web/repo/setting/setting.go
@@ -2,22 +2,18 @@
 // Copyright 2018 The Gitea Authors. All rights reserved.
 // SPDX-License-Identifier: MIT
 
-package repo
+package setting
 
 import (
-	"errors"
 	"fmt"
-	"io"
 	"net/http"
 	"strconv"
 	"strings"
 	"time"
 
 	"code.gitea.io/gitea/models"
-	asymkey_model "code.gitea.io/gitea/models/asymkey"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/organization"
-	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	unit_model "code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
@@ -32,17 +28,13 @@ import (
 	repo_module "code.gitea.io/gitea/modules/repository"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/structs"
-	"code.gitea.io/gitea/modules/typesniffer"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/modules/validation"
 	"code.gitea.io/gitea/modules/web"
-	"code.gitea.io/gitea/routers/utils"
 	asymkey_service "code.gitea.io/gitea/services/asymkey"
 	"code.gitea.io/gitea/services/forms"
-	"code.gitea.io/gitea/services/mailer"
 	"code.gitea.io/gitea/services/migrations"
 	mirror_service "code.gitea.io/gitea/services/mirror"
-	org_service "code.gitea.io/gitea/services/org"
 	repo_service "code.gitea.io/gitea/services/repository"
 	wiki_service "code.gitea.io/gitea/services/wiki"
 )
@@ -51,7 +43,6 @@ const (
 	tplSettingsOptions base.TplName = "repo/settings/options"
 	tplCollaboration   base.TplName = "repo/settings/collaboration"
 	tplBranches        base.TplName = "repo/settings/branches"
-	tplTags            base.TplName = "repo/settings/tags"
 	tplGithooks        base.TplName = "repo/settings/githooks"
 	tplGithookEdit     base.TplName = "repo/settings/githook_edit"
 	tplDeployKeys      base.TplName = "repo/settings/deploy_keys"
@@ -895,398 +886,6 @@ func handleSettingRemoteAddrError(ctx *context.Context, err error, form *forms.R
 	ctx.RenderWithErr(ctx.Tr("repo.mirror_address_url_invalid"), tplSettingsOptions, form)
 }
 
-// Collaboration render a repository's collaboration page
-func Collaboration(ctx *context.Context) {
-	ctx.Data["Title"] = ctx.Tr("repo.settings.collaboration")
-	ctx.Data["PageIsSettingsCollaboration"] = true
-
-	users, err := repo_model.GetCollaborators(ctx, ctx.Repo.Repository.ID, db.ListOptions{})
-	if err != nil {
-		ctx.ServerError("GetCollaborators", err)
-		return
-	}
-	ctx.Data["Collaborators"] = users
-
-	teams, err := organization.GetRepoTeams(ctx, ctx.Repo.Repository)
-	if err != nil {
-		ctx.ServerError("GetRepoTeams", err)
-		return
-	}
-	ctx.Data["Teams"] = teams
-	ctx.Data["Repo"] = ctx.Repo.Repository
-	ctx.Data["OrgID"] = ctx.Repo.Repository.OwnerID
-	ctx.Data["OrgName"] = ctx.Repo.Repository.OwnerName
-	ctx.Data["Org"] = ctx.Repo.Repository.Owner
-	ctx.Data["Units"] = unit_model.Units
-
-	ctx.HTML(http.StatusOK, tplCollaboration)
-}
-
-// CollaborationPost response for actions for a collaboration of a repository
-func CollaborationPost(ctx *context.Context) {
-	name := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.FormString("collaborator")))
-	if len(name) == 0 || ctx.Repo.Owner.LowerName == name {
-		ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
-		return
-	}
-
-	u, err := user_model.GetUserByName(ctx, name)
-	if err != nil {
-		if user_model.IsErrUserNotExist(err) {
-			ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
-			ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
-		} else {
-			ctx.ServerError("GetUserByName", err)
-		}
-		return
-	}
-
-	if !u.IsActive {
-		ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_inactive_user"))
-		ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
-		return
-	}
-
-	// Organization is not allowed to be added as a collaborator.
-	if u.IsOrganization() {
-		ctx.Flash.Error(ctx.Tr("repo.settings.org_not_allowed_to_be_collaborator"))
-		ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
-		return
-	}
-
-	if got, err := repo_model.IsCollaborator(ctx, ctx.Repo.Repository.ID, u.ID); err == nil && got {
-		ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_duplicate"))
-		ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
-		return
-	}
-
-	// find the owner team of the organization the repo belongs too and
-	// check if the user we're trying to add is an owner.
-	if ctx.Repo.Repository.Owner.IsOrganization() {
-		if isOwner, err := organization.IsOrganizationOwner(ctx, ctx.Repo.Repository.Owner.ID, u.ID); err != nil {
-			ctx.ServerError("IsOrganizationOwner", err)
-			return
-		} else if isOwner {
-			ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_owner"))
-			ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
-			return
-		}
-	}
-
-	if err = repo_module.AddCollaborator(ctx, ctx.Repo.Repository, u); err != nil {
-		ctx.ServerError("AddCollaborator", err)
-		return
-	}
-
-	if setting.Service.EnableNotifyMail {
-		mailer.SendCollaboratorMail(u, ctx.Doer, ctx.Repo.Repository)
-	}
-
-	ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success"))
-	ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
-}
-
-// ChangeCollaborationAccessMode response for changing access of a collaboration
-func ChangeCollaborationAccessMode(ctx *context.Context) {
-	if err := repo_model.ChangeCollaborationAccessMode(
-		ctx,
-		ctx.Repo.Repository,
-		ctx.FormInt64("uid"),
-		perm.AccessMode(ctx.FormInt("mode"))); err != nil {
-		log.Error("ChangeCollaborationAccessMode: %v", err)
-	}
-}
-
-// DeleteCollaboration delete a collaboration for a repository
-func DeleteCollaboration(ctx *context.Context) {
-	if err := models.DeleteCollaboration(ctx.Repo.Repository, ctx.FormInt64("id")); err != nil {
-		ctx.Flash.Error("DeleteCollaboration: " + err.Error())
-	} else {
-		ctx.Flash.Success(ctx.Tr("repo.settings.remove_collaborator_success"))
-	}
-
-	ctx.JSON(http.StatusOK, map[string]interface{}{
-		"redirect": ctx.Repo.RepoLink + "/settings/collaboration",
-	})
-}
-
-// AddTeamPost response for adding a team to a repository
-func AddTeamPost(ctx *context.Context) {
-	if !ctx.Repo.Owner.RepoAdminChangeTeamAccess && !ctx.Repo.IsOwner() {
-		ctx.Flash.Error(ctx.Tr("repo.settings.change_team_access_not_allowed"))
-		ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
-		return
-	}
-
-	name := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.FormString("team")))
-	if len(name) == 0 {
-		ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
-		return
-	}
-
-	team, err := organization.OrgFromUser(ctx.Repo.Owner).GetTeam(ctx, name)
-	if err != nil {
-		if organization.IsErrTeamNotExist(err) {
-			ctx.Flash.Error(ctx.Tr("form.team_not_exist"))
-			ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
-		} else {
-			ctx.ServerError("GetTeam", err)
-		}
-		return
-	}
-
-	if team.OrgID != ctx.Repo.Repository.OwnerID {
-		ctx.Flash.Error(ctx.Tr("repo.settings.team_not_in_organization"))
-		ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
-		return
-	}
-
-	if organization.HasTeamRepo(ctx, ctx.Repo.Repository.OwnerID, team.ID, ctx.Repo.Repository.ID) {
-		ctx.Flash.Error(ctx.Tr("repo.settings.add_team_duplicate"))
-		ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
-		return
-	}
-
-	if err = org_service.TeamAddRepository(team, ctx.Repo.Repository); err != nil {
-		ctx.ServerError("TeamAddRepository", err)
-		return
-	}
-
-	ctx.Flash.Success(ctx.Tr("repo.settings.add_team_success"))
-	ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
-}
-
-// DeleteTeam response for deleting a team from a repository
-func DeleteTeam(ctx *context.Context) {
-	if !ctx.Repo.Owner.RepoAdminChangeTeamAccess && !ctx.Repo.IsOwner() {
-		ctx.Flash.Error(ctx.Tr("repo.settings.change_team_access_not_allowed"))
-		ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
-		return
-	}
-
-	team, err := organization.GetTeamByID(ctx, ctx.FormInt64("id"))
-	if err != nil {
-		ctx.ServerError("GetTeamByID", err)
-		return
-	}
-
-	if err = models.RemoveRepository(team, ctx.Repo.Repository.ID); err != nil {
-		ctx.ServerError("team.RemoveRepositorys", err)
-		return
-	}
-
-	ctx.Flash.Success(ctx.Tr("repo.settings.remove_team_success"))
-	ctx.JSON(http.StatusOK, map[string]interface{}{
-		"redirect": ctx.Repo.RepoLink + "/settings/collaboration",
-	})
-}
-
-// GitHooks hooks of a repository
-func GitHooks(ctx *context.Context) {
-	ctx.Data["Title"] = ctx.Tr("repo.settings.githooks")
-	ctx.Data["PageIsSettingsGitHooks"] = true
-
-	hooks, err := ctx.Repo.GitRepo.Hooks()
-	if err != nil {
-		ctx.ServerError("Hooks", err)
-		return
-	}
-	ctx.Data["Hooks"] = hooks
-
-	ctx.HTML(http.StatusOK, tplGithooks)
-}
-
-// GitHooksEdit render for editing a hook of repository page
-func GitHooksEdit(ctx *context.Context) {
-	ctx.Data["Title"] = ctx.Tr("repo.settings.githooks")
-	ctx.Data["PageIsSettingsGitHooks"] = true
-
-	name := ctx.Params(":name")
-	hook, err := ctx.Repo.GitRepo.GetHook(name)
-	if err != nil {
-		if err == git.ErrNotValidHook {
-			ctx.NotFound("GetHook", err)
-		} else {
-			ctx.ServerError("GetHook", err)
-		}
-		return
-	}
-	ctx.Data["Hook"] = hook
-	ctx.HTML(http.StatusOK, tplGithookEdit)
-}
-
-// GitHooksEditPost response for editing a git hook of a repository
-func GitHooksEditPost(ctx *context.Context) {
-	name := ctx.Params(":name")
-	hook, err := ctx.Repo.GitRepo.GetHook(name)
-	if err != nil {
-		if err == git.ErrNotValidHook {
-			ctx.NotFound("GetHook", err)
-		} else {
-			ctx.ServerError("GetHook", err)
-		}
-		return
-	}
-	hook.Content = ctx.FormString("content")
-	if err = hook.Update(); err != nil {
-		ctx.ServerError("hook.Update", err)
-		return
-	}
-	ctx.Redirect(ctx.Repo.RepoLink + "/settings/hooks/git")
-}
-
-// DeployKeys render the deploy keys list of a repository page
-func DeployKeys(ctx *context.Context) {
-	ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys") + " / " + ctx.Tr("secrets.secrets")
-	ctx.Data["PageIsSettingsKeys"] = true
-	ctx.Data["DisableSSH"] = setting.SSH.Disabled
-
-	keys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID})
-	if err != nil {
-		ctx.ServerError("ListDeployKeys", err)
-		return
-	}
-	ctx.Data["Deploykeys"] = keys
-
-	ctx.HTML(http.StatusOK, tplDeployKeys)
-}
-
-// DeployKeysPost response for adding a deploy key of a repository
-func DeployKeysPost(ctx *context.Context) {
-	form := web.GetForm(ctx).(*forms.AddKeyForm)
-	ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys")
-	ctx.Data["PageIsSettingsKeys"] = true
-	ctx.Data["DisableSSH"] = setting.SSH.Disabled
-
-	keys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID})
-	if err != nil {
-		ctx.ServerError("ListDeployKeys", err)
-		return
-	}
-	ctx.Data["Deploykeys"] = keys
-
-	if ctx.HasError() {
-		ctx.HTML(http.StatusOK, tplDeployKeys)
-		return
-	}
-
-	content, err := asymkey_model.CheckPublicKeyString(form.Content)
-	if err != nil {
-		if db.IsErrSSHDisabled(err) {
-			ctx.Flash.Info(ctx.Tr("settings.ssh_disabled"))
-		} else if asymkey_model.IsErrKeyUnableVerify(err) {
-			ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key"))
-		} else if err == asymkey_model.ErrKeyIsPrivate {
-			ctx.Data["HasError"] = true
-			ctx.Data["Err_Content"] = true
-			ctx.Flash.Error(ctx.Tr("form.must_use_public_key"))
-		} else {
-			ctx.Data["HasError"] = true
-			ctx.Data["Err_Content"] = true
-			ctx.Flash.Error(ctx.Tr("form.invalid_ssh_key", err.Error()))
-		}
-		ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys")
-		return
-	}
-
-	key, err := asymkey_model.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content, !form.IsWritable)
-	if err != nil {
-		ctx.Data["HasError"] = true
-		switch {
-		case asymkey_model.IsErrDeployKeyAlreadyExist(err):
-			ctx.Data["Err_Content"] = true
-			ctx.RenderWithErr(ctx.Tr("repo.settings.key_been_used"), tplDeployKeys, &form)
-		case asymkey_model.IsErrKeyAlreadyExist(err):
-			ctx.Data["Err_Content"] = true
-			ctx.RenderWithErr(ctx.Tr("settings.ssh_key_been_used"), tplDeployKeys, &form)
-		case asymkey_model.IsErrKeyNameAlreadyUsed(err):
-			ctx.Data["Err_Title"] = true
-			ctx.RenderWithErr(ctx.Tr("repo.settings.key_name_used"), tplDeployKeys, &form)
-		case asymkey_model.IsErrDeployKeyNameAlreadyUsed(err):
-			ctx.Data["Err_Title"] = true
-			ctx.RenderWithErr(ctx.Tr("repo.settings.key_name_used"), tplDeployKeys, &form)
-		default:
-			ctx.ServerError("AddDeployKey", err)
-		}
-		return
-	}
-
-	log.Trace("Deploy key added: %d", ctx.Repo.Repository.ID)
-	ctx.Flash.Success(ctx.Tr("repo.settings.add_key_success", key.Name))
-	ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys")
-}
-
-// DeleteDeployKey response for deleting a deploy key
-func DeleteDeployKey(ctx *context.Context) {
-	if err := asymkey_service.DeleteDeployKey(ctx.Doer, ctx.FormInt64("id")); err != nil {
-		ctx.Flash.Error("DeleteDeployKey: " + err.Error())
-	} else {
-		ctx.Flash.Success(ctx.Tr("repo.settings.deploy_key_deletion_success"))
-	}
-
-	ctx.JSON(http.StatusOK, map[string]interface{}{
-		"redirect": ctx.Repo.RepoLink + "/settings/keys",
-	})
-}
-
-// UpdateAvatarSetting update repo's avatar
-func UpdateAvatarSetting(ctx *context.Context, form forms.AvatarForm) error {
-	ctxRepo := ctx.Repo.Repository
-
-	if form.Avatar == nil {
-		// No avatar is uploaded and we not removing it here.
-		// No random avatar generated here.
-		// Just exit, no action.
-		if ctxRepo.CustomAvatarRelativePath() == "" {
-			log.Trace("No avatar was uploaded for repo: %d. Default icon will appear instead.", ctxRepo.ID)
-		}
-		return nil
-	}
-
-	r, err := form.Avatar.Open()
-	if err != nil {
-		return fmt.Errorf("Avatar.Open: %w", err)
-	}
-	defer r.Close()
-
-	if form.Avatar.Size > setting.Avatar.MaxFileSize {
-		return errors.New(ctx.Tr("settings.uploaded_avatar_is_too_big"))
-	}
-
-	data, err := io.ReadAll(r)
-	if err != nil {
-		return fmt.Errorf("io.ReadAll: %w", err)
-	}
-	st := typesniffer.DetectContentType(data)
-	if !(st.IsImage() && !st.IsSvgImage()) {
-		return errors.New(ctx.Tr("settings.uploaded_avatar_not_a_image"))
-	}
-	if err = repo_service.UploadAvatar(ctx, ctxRepo, data); err != nil {
-		return fmt.Errorf("UploadAvatar: %w", err)
-	}
-	return nil
-}
-
-// SettingsAvatar save new POSTed repository avatar
-func SettingsAvatar(ctx *context.Context) {
-	form := web.GetForm(ctx).(*forms.AvatarForm)
-	form.Source = forms.AvatarLocal
-	if err := UpdateAvatarSetting(ctx, *form); err != nil {
-		ctx.Flash.Error(err.Error())
-	} else {
-		ctx.Flash.Success(ctx.Tr("repo.settings.update_avatar_success"))
-	}
-	ctx.Redirect(ctx.Repo.RepoLink + "/settings")
-}
-
-// SettingsDeleteAvatar delete repository avatar
-func SettingsDeleteAvatar(ctx *context.Context) {
-	if err := repo_service.DeleteAvatar(ctx, ctx.Repo.Repository); err != nil {
-		ctx.Flash.Error(fmt.Sprintf("DeleteAvatar: %v", err))
-	}
-	ctx.Redirect(ctx.Repo.RepoLink + "/settings")
-}
-
 func selectPushMirrorByForm(ctx *context.Context, form *forms.RepoSettingForm, repo *repo_model.Repository) (*repo_model.PushMirror, error) {
 	id, err := strconv.ParseInt(form.PushMirrorID, 10, 64)
 	if err != nil {
diff --git a/routers/web/repo/settings_test.go b/routers/web/repo/setting/settings_test.go
similarity index 99%
rename from routers/web/repo/settings_test.go
rename to routers/web/repo/setting/settings_test.go
index a33e92c82..6f7c844ce 100644
--- a/routers/web/repo/settings_test.go
+++ b/routers/web/repo/setting/settings_test.go
@@ -1,7 +1,7 @@
 // Copyright 2017 The Gitea Authors. All rights reserved.
 // SPDX-License-Identifier: MIT
 
-package repo
+package setting
 
 import (
 	"net/http"
diff --git a/routers/web/repo/webhook.go b/routers/web/repo/setting/webhook.go
similarity index 99%
rename from routers/web/repo/webhook.go
rename to routers/web/repo/setting/webhook.go
index b358c7260..a1cedd9a3 100644
--- a/routers/web/repo/webhook.go
+++ b/routers/web/repo/setting/webhook.go
@@ -2,7 +2,7 @@
 // Copyright 2017 The Gitea Authors. All rights reserved.
 // SPDX-License-Identifier: MIT
 
-package repo
+package setting
 
 import (
 	"errors"
diff --git a/routers/web/web.go b/routers/web/web.go
index a5465eb04..5dd7be120 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -279,32 +279,32 @@ func registerRoutes(m *web.Route) {
 	}
 
 	addWebhookAddRoutes := func() {
-		m.Get("/{type}/new", repo.WebhooksNew)
-		m.Post("/gitea/new", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksNewPost)
-		m.Post("/gogs/new", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksNewPost)
-		m.Post("/slack/new", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksNewPost)
-		m.Post("/discord/new", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksNewPost)
-		m.Post("/dingtalk/new", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost)
-		m.Post("/telegram/new", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksNewPost)
-		m.Post("/matrix/new", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksNewPost)
-		m.Post("/msteams/new", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
-		m.Post("/feishu/new", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost)
-		m.Post("/wechatwork/new", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost)
-		m.Post("/packagist/new", web.Bind(forms.NewPackagistHookForm{}), repo.PackagistHooksNewPost)
+		m.Get("/{type}/new", repo_setting.WebhooksNew)
+		m.Post("/gitea/new", web.Bind(forms.NewWebhookForm{}), repo_setting.GiteaHooksNewPost)
+		m.Post("/gogs/new", web.Bind(forms.NewGogshookForm{}), repo_setting.GogsHooksNewPost)
+		m.Post("/slack/new", web.Bind(forms.NewSlackHookForm{}), repo_setting.SlackHooksNewPost)
+		m.Post("/discord/new", web.Bind(forms.NewDiscordHookForm{}), repo_setting.DiscordHooksNewPost)
+		m.Post("/dingtalk/new", web.Bind(forms.NewDingtalkHookForm{}), repo_setting.DingtalkHooksNewPost)
+		m.Post("/telegram/new", web.Bind(forms.NewTelegramHookForm{}), repo_setting.TelegramHooksNewPost)
+		m.Post("/matrix/new", web.Bind(forms.NewMatrixHookForm{}), repo_setting.MatrixHooksNewPost)
+		m.Post("/msteams/new", web.Bind(forms.NewMSTeamsHookForm{}), repo_setting.MSTeamsHooksNewPost)
+		m.Post("/feishu/new", web.Bind(forms.NewFeishuHookForm{}), repo_setting.FeishuHooksNewPost)
+		m.Post("/wechatwork/new", web.Bind(forms.NewWechatWorkHookForm{}), repo_setting.WechatworkHooksNewPost)
+		m.Post("/packagist/new", web.Bind(forms.NewPackagistHookForm{}), repo_setting.PackagistHooksNewPost)
 	}
 
 	addWebhookEditRoutes := func() {
-		m.Post("/gitea/{id}", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksEditPost)
-		m.Post("/gogs/{id}", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksEditPost)
-		m.Post("/slack/{id}", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksEditPost)
-		m.Post("/discord/{id}", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
-		m.Post("/dingtalk/{id}", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost)
-		m.Post("/telegram/{id}", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksEditPost)
-		m.Post("/matrix/{id}", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksEditPost)
-		m.Post("/msteams/{id}", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
-		m.Post("/feishu/{id}", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
-		m.Post("/wechatwork/{id}", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost)
-		m.Post("/packagist/{id}", web.Bind(forms.NewPackagistHookForm{}), repo.PackagistHooksEditPost)
+		m.Post("/gitea/{id}", web.Bind(forms.NewWebhookForm{}), repo_setting.GiteaHooksEditPost)
+		m.Post("/gogs/{id}", web.Bind(forms.NewGogshookForm{}), repo_setting.GogsHooksEditPost)
+		m.Post("/slack/{id}", web.Bind(forms.NewSlackHookForm{}), repo_setting.SlackHooksEditPost)
+		m.Post("/discord/{id}", web.Bind(forms.NewDiscordHookForm{}), repo_setting.DiscordHooksEditPost)
+		m.Post("/dingtalk/{id}", web.Bind(forms.NewDingtalkHookForm{}), repo_setting.DingtalkHooksEditPost)
+		m.Post("/telegram/{id}", web.Bind(forms.NewTelegramHookForm{}), repo_setting.TelegramHooksEditPost)
+		m.Post("/matrix/{id}", web.Bind(forms.NewMatrixHookForm{}), repo_setting.MatrixHooksEditPost)
+		m.Post("/msteams/{id}", web.Bind(forms.NewMSTeamsHookForm{}), repo_setting.MSTeamsHooksEditPost)
+		m.Post("/feishu/{id}", web.Bind(forms.NewFeishuHookForm{}), repo_setting.FeishuHooksEditPost)
+		m.Post("/wechatwork/{id}", web.Bind(forms.NewWechatWorkHookForm{}), repo_setting.WechatworkHooksEditPost)
+		m.Post("/packagist/{id}", web.Bind(forms.NewPackagistHookForm{}), repo_setting.PackagistHooksEditPost)
 	}
 
 	addSettingVariablesRoutes := func() {
@@ -515,8 +515,8 @@ func registerRoutes(m *web.Route) {
 			m.Post("/delete", user_setting.DeleteWebhook)
 			addWebhookAddRoutes()
 			m.Group("/{id}", func() {
-				m.Get("", repo.WebHooksEdit)
-				m.Post("/replay/{uuid}", repo.ReplayWebhook)
+				m.Get("", repo_setting.WebHooksEdit)
+				m.Post("/replay/{uuid}", repo_setting.ReplayWebhook)
 			})
 			addWebhookEditRoutes()
 		}, webhooksEnabled)
@@ -604,8 +604,8 @@ func registerRoutes(m *web.Route) {
 			m.Get("", admin.DefaultOrSystemWebhooks)
 			m.Post("/delete", admin.DeleteDefaultOrSystemWebhook)
 			m.Group("/{id}", func() {
-				m.Get("", repo.WebHooksEdit)
-				m.Post("/replay/{uuid}", repo.ReplayWebhook)
+				m.Get("", repo_setting.WebHooksEdit)
+				m.Post("/replay/{uuid}", repo_setting.ReplayWebhook)
 			})
 			addWebhookEditRoutes()
 		}, webhooksEnabled)
@@ -752,8 +752,8 @@ func registerRoutes(m *web.Route) {
 					m.Post("/delete", org.DeleteWebhook)
 					addWebhookAddRoutes()
 					m.Group("/{id}", func() {
-						m.Get("", repo.WebHooksEdit)
-						m.Post("/replay/{uuid}", repo.ReplayWebhook)
+						m.Get("", repo_setting.WebHooksEdit)
+						m.Post("/replay/{uuid}", repo_setting.ReplayWebhook)
 					})
 					addWebhookEditRoutes()
 				}, webhooksEnabled)
@@ -874,78 +874,78 @@ func registerRoutes(m *web.Route) {
 	m.Group("/{username}/{reponame}", func() {
 		m.Group("/settings", func() {
 			m.Group("", func() {
-				m.Combo("").Get(repo.Settings).
-					Post(web.Bind(forms.RepoSettingForm{}), repo.SettingsPost)
-			}, repo.SettingsCtxData)
-			m.Post("/avatar", web.Bind(forms.AvatarForm{}), repo.SettingsAvatar)
-			m.Post("/avatar/delete", repo.SettingsDeleteAvatar)
+				m.Combo("").Get(repo_setting.Settings).
+					Post(web.Bind(forms.RepoSettingForm{}), repo_setting.SettingsPost)
+			}, repo_setting.SettingsCtxData)
+			m.Post("/avatar", web.Bind(forms.AvatarForm{}), repo_setting.SettingsAvatar)
+			m.Post("/avatar/delete", repo_setting.SettingsDeleteAvatar)
 
 			m.Group("/collaboration", func() {
-				m.Combo("").Get(repo.Collaboration).Post(repo.CollaborationPost)
-				m.Post("/access_mode", repo.ChangeCollaborationAccessMode)
-				m.Post("/delete", repo.DeleteCollaboration)
+				m.Combo("").Get(repo_setting.Collaboration).Post(repo_setting.CollaborationPost)
+				m.Post("/access_mode", repo_setting.ChangeCollaborationAccessMode)
+				m.Post("/delete", repo_setting.DeleteCollaboration)
 				m.Group("/team", func() {
-					m.Post("", repo.AddTeamPost)
-					m.Post("/delete", repo.DeleteTeam)
+					m.Post("", repo_setting.AddTeamPost)
+					m.Post("/delete", repo_setting.DeleteTeam)
 				})
 			})
 
 			m.Group("/branches", func() {
-				m.Post("/", repo.SetDefaultBranchPost)
+				m.Post("/", repo_setting.SetDefaultBranchPost)
 			}, repo.MustBeNotEmpty)
 
 			m.Group("/branches", func() {
-				m.Get("/", repo.ProtectedBranchRules)
-				m.Combo("/edit").Get(repo.SettingsProtectedBranch).
-					Post(web.Bind(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo.SettingsProtectedBranchPost)
-				m.Post("/{id}/delete", repo.DeleteProtectedBranchRulePost)
+				m.Get("/", repo_setting.ProtectedBranchRules)
+				m.Combo("/edit").Get(repo_setting.SettingsProtectedBranch).
+					Post(web.Bind(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.SettingsProtectedBranchPost)
+				m.Post("/{id}/delete", repo_setting.DeleteProtectedBranchRulePost)
 			}, repo.MustBeNotEmpty)
 
-			m.Post("/rename_branch", web.Bind(forms.RenameBranchForm{}), context.RepoMustNotBeArchived(), repo.RenameBranchPost)
+			m.Post("/rename_branch", web.Bind(forms.RenameBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.RenameBranchPost)
 
 			m.Group("/tags", func() {
-				m.Get("", repo.Tags)
-				m.Post("", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo.NewProtectedTagPost)
-				m.Post("/delete", context.RepoMustNotBeArchived(), repo.DeleteProtectedTagPost)
-				m.Get("/{id}", repo.EditProtectedTag)
-				m.Post("/{id}", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo.EditProtectedTagPost)
+				m.Get("", repo_setting.ProtectedTags)
+				m.Post("", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo_setting.NewProtectedTagPost)
+				m.Post("/delete", context.RepoMustNotBeArchived(), repo_setting.DeleteProtectedTagPost)
+				m.Get("/{id}", repo_setting.EditProtectedTag)
+				m.Post("/{id}", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo_setting.EditProtectedTagPost)
 			})
 
 			m.Group("/hooks/git", func() {
-				m.Get("", repo.GitHooks)
-				m.Combo("/{name}").Get(repo.GitHooksEdit).
-					Post(repo.GitHooksEditPost)
+				m.Get("", repo_setting.GitHooks)
+				m.Combo("/{name}").Get(repo_setting.GitHooksEdit).
+					Post(repo_setting.GitHooksEditPost)
 			}, context.GitHookService())
 
 			m.Group("/hooks", func() {
-				m.Get("", repo.Webhooks)
-				m.Post("/delete", repo.DeleteWebhook)
+				m.Get("", repo_setting.Webhooks)
+				m.Post("/delete", repo_setting.DeleteWebhook)
 				addWebhookAddRoutes()
 				m.Group("/{id}", func() {
-					m.Get("", repo.WebHooksEdit)
-					m.Post("/test", repo.TestWebhook)
-					m.Post("/replay/{uuid}", repo.ReplayWebhook)
+					m.Get("", repo_setting.WebHooksEdit)
+					m.Post("/test", repo_setting.TestWebhook)
+					m.Post("/replay/{uuid}", repo_setting.ReplayWebhook)
 				})
 				addWebhookEditRoutes()
 			}, webhooksEnabled)
 
 			m.Group("/keys", func() {
-				m.Combo("").Get(repo.DeployKeys).
-					Post(web.Bind(forms.AddKeyForm{}), repo.DeployKeysPost)
-				m.Post("/delete", repo.DeleteDeployKey)
+				m.Combo("").Get(repo_setting.DeployKeys).
+					Post(web.Bind(forms.AddKeyForm{}), repo_setting.DeployKeysPost)
+				m.Post("/delete", repo_setting.DeleteDeployKey)
 			})
 
 			m.Group("/lfs", func() {
-				m.Get("/", repo.LFSFiles)
-				m.Get("/show/{oid}", repo.LFSFileGet)
-				m.Post("/delete/{oid}", repo.LFSDelete)
-				m.Get("/pointers", repo.LFSPointerFiles)
-				m.Post("/pointers/associate", repo.LFSAutoAssociate)
-				m.Get("/find", repo.LFSFileFind)
+				m.Get("/", repo_setting.LFSFiles)
+				m.Get("/show/{oid}", repo_setting.LFSFileGet)
+				m.Post("/delete/{oid}", repo_setting.LFSDelete)
+				m.Get("/pointers", repo_setting.LFSPointerFiles)
+				m.Post("/pointers/associate", repo_setting.LFSAutoAssociate)
+				m.Get("/find", repo_setting.LFSFileFind)
 				m.Group("/locks", func() {
-					m.Get("/", repo.LFSLocks)
-					m.Post("/", repo.LFSLockFile)
-					m.Post("/{lid}/unlock", repo.LFSUnlock)
+					m.Get("/", repo_setting.LFSLocks)
+					m.Post("/", repo_setting.LFSLockFile)
+					m.Post("/{lid}/unlock", repo_setting.LFSUnlock)
 				})
 			})
 			m.Group("/actions", func() {