From 33fca2b537d36cf998dd27425b2bb8ed5b0965f3 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Wed, 10 Nov 2021 13:13:16 +0800
Subject: [PATCH] Move webhook into models/webhook/ (#17579)

---
 integrations/pull_merge_test.go          |  17 +-
 models/db/error.go                       |  29 +++
 models/error.go                          |  44 ----
 models/repo.go                           |  15 +-
 models/repo_avatar.go                    |   2 +-
 models/repo_generate.go                  |   7 +-
 models/statistic.go                      |   5 +-
 models/user.go                           |   2 +-
 models/webhook/hooktask.go               | 280 ++++++++++++++++++++
 models/webhook/main_test.go              |  16 ++
 models/{ => webhook}/webhook.go          | 310 +++--------------------
 models/{ => webhook}/webhook_test.go     |   4 +-
 modules/convert/convert.go               |   9 +-
 modules/cron/tasks.go                    |   5 +-
 modules/cron/tasks_basic.go              |   3 +-
 modules/migrations/update.go             |   3 +-
 modules/notification/webhook/webhook.go  |  79 +++---
 modules/repository/check.go              |  10 +-
 modules/repository/hooks.go              |   2 +-
 routers/api/v1/org/hook.go               |  12 +-
 routers/api/v1/repo/hook.go              |  15 +-
 routers/api/v1/repo/hook_test.go         |   4 +-
 routers/api/v1/utils/hook.go             | 109 ++++----
 routers/web/admin/hooks.go               |   8 +-
 routers/web/org/setting.go               |   5 +-
 routers/web/repo/webhook.go              | 228 ++++++++---------
 services/auth/source/ldap/source_sync.go |   7 +-
 services/auth/sync.go                    |   4 +-
 services/webhook/deliver.go              |  29 ++-
 services/webhook/dingtalk.go             |   6 +-
 services/webhook/dingtalk_test.go        |   4 +-
 services/webhook/discord.go              |  22 +-
 services/webhook/discord_test.go         |   4 +-
 services/webhook/feishu.go               |   6 +-
 services/webhook/feishu_test.go          |   4 +-
 services/webhook/matrix.go               |  10 +-
 services/webhook/matrix_test.go          |   8 +-
 services/webhook/msteams.go              |  12 +-
 services/webhook/msteams_test.go         |   4 +-
 services/webhook/payloader.go            |  28 +-
 services/webhook/slack.go                |   8 +-
 services/webhook/slack_test.go           |   4 +-
 services/webhook/telegram.go             |   8 +-
 services/webhook/telegram_test.go        |   4 +-
 services/webhook/webhook.go              |  65 ++---
 services/webhook/webhook_test.go         |  21 +-
 services/webhook/wechatwork.go           |   6 +-
 47 files changed, 770 insertions(+), 717 deletions(-)
 create mode 100644 models/db/error.go
 create mode 100644 models/webhook/hooktask.go
 create mode 100644 models/webhook/main_test.go
 rename models/{ => webhook}/webhook.go (63%)
 rename models/{ => webhook}/webhook_test.go (99%)

diff --git a/integrations/pull_merge_test.go b/integrations/pull_merge_test.go
index 867817c2c2..9ccc4043a0 100644
--- a/integrations/pull_merge_test.go
+++ b/integrations/pull_merge_test.go
@@ -18,6 +18,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/test"
@@ -62,7 +63,7 @@ func testPullCleanUp(t *testing.T, session *TestSession, user, repo, pullnum str
 
 func TestPullMerge(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
-		hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
+		hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number
 		assert.NoError(t, err)
 		hookTasksLenBefore := len(hookTasks)
 
@@ -76,7 +77,7 @@ func TestPullMerge(t *testing.T) {
 		assert.EqualValues(t, "pulls", elem[3])
 		testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge)
 
-		hookTasks, err = models.HookTasks(1, 1)
+		hookTasks, err = webhook.HookTasks(1, 1)
 		assert.NoError(t, err)
 		assert.Len(t, hookTasks, hookTasksLenBefore+1)
 	})
@@ -84,7 +85,7 @@ func TestPullMerge(t *testing.T) {
 
 func TestPullRebase(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
-		hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
+		hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number
 		assert.NoError(t, err)
 		hookTasksLenBefore := len(hookTasks)
 
@@ -98,7 +99,7 @@ func TestPullRebase(t *testing.T) {
 		assert.EqualValues(t, "pulls", elem[3])
 		testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleRebase)
 
-		hookTasks, err = models.HookTasks(1, 1)
+		hookTasks, err = webhook.HookTasks(1, 1)
 		assert.NoError(t, err)
 		assert.Len(t, hookTasks, hookTasksLenBefore+1)
 	})
@@ -106,7 +107,7 @@ func TestPullRebase(t *testing.T) {
 
 func TestPullRebaseMerge(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
-		hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
+		hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number
 		assert.NoError(t, err)
 		hookTasksLenBefore := len(hookTasks)
 
@@ -120,7 +121,7 @@ func TestPullRebaseMerge(t *testing.T) {
 		assert.EqualValues(t, "pulls", elem[3])
 		testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleRebaseMerge)
 
-		hookTasks, err = models.HookTasks(1, 1)
+		hookTasks, err = webhook.HookTasks(1, 1)
 		assert.NoError(t, err)
 		assert.Len(t, hookTasks, hookTasksLenBefore+1)
 	})
@@ -128,7 +129,7 @@ func TestPullRebaseMerge(t *testing.T) {
 
 func TestPullSquash(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
-		hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
+		hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number
 		assert.NoError(t, err)
 		hookTasksLenBefore := len(hookTasks)
 
@@ -143,7 +144,7 @@ func TestPullSquash(t *testing.T) {
 		assert.EqualValues(t, "pulls", elem[3])
 		testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleSquash)
 
-		hookTasks, err = models.HookTasks(1, 1)
+		hookTasks, err = webhook.HookTasks(1, 1)
 		assert.NoError(t, err)
 		assert.Len(t, hookTasks, hookTasksLenBefore+1)
 	})
diff --git a/models/db/error.go b/models/db/error.go
new file mode 100644
index 0000000000..675247ed87
--- /dev/null
+++ b/models/db/error.go
@@ -0,0 +1,29 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package db
+
+import "fmt"
+
+// ErrCancelled represents an error due to context cancellation
+type ErrCancelled struct {
+	Message string
+}
+
+// IsErrCancelled checks if an error is a ErrCancelled.
+func IsErrCancelled(err error) bool {
+	_, ok := err.(ErrCancelled)
+	return ok
+}
+
+func (err ErrCancelled) Error() string {
+	return "Cancelled: " + err.Message
+}
+
+// ErrCancelledf returns an ErrCancelled for the provided format and args
+func ErrCancelledf(format string, args ...interface{}) error {
+	return ErrCancelled{
+		fmt.Sprintf(format, args...),
+	}
+}
diff --git a/models/error.go b/models/error.go
index 1179fa6eb7..b365a67b73 100644
--- a/models/error.go
+++ b/models/error.go
@@ -84,28 +84,6 @@ func (err ErrSSHDisabled) Error() string {
 	return "SSH is disabled"
 }
 
-// ErrCancelled represents an error due to context cancellation
-type ErrCancelled struct {
-	Message string
-}
-
-// IsErrCancelled checks if an error is a ErrCancelled.
-func IsErrCancelled(err error) bool {
-	_, ok := err.(ErrCancelled)
-	return ok
-}
-
-func (err ErrCancelled) Error() string {
-	return "Cancelled: " + err.Message
-}
-
-// ErrCancelledf returns an ErrCancelled for the provided format and args
-func ErrCancelledf(format string, args ...interface{}) error {
-	return ErrCancelled{
-		fmt.Sprintf(format, args...),
-	}
-}
-
 //  ____ ___
 // |    |   \______ ___________
 // |    |   /  ___// __ \_  __ \
@@ -1309,28 +1287,6 @@ func (err ErrSHAOrCommitIDNotProvided) Error() string {
 	return "a SHA or commit ID must be proved when updating a file"
 }
 
-//  __      __      ___.   .__                   __
-// /  \    /  \ ____\_ |__ |  |__   ____   ____ |  | __
-// \   \/\/   // __ \| __ \|  |  \ /  _ \ /  _ \|  |/ /
-//  \        /\  ___/| \_\ \   Y  (  <_> |  <_> )    <
-//   \__/\  /  \___  >___  /___|  /\____/ \____/|__|_ \
-//        \/       \/    \/     \/                   \/
-
-// ErrWebhookNotExist represents a "WebhookNotExist" kind of error.
-type ErrWebhookNotExist struct {
-	ID int64
-}
-
-// IsErrWebhookNotExist checks if an error is a ErrWebhookNotExist.
-func IsErrWebhookNotExist(err error) bool {
-	_, ok := err.(ErrWebhookNotExist)
-	return ok
-}
-
-func (err ErrWebhookNotExist) Error() string {
-	return fmt.Sprintf("webhook does not exist [id: %d]", err.ID)
-}
-
 // .___
 // |   | ______ ________ __   ____
 // |   |/  ___//  ___/  |  \_/ __ \
diff --git a/models/repo.go b/models/repo.go
index 10dc8d9b09..f44fc763a5 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -24,6 +24,7 @@ import (
 
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/unit"
+	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/lfs"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/markup"
@@ -1153,7 +1154,7 @@ func CreateRepository(ctx context.Context, doer, u *User, repo *Repository, over
 		}
 	}
 
-	if err = copyDefaultWebhooksToRepo(db.GetEngine(ctx), repo.ID); err != nil {
+	if err = webhook.CopyDefaultWebhooksToRepo(ctx, repo.ID); err != nil {
 		return fmt.Errorf("copyDefaultWebhooksToRepo: %v", err)
 	}
 
@@ -1509,7 +1510,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
 		&Comment{RefRepoID: repoID},
 		&CommitStatus{RepoID: repoID},
 		&DeletedBranch{RepoID: repoID},
-		&HookTask{RepoID: repoID},
+		&webhook.HookTask{RepoID: repoID},
 		&LFSLock{RepoID: repoID},
 		&LanguageStat{RepoID: repoID},
 		&Milestone{RepoID: repoID},
@@ -1526,7 +1527,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
 		&Star{RepoID: repoID},
 		&Task{RepoID: repoID},
 		&Watch{RepoID: repoID},
-		&Webhook{RepoID: repoID},
+		&webhook.Webhook{RepoID: repoID},
 	); err != nil {
 		return fmt.Errorf("deleteBeans: %v", err)
 	}
@@ -1932,7 +1933,7 @@ func CheckRepoStats(ctx context.Context) error {
 		select {
 		case <-ctx.Done():
 			log.Warn("CheckRepoStats: Cancelled before %s", checker.desc)
-			return ErrCancelledf("before checking %s", checker.desc)
+			return db.ErrCancelledf("before checking %s", checker.desc)
 		default:
 			repoStatsCheck(ctx, checker)
 		}
@@ -1949,7 +1950,7 @@ func CheckRepoStats(ctx context.Context) error {
 			select {
 			case <-ctx.Done():
 				log.Warn("CheckRepoStats: Cancelled during %s for repo ID %d", desc, id)
-				return ErrCancelledf("during %s for repo ID %d", desc, id)
+				return db.ErrCancelledf("during %s for repo ID %d", desc, id)
 			default:
 			}
 			log.Trace("Updating %s: %d", desc, id)
@@ -1972,7 +1973,7 @@ func CheckRepoStats(ctx context.Context) error {
 			select {
 			case <-ctx.Done():
 				log.Warn("CheckRepoStats: Cancelled")
-				return ErrCancelledf("during %s for repo ID %d", desc, id)
+				return db.ErrCancelledf("during %s for repo ID %d", desc, id)
 			default:
 			}
 			log.Trace("Updating %s: %d", desc, id)
@@ -1995,7 +1996,7 @@ func CheckRepoStats(ctx context.Context) error {
 			select {
 			case <-ctx.Done():
 				log.Warn("CheckRepoStats: Cancelled")
-				return ErrCancelledf("during %s for repo ID %d", desc, id)
+				return db.ErrCancelledf("during %s for repo ID %d", desc, id)
 			default:
 			}
 			log.Trace("Updating repository count 'num_forks': %d", id)
diff --git a/models/repo_avatar.go b/models/repo_avatar.go
index bb5f083dd5..6c5e03c0d0 100644
--- a/models/repo_avatar.go
+++ b/models/repo_avatar.go
@@ -64,7 +64,7 @@ func RemoveRandomAvatars(ctx context.Context) error {
 				repository := bean.(*Repository)
 				select {
 				case <-ctx.Done():
-					return ErrCancelledf("before random avatars removed for %s", repository.FullName())
+					return db.ErrCancelledf("before random avatars removed for %s", repository.FullName())
 				default:
 				}
 				stringifiedID := strconv.FormatInt(repository.ID, 10)
diff --git a/models/repo_generate.go b/models/repo_generate.go
index 650da711a3..cef5fa7928 100644
--- a/models/repo_generate.go
+++ b/models/repo_generate.go
@@ -12,6 +12,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/storage"
@@ -113,13 +114,13 @@ func GenerateGitHooks(ctx context.Context, templateRepo, generateRepo *Repositor
 
 // GenerateWebhooks generates webhooks from a template repository
 func GenerateWebhooks(ctx context.Context, templateRepo, generateRepo *Repository) error {
-	templateWebhooks, err := ListWebhooksByOpts(&ListWebhookOptions{RepoID: templateRepo.ID})
+	templateWebhooks, err := webhook.ListWebhooksByOpts(&webhook.ListWebhookOptions{RepoID: templateRepo.ID})
 	if err != nil {
 		return err
 	}
 
 	for _, templateWebhook := range templateWebhooks {
-		generateWebhook := &Webhook{
+		generateWebhook := &webhook.Webhook{
 			RepoID:      generateRepo.ID,
 			URL:         templateWebhook.URL,
 			HTTPMethod:  templateWebhook.HTTPMethod,
@@ -132,7 +133,7 @@ func GenerateWebhooks(ctx context.Context, templateRepo, generateRepo *Repositor
 			Events:      templateWebhook.Events,
 			Meta:        templateWebhook.Meta,
 		}
-		if err := createWebhook(db.GetEngine(ctx), generateWebhook); err != nil {
+		if err := webhook.CreateWebhook(ctx, generateWebhook); err != nil {
 			return err
 		}
 	}
diff --git a/models/statistic.go b/models/statistic.go
index 5e72dc713d..fab35e62dc 100644
--- a/models/statistic.go
+++ b/models/statistic.go
@@ -7,6 +7,7 @@ package models
 import (
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/login"
+	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/setting"
 )
 
@@ -95,10 +96,10 @@ func GetStatistic() (stats Statistic) {
 	stats.Counter.Mirror, _ = e.Count(new(Mirror))
 	stats.Counter.Release, _ = e.Count(new(Release))
 	stats.Counter.LoginSource = login.CountSources()
-	stats.Counter.Webhook, _ = e.Count(new(Webhook))
+	stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook))
 	stats.Counter.Milestone, _ = e.Count(new(Milestone))
 	stats.Counter.Label, _ = e.Count(new(Label))
-	stats.Counter.HookTask, _ = e.Count(new(HookTask))
+	stats.Counter.HookTask, _ = e.Count(new(webhook.HookTask))
 	stats.Counter.Team, _ = e.Count(new(Team))
 	stats.Counter.Attachment, _ = e.Count(new(Attachment))
 	stats.Counter.Project, _ = e.Count(new(Project))
diff --git a/models/user.go b/models/user.go
index d27c581bab..13347a46b7 100644
--- a/models/user.go
+++ b/models/user.go
@@ -1356,7 +1356,7 @@ func DeleteInactiveUsers(ctx context.Context, olderThan time.Duration) (err erro
 	for _, u := range users {
 		select {
 		case <-ctx.Done():
-			return ErrCancelledf("Before delete inactive user %s", u.Name)
+			return db.ErrCancelledf("Before delete inactive user %s", u.Name)
 		default:
 		}
 		if err = DeleteUser(u); err != nil {
diff --git a/models/webhook/hooktask.go b/models/webhook/hooktask.go
new file mode 100644
index 0000000000..1967ded298
--- /dev/null
+++ b/models/webhook/hooktask.go
@@ -0,0 +1,280 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package webhook
+
+import (
+	"context"
+	"time"
+
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/modules/json"
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
+	api "code.gitea.io/gitea/modules/structs"
+
+	gouuid "github.com/google/uuid"
+)
+
+//   ___ ___                __   ___________              __
+//  /   |   \  ____   ____ |  | _\__    ___/____    _____|  | __
+// /    ~    \/  _ \ /  _ \|  |/ / |    |  \__  \  /  ___/  |/ /
+// \    Y    (  <_> |  <_> )    <  |    |   / __ \_\___ \|    <
+//  \___|_  / \____/ \____/|__|_ \ |____|  (____  /____  >__|_ \
+//        \/                    \/              \/     \/     \/
+
+// HookEventType is the type of an hook event
+type HookEventType string
+
+// Types of hook events
+const (
+	HookEventCreate                    HookEventType = "create"
+	HookEventDelete                    HookEventType = "delete"
+	HookEventFork                      HookEventType = "fork"
+	HookEventPush                      HookEventType = "push"
+	HookEventIssues                    HookEventType = "issues"
+	HookEventIssueAssign               HookEventType = "issue_assign"
+	HookEventIssueLabel                HookEventType = "issue_label"
+	HookEventIssueMilestone            HookEventType = "issue_milestone"
+	HookEventIssueComment              HookEventType = "issue_comment"
+	HookEventPullRequest               HookEventType = "pull_request"
+	HookEventPullRequestAssign         HookEventType = "pull_request_assign"
+	HookEventPullRequestLabel          HookEventType = "pull_request_label"
+	HookEventPullRequestMilestone      HookEventType = "pull_request_milestone"
+	HookEventPullRequestComment        HookEventType = "pull_request_comment"
+	HookEventPullRequestReviewApproved HookEventType = "pull_request_review_approved"
+	HookEventPullRequestReviewRejected HookEventType = "pull_request_review_rejected"
+	HookEventPullRequestReviewComment  HookEventType = "pull_request_review_comment"
+	HookEventPullRequestSync           HookEventType = "pull_request_sync"
+	HookEventRepository                HookEventType = "repository"
+	HookEventRelease                   HookEventType = "release"
+)
+
+// Event returns the HookEventType as an event string
+func (h HookEventType) Event() string {
+	switch h {
+	case HookEventCreate:
+		return "create"
+	case HookEventDelete:
+		return "delete"
+	case HookEventFork:
+		return "fork"
+	case HookEventPush:
+		return "push"
+	case HookEventIssues, HookEventIssueAssign, HookEventIssueLabel, HookEventIssueMilestone:
+		return "issues"
+	case HookEventPullRequest, HookEventPullRequestAssign, HookEventPullRequestLabel, HookEventPullRequestMilestone,
+		HookEventPullRequestSync:
+		return "pull_request"
+	case HookEventIssueComment, HookEventPullRequestComment:
+		return "issue_comment"
+	case HookEventPullRequestReviewApproved:
+		return "pull_request_approved"
+	case HookEventPullRequestReviewRejected:
+		return "pull_request_rejected"
+	case HookEventPullRequestReviewComment:
+		return "pull_request_comment"
+	case HookEventRepository:
+		return "repository"
+	case HookEventRelease:
+		return "release"
+	}
+	return ""
+}
+
+// HookRequest represents hook task request information.
+type HookRequest struct {
+	URL        string            `json:"url"`
+	HTTPMethod string            `json:"http_method"`
+	Headers    map[string]string `json:"headers"`
+}
+
+// HookResponse represents hook task response information.
+type HookResponse struct {
+	Status  int               `json:"status"`
+	Headers map[string]string `json:"headers"`
+	Body    string            `json:"body"`
+}
+
+// HookTask represents a hook task.
+type HookTask struct {
+	ID              int64 `xorm:"pk autoincr"`
+	RepoID          int64 `xorm:"INDEX"`
+	HookID          int64
+	UUID            string
+	api.Payloader   `xorm:"-"`
+	PayloadContent  string `xorm:"TEXT"`
+	EventType       HookEventType
+	IsDelivered     bool
+	Delivered       int64
+	DeliveredString string `xorm:"-"`
+
+	// History info.
+	IsSucceed       bool
+	RequestContent  string        `xorm:"TEXT"`
+	RequestInfo     *HookRequest  `xorm:"-"`
+	ResponseContent string        `xorm:"TEXT"`
+	ResponseInfo    *HookResponse `xorm:"-"`
+}
+
+func init() {
+	db.RegisterModel(new(HookTask))
+}
+
+// BeforeUpdate will be invoked by XORM before updating a record
+// representing this object
+func (t *HookTask) BeforeUpdate() {
+	if t.RequestInfo != nil {
+		t.RequestContent = t.simpleMarshalJSON(t.RequestInfo)
+	}
+	if t.ResponseInfo != nil {
+		t.ResponseContent = t.simpleMarshalJSON(t.ResponseInfo)
+	}
+}
+
+// AfterLoad updates the webhook object upon setting a column
+func (t *HookTask) AfterLoad() {
+	t.DeliveredString = time.Unix(0, t.Delivered).Format("2006-01-02 15:04:05 MST")
+
+	if len(t.RequestContent) == 0 {
+		return
+	}
+
+	t.RequestInfo = &HookRequest{}
+	if err := json.Unmarshal([]byte(t.RequestContent), t.RequestInfo); err != nil {
+		log.Error("Unmarshal RequestContent[%d]: %v", t.ID, err)
+	}
+
+	if len(t.ResponseContent) > 0 {
+		t.ResponseInfo = &HookResponse{}
+		if err := json.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil {
+			log.Error("Unmarshal ResponseContent[%d]: %v", t.ID, err)
+		}
+	}
+}
+
+func (t *HookTask) simpleMarshalJSON(v interface{}) string {
+	p, err := json.Marshal(v)
+	if err != nil {
+		log.Error("Marshal [%d]: %v", t.ID, err)
+	}
+	return string(p)
+}
+
+// HookTasks returns a list of hook tasks by given conditions.
+func HookTasks(hookID int64, page int) ([]*HookTask, error) {
+	tasks := make([]*HookTask, 0, setting.Webhook.PagingNum)
+	return tasks, db.GetEngine(db.DefaultContext).
+		Limit(setting.Webhook.PagingNum, (page-1)*setting.Webhook.PagingNum).
+		Where("hook_id=?", hookID).
+		Desc("id").
+		Find(&tasks)
+}
+
+// CreateHookTask creates a new hook task,
+// it handles conversion from Payload to PayloadContent.
+func CreateHookTask(t *HookTask) error {
+	return createHookTask(db.GetEngine(db.DefaultContext), t)
+}
+
+func createHookTask(e db.Engine, t *HookTask) error {
+	data, err := t.Payloader.JSONPayload()
+	if err != nil {
+		return err
+	}
+	t.UUID = gouuid.New().String()
+	t.PayloadContent = string(data)
+	_, err = e.Insert(t)
+	return err
+}
+
+// UpdateHookTask updates information of hook task.
+func UpdateHookTask(t *HookTask) error {
+	_, err := db.GetEngine(db.DefaultContext).ID(t.ID).AllCols().Update(t)
+	return err
+}
+
+// FindUndeliveredHookTasks represents find the undelivered hook tasks
+func FindUndeliveredHookTasks() ([]*HookTask, error) {
+	tasks := make([]*HookTask, 0, 10)
+	if err := db.GetEngine(db.DefaultContext).Where("is_delivered=?", false).Find(&tasks); err != nil {
+		return nil, err
+	}
+	return tasks, nil
+}
+
+// FindRepoUndeliveredHookTasks represents find the undelivered hook tasks of one repository
+func FindRepoUndeliveredHookTasks(repoID int64) ([]*HookTask, error) {
+	tasks := make([]*HookTask, 0, 5)
+	if err := db.GetEngine(db.DefaultContext).Where("repo_id=? AND is_delivered=?", repoID, false).Find(&tasks); err != nil {
+		return nil, err
+	}
+	return tasks, nil
+}
+
+// CleanupHookTaskTable deletes rows from hook_task as needed.
+func CleanupHookTaskTable(ctx context.Context, cleanupType HookTaskCleanupType, olderThan time.Duration, numberToKeep int) error {
+	log.Trace("Doing: CleanupHookTaskTable")
+
+	if cleanupType == OlderThan {
+		deleteOlderThan := time.Now().Add(-olderThan).UnixNano()
+		deletes, err := db.GetEngine(db.DefaultContext).
+			Where("is_delivered = ? and delivered < ?", true, deleteOlderThan).
+			Delete(new(HookTask))
+		if err != nil {
+			return err
+		}
+		log.Trace("Deleted %d rows from hook_task", deletes)
+	} else if cleanupType == PerWebhook {
+		hookIDs := make([]int64, 0, 10)
+		err := db.GetEngine(db.DefaultContext).Table("webhook").
+			Where("id > 0").
+			Cols("id").
+			Find(&hookIDs)
+		if err != nil {
+			return err
+		}
+		for _, hookID := range hookIDs {
+			select {
+			case <-ctx.Done():
+				return db.ErrCancelledf("Before deleting hook_task records for hook id %d", hookID)
+			default:
+			}
+			if err = deleteDeliveredHookTasksByWebhook(hookID, numberToKeep); err != nil {
+				return err
+			}
+		}
+	}
+	log.Trace("Finished: CleanupHookTaskTable")
+	return nil
+}
+
+func deleteDeliveredHookTasksByWebhook(hookID int64, numberDeliveriesToKeep int) error {
+	log.Trace("Deleting hook_task rows for webhook %d, keeping the most recent %d deliveries", hookID, numberDeliveriesToKeep)
+	deliveryDates := make([]int64, 0, 10)
+	err := db.GetEngine(db.DefaultContext).Table("hook_task").
+		Where("hook_task.hook_id = ? AND hook_task.is_delivered = ? AND hook_task.delivered is not null", hookID, true).
+		Cols("hook_task.delivered").
+		Join("INNER", "webhook", "hook_task.hook_id = webhook.id").
+		OrderBy("hook_task.delivered desc").
+		Limit(1, int(numberDeliveriesToKeep)).
+		Find(&deliveryDates)
+	if err != nil {
+		return err
+	}
+
+	if len(deliveryDates) > 0 {
+		deletes, err := db.GetEngine(db.DefaultContext).
+			Where("hook_id = ? and is_delivered = ? and delivered <= ?", hookID, true, deliveryDates[0]).
+			Delete(new(HookTask))
+		if err != nil {
+			return err
+		}
+		log.Trace("Deleted %d hook_task rows for webhook %d", deletes, hookID)
+	} else {
+		log.Trace("No hook_task rows to delete for webhook %d", hookID)
+	}
+
+	return nil
+}
diff --git a/models/webhook/main_test.go b/models/webhook/main_test.go
new file mode 100644
index 0000000000..f94612a755
--- /dev/null
+++ b/models/webhook/main_test.go
@@ -0,0 +1,16 @@
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package webhook
+
+import (
+	"path/filepath"
+	"testing"
+
+	"code.gitea.io/gitea/models/db"
+)
+
+func TestMain(m *testing.M) {
+	db.MainTest(m, filepath.Join("..", ".."), "webhook.yml", "hook_task.yml")
+}
diff --git a/models/webhook.go b/models/webhook/webhook.go
similarity index 63%
rename from models/webhook.go
rename to models/webhook/webhook.go
index 9d04f8f5e4..de8bd5e338 100644
--- a/models/webhook.go
+++ b/models/webhook/webhook.go
@@ -3,26 +3,44 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package models
+package webhook
 
 import (
 	"context"
 	"fmt"
 	"strings"
-	"time"
 
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
-	"code.gitea.io/gitea/modules/setting"
-	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/timeutil"
 	"code.gitea.io/gitea/modules/util"
 
-	gouuid "github.com/google/uuid"
 	"xorm.io/builder"
 )
 
+//  __      __      ___.   .__                   __
+// /  \    /  \ ____\_ |__ |  |__   ____   ____ |  | __
+// \   \/\/   // __ \| __ \|  |  \ /  _ \ /  _ \|  |/ /
+//  \        /\  ___/| \_\ \   Y  (  <_> |  <_> )    <
+//   \__/\  /  \___  >___  /___|  /\____/ \____/|__|_ \
+//        \/       \/    \/     \/                   \/
+
+// ErrWebhookNotExist represents a "WebhookNotExist" kind of error.
+type ErrWebhookNotExist struct {
+	ID int64
+}
+
+// IsErrWebhookNotExist checks if an error is a ErrWebhookNotExist.
+func IsErrWebhookNotExist(err error) bool {
+	_, ok := err.(ErrWebhookNotExist)
+	return ok
+}
+
+func (err ErrWebhookNotExist) Error() string {
+	return fmt.Sprintf("webhook does not exist [id: %d]", err.ID)
+}
+
 // HookContentType is the content type of a web hook
 type HookContentType int
 
@@ -162,7 +180,6 @@ type Webhook struct {
 
 func init() {
 	db.RegisterModel(new(Webhook))
-	db.RegisterModel(new(HookTask))
 }
 
 // AfterLoad updates the webhook object upon setting a column
@@ -350,14 +367,9 @@ func (w *Webhook) EventsArray() []string {
 }
 
 // CreateWebhook creates a new web hook.
-func CreateWebhook(w *Webhook) error {
-	return createWebhook(db.GetEngine(db.DefaultContext), w)
-}
-
-func createWebhook(e db.Engine, w *Webhook) error {
+func CreateWebhook(ctx context.Context, w *Webhook) error {
 	w.Type = strings.TrimSpace(w.Type)
-	_, err := e.Insert(w)
-	return err
+	return db.Insert(ctx, w)
 }
 
 // getWebhook uses argument bean as query condition,
@@ -444,12 +456,12 @@ func CountWebhooksByOpts(opts *ListWebhookOptions) (int64, error) {
 
 // GetDefaultWebhooks returns all admin-default webhooks.
 func GetDefaultWebhooks() ([]*Webhook, error) {
-	return getDefaultWebhooks(db.GetEngine(db.DefaultContext))
+	return getDefaultWebhooks(db.DefaultContext)
 }
 
-func getDefaultWebhooks(e db.Engine) ([]*Webhook, error) {
+func getDefaultWebhooks(ctx context.Context) ([]*Webhook, error) {
 	webhooks := make([]*Webhook, 0, 5)
-	return webhooks, e.
+	return webhooks, db.GetEngine(ctx).
 		Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, false).
 		Find(&webhooks)
 }
@@ -552,9 +564,9 @@ func DeleteDefaultSystemWebhook(id int64) error {
 	return sess.Commit()
 }
 
-// copyDefaultWebhooksToRepo creates copies of the default webhooks in a new repo
-func copyDefaultWebhooksToRepo(e db.Engine, repoID int64) error {
-	ws, err := getDefaultWebhooks(e)
+// CopyDefaultWebhooksToRepo creates copies of the default webhooks in a new repo
+func CopyDefaultWebhooksToRepo(ctx context.Context, repoID int64) error {
+	ws, err := getDefaultWebhooks(ctx)
 	if err != nil {
 		return fmt.Errorf("GetDefaultWebhooks: %v", err)
 	}
@@ -562,267 +574,9 @@ func copyDefaultWebhooksToRepo(e db.Engine, repoID int64) error {
 	for _, w := range ws {
 		w.ID = 0
 		w.RepoID = repoID
-		if err := createWebhook(e, w); err != nil {
+		if err := CreateWebhook(ctx, w); err != nil {
 			return fmt.Errorf("CreateWebhook: %v", err)
 		}
 	}
 	return nil
 }
-
-//   ___ ___                __   ___________              __
-//  /   |   \  ____   ____ |  | _\__    ___/____    _____|  | __
-// /    ~    \/  _ \ /  _ \|  |/ / |    |  \__  \  /  ___/  |/ /
-// \    Y    (  <_> |  <_> )    <  |    |   / __ \_\___ \|    <
-//  \___|_  / \____/ \____/|__|_ \ |____|  (____  /____  >__|_ \
-//        \/                    \/              \/     \/     \/
-
-// HookEventType is the type of an hook event
-type HookEventType string
-
-// Types of hook events
-const (
-	HookEventCreate                    HookEventType = "create"
-	HookEventDelete                    HookEventType = "delete"
-	HookEventFork                      HookEventType = "fork"
-	HookEventPush                      HookEventType = "push"
-	HookEventIssues                    HookEventType = "issues"
-	HookEventIssueAssign               HookEventType = "issue_assign"
-	HookEventIssueLabel                HookEventType = "issue_label"
-	HookEventIssueMilestone            HookEventType = "issue_milestone"
-	HookEventIssueComment              HookEventType = "issue_comment"
-	HookEventPullRequest               HookEventType = "pull_request"
-	HookEventPullRequestAssign         HookEventType = "pull_request_assign"
-	HookEventPullRequestLabel          HookEventType = "pull_request_label"
-	HookEventPullRequestMilestone      HookEventType = "pull_request_milestone"
-	HookEventPullRequestComment        HookEventType = "pull_request_comment"
-	HookEventPullRequestReviewApproved HookEventType = "pull_request_review_approved"
-	HookEventPullRequestReviewRejected HookEventType = "pull_request_review_rejected"
-	HookEventPullRequestReviewComment  HookEventType = "pull_request_review_comment"
-	HookEventPullRequestSync           HookEventType = "pull_request_sync"
-	HookEventRepository                HookEventType = "repository"
-	HookEventRelease                   HookEventType = "release"
-)
-
-// Event returns the HookEventType as an event string
-func (h HookEventType) Event() string {
-	switch h {
-	case HookEventCreate:
-		return "create"
-	case HookEventDelete:
-		return "delete"
-	case HookEventFork:
-		return "fork"
-	case HookEventPush:
-		return "push"
-	case HookEventIssues, HookEventIssueAssign, HookEventIssueLabel, HookEventIssueMilestone:
-		return "issues"
-	case HookEventPullRequest, HookEventPullRequestAssign, HookEventPullRequestLabel, HookEventPullRequestMilestone,
-		HookEventPullRequestSync:
-		return "pull_request"
-	case HookEventIssueComment, HookEventPullRequestComment:
-		return "issue_comment"
-	case HookEventPullRequestReviewApproved:
-		return "pull_request_approved"
-	case HookEventPullRequestReviewRejected:
-		return "pull_request_rejected"
-	case HookEventPullRequestReviewComment:
-		return "pull_request_comment"
-	case HookEventRepository:
-		return "repository"
-	case HookEventRelease:
-		return "release"
-	}
-	return ""
-}
-
-// HookRequest represents hook task request information.
-type HookRequest struct {
-	URL        string            `json:"url"`
-	HTTPMethod string            `json:"http_method"`
-	Headers    map[string]string `json:"headers"`
-}
-
-// HookResponse represents hook task response information.
-type HookResponse struct {
-	Status  int               `json:"status"`
-	Headers map[string]string `json:"headers"`
-	Body    string            `json:"body"`
-}
-
-// HookTask represents a hook task.
-type HookTask struct {
-	ID              int64 `xorm:"pk autoincr"`
-	RepoID          int64 `xorm:"INDEX"`
-	HookID          int64
-	UUID            string
-	api.Payloader   `xorm:"-"`
-	PayloadContent  string `xorm:"TEXT"`
-	EventType       HookEventType
-	IsDelivered     bool
-	Delivered       int64
-	DeliveredString string `xorm:"-"`
-
-	// History info.
-	IsSucceed       bool
-	RequestContent  string        `xorm:"TEXT"`
-	RequestInfo     *HookRequest  `xorm:"-"`
-	ResponseContent string        `xorm:"TEXT"`
-	ResponseInfo    *HookResponse `xorm:"-"`
-}
-
-// BeforeUpdate will be invoked by XORM before updating a record
-// representing this object
-func (t *HookTask) BeforeUpdate() {
-	if t.RequestInfo != nil {
-		t.RequestContent = t.simpleMarshalJSON(t.RequestInfo)
-	}
-	if t.ResponseInfo != nil {
-		t.ResponseContent = t.simpleMarshalJSON(t.ResponseInfo)
-	}
-}
-
-// AfterLoad updates the webhook object upon setting a column
-func (t *HookTask) AfterLoad() {
-	t.DeliveredString = time.Unix(0, t.Delivered).Format("2006-01-02 15:04:05 MST")
-
-	if len(t.RequestContent) == 0 {
-		return
-	}
-
-	t.RequestInfo = &HookRequest{}
-	if err := json.Unmarshal([]byte(t.RequestContent), t.RequestInfo); err != nil {
-		log.Error("Unmarshal RequestContent[%d]: %v", t.ID, err)
-	}
-
-	if len(t.ResponseContent) > 0 {
-		t.ResponseInfo = &HookResponse{}
-		if err := json.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil {
-			log.Error("Unmarshal ResponseContent[%d]: %v", t.ID, err)
-		}
-	}
-}
-
-func (t *HookTask) simpleMarshalJSON(v interface{}) string {
-	p, err := json.Marshal(v)
-	if err != nil {
-		log.Error("Marshal [%d]: %v", t.ID, err)
-	}
-	return string(p)
-}
-
-// HookTasks returns a list of hook tasks by given conditions.
-func HookTasks(hookID int64, page int) ([]*HookTask, error) {
-	tasks := make([]*HookTask, 0, setting.Webhook.PagingNum)
-	return tasks, db.GetEngine(db.DefaultContext).
-		Limit(setting.Webhook.PagingNum, (page-1)*setting.Webhook.PagingNum).
-		Where("hook_id=?", hookID).
-		Desc("id").
-		Find(&tasks)
-}
-
-// CreateHookTask creates a new hook task,
-// it handles conversion from Payload to PayloadContent.
-func CreateHookTask(t *HookTask) error {
-	return createHookTask(db.GetEngine(db.DefaultContext), t)
-}
-
-func createHookTask(e db.Engine, t *HookTask) error {
-	data, err := t.Payloader.JSONPayload()
-	if err != nil {
-		return err
-	}
-	t.UUID = gouuid.New().String()
-	t.PayloadContent = string(data)
-	_, err = e.Insert(t)
-	return err
-}
-
-// UpdateHookTask updates information of hook task.
-func UpdateHookTask(t *HookTask) error {
-	_, err := db.GetEngine(db.DefaultContext).ID(t.ID).AllCols().Update(t)
-	return err
-}
-
-// FindUndeliveredHookTasks represents find the undelivered hook tasks
-func FindUndeliveredHookTasks() ([]*HookTask, error) {
-	tasks := make([]*HookTask, 0, 10)
-	if err := db.GetEngine(db.DefaultContext).Where("is_delivered=?", false).Find(&tasks); err != nil {
-		return nil, err
-	}
-	return tasks, nil
-}
-
-// FindRepoUndeliveredHookTasks represents find the undelivered hook tasks of one repository
-func FindRepoUndeliveredHookTasks(repoID int64) ([]*HookTask, error) {
-	tasks := make([]*HookTask, 0, 5)
-	if err := db.GetEngine(db.DefaultContext).Where("repo_id=? AND is_delivered=?", repoID, false).Find(&tasks); err != nil {
-		return nil, err
-	}
-	return tasks, nil
-}
-
-// CleanupHookTaskTable deletes rows from hook_task as needed.
-func CleanupHookTaskTable(ctx context.Context, cleanupType HookTaskCleanupType, olderThan time.Duration, numberToKeep int) error {
-	log.Trace("Doing: CleanupHookTaskTable")
-
-	if cleanupType == OlderThan {
-		deleteOlderThan := time.Now().Add(-olderThan).UnixNano()
-		deletes, err := db.GetEngine(db.DefaultContext).
-			Where("is_delivered = ? and delivered < ?", true, deleteOlderThan).
-			Delete(new(HookTask))
-		if err != nil {
-			return err
-		}
-		log.Trace("Deleted %d rows from hook_task", deletes)
-	} else if cleanupType == PerWebhook {
-		hookIDs := make([]int64, 0, 10)
-		err := db.GetEngine(db.DefaultContext).Table("webhook").
-			Where("id > 0").
-			Cols("id").
-			Find(&hookIDs)
-		if err != nil {
-			return err
-		}
-		for _, hookID := range hookIDs {
-			select {
-			case <-ctx.Done():
-				return ErrCancelledf("Before deleting hook_task records for hook id %d", hookID)
-			default:
-			}
-			if err = deleteDeliveredHookTasksByWebhook(hookID, numberToKeep); err != nil {
-				return err
-			}
-		}
-	}
-	log.Trace("Finished: CleanupHookTaskTable")
-	return nil
-}
-
-func deleteDeliveredHookTasksByWebhook(hookID int64, numberDeliveriesToKeep int) error {
-	log.Trace("Deleting hook_task rows for webhook %d, keeping the most recent %d deliveries", hookID, numberDeliveriesToKeep)
-	deliveryDates := make([]int64, 0, 10)
-	err := db.GetEngine(db.DefaultContext).Table("hook_task").
-		Where("hook_task.hook_id = ? AND hook_task.is_delivered = ? AND hook_task.delivered is not null", hookID, true).
-		Cols("hook_task.delivered").
-		Join("INNER", "webhook", "hook_task.hook_id = webhook.id").
-		OrderBy("hook_task.delivered desc").
-		Limit(1, int(numberDeliveriesToKeep)).
-		Find(&deliveryDates)
-	if err != nil {
-		return err
-	}
-
-	if len(deliveryDates) > 0 {
-		deletes, err := db.GetEngine(db.DefaultContext).
-			Where("hook_id = ? and is_delivered = ? and delivered <= ?", hookID, true, deliveryDates[0]).
-			Delete(new(HookTask))
-		if err != nil {
-			return err
-		}
-		log.Trace("Deleted %d hook_task rows for webhook %d", deletes, hookID)
-	} else {
-		log.Trace("No hook_task rows to delete for webhook %d", hookID)
-	}
-
-	return nil
-}
diff --git a/models/webhook_test.go b/models/webhook/webhook_test.go
similarity index 99%
rename from models/webhook_test.go
rename to models/webhook/webhook_test.go
index d48fa365be..df2c37b355 100644
--- a/models/webhook_test.go
+++ b/models/webhook/webhook_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package models
+package webhook
 
 import (
 	"context"
@@ -92,7 +92,7 @@ func TestCreateWebhook(t *testing.T) {
 		Events:      `{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}`,
 	}
 	db.AssertNotExistsBean(t, hook)
-	assert.NoError(t, CreateWebhook(hook))
+	assert.NoError(t, CreateWebhook(db.DefaultContext, hook))
 	db.AssertExistsAndLoadBean(t, hook)
 }
 
diff --git a/modules/convert/convert.go b/modules/convert/convert.go
index 9c5e2037c5..7d67e2b5b1 100644
--- a/modules/convert/convert.go
+++ b/modules/convert/convert.go
@@ -14,11 +14,12 @@ import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/login"
 	"code.gitea.io/gitea/models/unit"
+	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/util"
-	"code.gitea.io/gitea/services/webhook"
+	webhook_service "code.gitea.io/gitea/services/webhook"
 )
 
 // ToEmail convert models.EmailAddress to api.Email
@@ -226,13 +227,13 @@ func ToGPGKeyEmail(email *models.EmailAddress) *api.GPGKeyEmail {
 }
 
 // ToHook convert models.Webhook to api.Hook
-func ToHook(repoLink string, w *models.Webhook) *api.Hook {
+func ToHook(repoLink string, w *webhook.Webhook) *api.Hook {
 	config := map[string]string{
 		"url":          w.URL,
 		"content_type": w.ContentType.Name(),
 	}
-	if w.Type == models.SLACK {
-		s := webhook.GetSlackHook(w)
+	if w.Type == webhook.SLACK {
+		s := webhook_service.GetSlackHook(w)
 		config["channel"] = s.Channel
 		config["username"] = s.Username
 		config["icon_url"] = s.IconURL
diff --git a/modules/cron/tasks.go b/modules/cron/tasks.go
index 6f3a96fd3f..56c363e0b8 100644
--- a/modules/cron/tasks.go
+++ b/modules/cron/tasks.go
@@ -11,6 +11,7 @@ import (
 	"sync"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/process"
@@ -86,8 +87,8 @@ func (t *Task) RunWithUser(doer *models.User, config Config) {
 		pid := pm.Add(config.FormatMessage(t.Name, "process", doer), cancel)
 		defer pm.Remove(pid)
 		if err := t.fun(ctx, doer, config); err != nil {
-			if models.IsErrCancelled(err) {
-				message := err.(models.ErrCancelled).Message
+			if db.IsErrCancelled(err) {
+				message := err.(db.ErrCancelled).Message
 				if err := models.CreateNotice(models.NoticeTask, config.FormatMessage(t.Name, "aborted", doer, message)); err != nil {
 					log.Error("CreateNotice: %v", err)
 				}
diff --git a/modules/cron/tasks_basic.go b/modules/cron/tasks_basic.go
index 6c61d628c5..a42c031b36 100644
--- a/modules/cron/tasks_basic.go
+++ b/modules/cron/tasks_basic.go
@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/migrations"
 	repository_service "code.gitea.io/gitea/modules/repository"
 	"code.gitea.io/gitea/modules/setting"
@@ -122,7 +123,7 @@ func registerCleanupHookTaskTable() {
 		NumberToKeep: 10,
 	}, func(ctx context.Context, _ *models.User, config Config) error {
 		realConfig := config.(*CleanupHookTaskConfig)
-		return models.CleanupHookTaskTable(ctx, models.ToHookTaskCleanupType(realConfig.CleanupType), realConfig.OlderThan, realConfig.NumberToKeep)
+		return webhook.CleanupHookTaskTable(ctx, webhook.ToHookTaskCleanupType(realConfig.CleanupType), realConfig.OlderThan, realConfig.NumberToKeep)
 	})
 }
 
diff --git a/modules/migrations/update.go b/modules/migrations/update.go
index e7a57ceca8..ddc9401ead 100644
--- a/modules/migrations/update.go
+++ b/modules/migrations/update.go
@@ -8,6 +8,7 @@ import (
 	"context"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/structs"
 )
@@ -18,7 +19,7 @@ func UpdateMigrationPosterID(ctx context.Context) error {
 		select {
 		case <-ctx.Done():
 			log.Warn("UpdateMigrationPosterID aborted before %s", gitService.Name())
-			return models.ErrCancelledf("during UpdateMigrationPosterID before %s", gitService.Name())
+			return db.ErrCancelledf("during UpdateMigrationPosterID before %s", gitService.Name())
 		default:
 		}
 		if err := updateMigrationPosterIDByGitService(ctx, gitService); err != nil {
diff --git a/modules/notification/webhook/webhook.go b/modules/notification/webhook/webhook.go
index 35f0cdc596..de6e19a065 100644
--- a/modules/notification/webhook/webhook.go
+++ b/modules/notification/webhook/webhook.go
@@ -7,6 +7,7 @@ package webhook
 import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unit"
+	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
@@ -49,7 +50,7 @@ func (m *webhookNotifier) NotifyIssueClearLabels(doer *models.User, issue *model
 			return
 		}
 
-		err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventPullRequestLabel, &api.PullRequestPayload{
+		err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequestLabel, &api.PullRequestPayload{
 			Action:      api.HookIssueLabelCleared,
 			Index:       issue.Index,
 			PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
@@ -57,7 +58,7 @@ func (m *webhookNotifier) NotifyIssueClearLabels(doer *models.User, issue *model
 			Sender:      convert.ToUser(doer, nil),
 		})
 	} else {
-		err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventIssueLabel, &api.IssuePayload{
+		err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssueLabel, &api.IssuePayload{
 			Action:     api.HookIssueLabelCleared,
 			Index:      issue.Index,
 			Issue:      convert.ToAPIIssue(issue),
@@ -75,7 +76,7 @@ func (m *webhookNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo
 	mode, _ := models.AccessLevel(doer, repo)
 
 	// forked webhook
-	if err := webhook_services.PrepareWebhooks(oldRepo, models.HookEventFork, &api.ForkPayload{
+	if err := webhook_services.PrepareWebhooks(oldRepo, webhook.HookEventFork, &api.ForkPayload{
 		Forkee: convert.ToRepo(oldRepo, oldMode),
 		Repo:   convert.ToRepo(repo, mode),
 		Sender: convert.ToUser(doer, nil),
@@ -87,7 +88,7 @@ func (m *webhookNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo
 
 	// Add to hook queue for created repo after session commit.
 	if u.IsOrganization() {
-		if err := webhook_services.PrepareWebhooks(repo, models.HookEventRepository, &api.RepositoryPayload{
+		if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventRepository, &api.RepositoryPayload{
 			Action:       api.HookRepoCreated,
 			Repository:   convert.ToRepo(repo, models.AccessModeOwner),
 			Organization: convert.ToUser(u, nil),
@@ -100,7 +101,7 @@ func (m *webhookNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo
 
 func (m *webhookNotifier) NotifyCreateRepository(doer *models.User, u *models.User, repo *models.Repository) {
 	// Add to hook queue for created repo after session commit.
-	if err := webhook_services.PrepareWebhooks(repo, models.HookEventRepository, &api.RepositoryPayload{
+	if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventRepository, &api.RepositoryPayload{
 		Action:       api.HookRepoCreated,
 		Repository:   convert.ToRepo(repo, models.AccessModeOwner),
 		Organization: convert.ToUser(u, nil),
@@ -113,7 +114,7 @@ func (m *webhookNotifier) NotifyCreateRepository(doer *models.User, u *models.Us
 func (m *webhookNotifier) NotifyDeleteRepository(doer *models.User, repo *models.Repository) {
 	u := repo.MustOwner()
 
-	if err := webhook_services.PrepareWebhooks(repo, models.HookEventRepository, &api.RepositoryPayload{
+	if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventRepository, &api.RepositoryPayload{
 		Action:       api.HookRepoDeleted,
 		Repository:   convert.ToRepo(repo, models.AccessModeOwner),
 		Organization: convert.ToUser(u, nil),
@@ -125,7 +126,7 @@ func (m *webhookNotifier) NotifyDeleteRepository(doer *models.User, repo *models
 
 func (m *webhookNotifier) NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository) {
 	// Add to hook queue for created repo after session commit.
-	if err := webhook_services.PrepareWebhooks(repo, models.HookEventRepository, &api.RepositoryPayload{
+	if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventRepository, &api.RepositoryPayload{
 		Action:       api.HookRepoCreated,
 		Repository:   convert.ToRepo(repo, models.AccessModeOwner),
 		Organization: convert.ToUser(u, nil),
@@ -156,7 +157,7 @@ func (m *webhookNotifier) NotifyIssueChangeAssignee(doer *models.User, issue *mo
 			apiPullRequest.Action = api.HookIssueAssigned
 		}
 		// Assignee comment triggers a webhook
-		if err := webhook_services.PrepareWebhooks(issue.Repo, models.HookEventPullRequestAssign, apiPullRequest); err != nil {
+		if err := webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequestAssign, apiPullRequest); err != nil {
 			log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err)
 			return
 		}
@@ -174,7 +175,7 @@ func (m *webhookNotifier) NotifyIssueChangeAssignee(doer *models.User, issue *mo
 			apiIssue.Action = api.HookIssueAssigned
 		}
 		// Assignee comment triggers a webhook
-		if err := webhook_services.PrepareWebhooks(issue.Repo, models.HookEventIssueAssign, apiIssue); err != nil {
+		if err := webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssueAssign, apiIssue); err != nil {
 			log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err)
 			return
 		}
@@ -190,7 +191,7 @@ func (m *webhookNotifier) NotifyIssueChangeTitle(doer *models.User, issue *model
 			return
 		}
 		issue.PullRequest.Issue = issue
-		err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
+		err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequest, &api.PullRequestPayload{
 			Action: api.HookIssueEdited,
 			Index:  issue.Index,
 			Changes: &api.ChangesPayload{
@@ -203,7 +204,7 @@ func (m *webhookNotifier) NotifyIssueChangeTitle(doer *models.User, issue *model
 			Sender:      convert.ToUser(doer, nil),
 		})
 	} else {
-		err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventIssues, &api.IssuePayload{
+		err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssues, &api.IssuePayload{
 			Action: api.HookIssueEdited,
 			Index:  issue.Index,
 			Changes: &api.ChangesPayload{
@@ -242,7 +243,7 @@ func (m *webhookNotifier) NotifyIssueChangeStatus(doer *models.User, issue *mode
 		} else {
 			apiPullRequest.Action = api.HookIssueReOpened
 		}
-		err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventPullRequest, apiPullRequest)
+		err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequest, apiPullRequest)
 	} else {
 		apiIssue := &api.IssuePayload{
 			Index:      issue.Index,
@@ -255,7 +256,7 @@ func (m *webhookNotifier) NotifyIssueChangeStatus(doer *models.User, issue *mode
 		} else {
 			apiIssue.Action = api.HookIssueReOpened
 		}
-		err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventIssues, apiIssue)
+		err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssues, apiIssue)
 	}
 	if err != nil {
 		log.Error("PrepareWebhooks [is_pull: %v, is_closed: %v]: %v", issue.IsPull, isClosed, err)
@@ -273,7 +274,7 @@ func (m *webhookNotifier) NotifyNewIssue(issue *models.Issue, mentions []*models
 	}
 
 	mode, _ := models.AccessLevel(issue.Poster, issue.Repo)
-	if err := webhook_services.PrepareWebhooks(issue.Repo, models.HookEventIssues, &api.IssuePayload{
+	if err := webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssues, &api.IssuePayload{
 		Action:     api.HookIssueOpened,
 		Index:      issue.Index,
 		Issue:      convert.ToAPIIssue(issue),
@@ -299,7 +300,7 @@ func (m *webhookNotifier) NotifyNewPullRequest(pull *models.PullRequest, mention
 	}
 
 	mode, _ := models.AccessLevel(pull.Issue.Poster, pull.Issue.Repo)
-	if err := webhook_services.PrepareWebhooks(pull.Issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
+	if err := webhook_services.PrepareWebhooks(pull.Issue.Repo, webhook.HookEventPullRequest, &api.PullRequestPayload{
 		Action:      api.HookIssueOpened,
 		Index:       pull.Issue.Index,
 		PullRequest: convert.ToAPIPullRequest(pull, nil),
@@ -315,7 +316,7 @@ func (m *webhookNotifier) NotifyIssueChangeContent(doer *models.User, issue *mod
 	var err error
 	if issue.IsPull {
 		issue.PullRequest.Issue = issue
-		err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
+		err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequest, &api.PullRequestPayload{
 			Action: api.HookIssueEdited,
 			Index:  issue.Index,
 			Changes: &api.ChangesPayload{
@@ -328,7 +329,7 @@ func (m *webhookNotifier) NotifyIssueChangeContent(doer *models.User, issue *mod
 			Sender:      convert.ToUser(doer, nil),
 		})
 	} else {
-		err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventIssues, &api.IssuePayload{
+		err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssues, &api.IssuePayload{
 			Action: api.HookIssueEdited,
 			Index:  issue.Index,
 			Changes: &api.ChangesPayload{
@@ -365,7 +366,7 @@ func (m *webhookNotifier) NotifyUpdateComment(doer *models.User, c *models.Comme
 
 	mode, _ := models.AccessLevel(doer, c.Issue.Repo)
 	if c.Issue.IsPull {
-		err = webhook_services.PrepareWebhooks(c.Issue.Repo, models.HookEventPullRequestComment, &api.IssueCommentPayload{
+		err = webhook_services.PrepareWebhooks(c.Issue.Repo, webhook.HookEventPullRequestComment, &api.IssueCommentPayload{
 			Action:  api.HookIssueCommentEdited,
 			Issue:   convert.ToAPIIssue(c.Issue),
 			Comment: convert.ToComment(c),
@@ -379,7 +380,7 @@ func (m *webhookNotifier) NotifyUpdateComment(doer *models.User, c *models.Comme
 			IsPull:     true,
 		})
 	} else {
-		err = webhook_services.PrepareWebhooks(c.Issue.Repo, models.HookEventIssueComment, &api.IssueCommentPayload{
+		err = webhook_services.PrepareWebhooks(c.Issue.Repo, webhook.HookEventIssueComment, &api.IssueCommentPayload{
 			Action:  api.HookIssueCommentEdited,
 			Issue:   convert.ToAPIIssue(c.Issue),
 			Comment: convert.ToComment(c),
@@ -405,7 +406,7 @@ func (m *webhookNotifier) NotifyCreateIssueComment(doer *models.User, repo *mode
 
 	var err error
 	if issue.IsPull {
-		err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventPullRequestComment, &api.IssueCommentPayload{
+		err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequestComment, &api.IssueCommentPayload{
 			Action:     api.HookIssueCommentCreated,
 			Issue:      convert.ToAPIIssue(issue),
 			Comment:    convert.ToComment(comment),
@@ -414,7 +415,7 @@ func (m *webhookNotifier) NotifyCreateIssueComment(doer *models.User, repo *mode
 			IsPull:     true,
 		})
 	} else {
-		err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventIssueComment, &api.IssueCommentPayload{
+		err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssueComment, &api.IssueCommentPayload{
 			Action:     api.HookIssueCommentCreated,
 			Issue:      convert.ToAPIIssue(issue),
 			Comment:    convert.ToComment(comment),
@@ -449,7 +450,7 @@ func (m *webhookNotifier) NotifyDeleteComment(doer *models.User, comment *models
 	mode, _ := models.AccessLevel(doer, comment.Issue.Repo)
 
 	if comment.Issue.IsPull {
-		err = webhook_services.PrepareWebhooks(comment.Issue.Repo, models.HookEventPullRequestComment, &api.IssueCommentPayload{
+		err = webhook_services.PrepareWebhooks(comment.Issue.Repo, webhook.HookEventPullRequestComment, &api.IssueCommentPayload{
 			Action:     api.HookIssueCommentDeleted,
 			Issue:      convert.ToAPIIssue(comment.Issue),
 			Comment:    convert.ToComment(comment),
@@ -458,7 +459,7 @@ func (m *webhookNotifier) NotifyDeleteComment(doer *models.User, comment *models
 			IsPull:     true,
 		})
 	} else {
-		err = webhook_services.PrepareWebhooks(comment.Issue.Repo, models.HookEventIssueComment, &api.IssueCommentPayload{
+		err = webhook_services.PrepareWebhooks(comment.Issue.Repo, webhook.HookEventIssueComment, &api.IssueCommentPayload{
 			Action:     api.HookIssueCommentDeleted,
 			Issue:      convert.ToAPIIssue(comment.Issue),
 			Comment:    convert.ToComment(comment),
@@ -498,7 +499,7 @@ func (m *webhookNotifier) NotifyIssueChangeLabels(doer *models.User, issue *mode
 			log.Error("LoadIssue: %v", err)
 			return
 		}
-		err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventPullRequestLabel, &api.PullRequestPayload{
+		err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequestLabel, &api.PullRequestPayload{
 			Action:      api.HookIssueLabelUpdated,
 			Index:       issue.Index,
 			PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
@@ -506,7 +507,7 @@ func (m *webhookNotifier) NotifyIssueChangeLabels(doer *models.User, issue *mode
 			Sender:      convert.ToUser(doer, nil),
 		})
 	} else {
-		err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventIssueLabel, &api.IssuePayload{
+		err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssueLabel, &api.IssuePayload{
 			Action:     api.HookIssueLabelUpdated,
 			Index:      issue.Index,
 			Issue:      convert.ToAPIIssue(issue),
@@ -540,7 +541,7 @@ func (m *webhookNotifier) NotifyIssueChangeMilestone(doer *models.User, issue *m
 			log.Error("LoadIssue: %v", err)
 			return
 		}
-		err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventPullRequestMilestone, &api.PullRequestPayload{
+		err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequestMilestone, &api.PullRequestPayload{
 			Action:      hookAction,
 			Index:       issue.Index,
 			PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
@@ -548,7 +549,7 @@ func (m *webhookNotifier) NotifyIssueChangeMilestone(doer *models.User, issue *m
 			Sender:      convert.ToUser(doer, nil),
 		})
 	} else {
-		err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventIssueMilestone, &api.IssuePayload{
+		err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssueMilestone, &api.IssuePayload{
 			Action:     hookAction,
 			Index:      issue.Index,
 			Issue:      convert.ToAPIIssue(issue),
@@ -569,7 +570,7 @@ func (m *webhookNotifier) NotifyPushCommits(pusher *models.User, repo *models.Re
 		return
 	}
 
-	if err := webhook_services.PrepareWebhooks(repo, models.HookEventPush, &api.PushPayload{
+	if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventPush, &api.PushPayload{
 		Ref:        opts.RefFullName,
 		Before:     opts.OldCommitID,
 		After:      opts.NewCommitID,
@@ -616,7 +617,7 @@ func (*webhookNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *mod
 		Action:      api.HookIssueClosed,
 	}
 
-	err = webhook_services.PrepareWebhooks(pr.Issue.Repo, models.HookEventPullRequest, apiPullRequest)
+	err = webhook_services.PrepareWebhooks(pr.Issue.Repo, webhook.HookEventPullRequest, apiPullRequest)
 	if err != nil {
 		log.Error("PrepareWebhooks: %v", err)
 	}
@@ -635,7 +636,7 @@ func (m *webhookNotifier) NotifyPullRequestChangeTargetBranch(doer *models.User,
 	}
 	issue.PullRequest.Issue = issue
 	mode, _ := models.AccessLevel(issue.Poster, issue.Repo)
-	err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
+	err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequest, &api.PullRequestPayload{
 		Action: api.HookIssueEdited,
 		Index:  issue.Index,
 		Changes: &api.ChangesPayload{
@@ -654,15 +655,15 @@ func (m *webhookNotifier) NotifyPullRequestChangeTargetBranch(doer *models.User,
 }
 
 func (m *webhookNotifier) NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment, mentions []*models.User) {
-	var reviewHookType models.HookEventType
+	var reviewHookType webhook.HookEventType
 
 	switch review.Type {
 	case models.ReviewTypeApprove:
-		reviewHookType = models.HookEventPullRequestReviewApproved
+		reviewHookType = webhook.HookEventPullRequestReviewApproved
 	case models.ReviewTypeComment:
-		reviewHookType = models.HookEventPullRequestComment
+		reviewHookType = webhook.HookEventPullRequestComment
 	case models.ReviewTypeReject:
-		reviewHookType = models.HookEventPullRequestReviewRejected
+		reviewHookType = webhook.HookEventPullRequestReviewRejected
 	default:
 		// unsupported review webhook type here
 		log.Error("Unsupported review webhook type")
@@ -713,7 +714,7 @@ func (m *webhookNotifier) NotifyCreateRef(pusher *models.User, repo *models.Repo
 	}
 	gitRepo.Close()
 
-	if err = webhook_services.PrepareWebhooks(repo, models.HookEventCreate, &api.CreatePayload{
+	if err = webhook_services.PrepareWebhooks(repo, webhook.HookEventCreate, &api.CreatePayload{
 		Ref:     refName,
 		Sha:     shaSum,
 		RefType: refType,
@@ -734,7 +735,7 @@ func (m *webhookNotifier) NotifyPullRequestSynchronized(doer *models.User, pr *m
 		return
 	}
 
-	if err := webhook_services.PrepareWebhooks(pr.Issue.Repo, models.HookEventPullRequestSync, &api.PullRequestPayload{
+	if err := webhook_services.PrepareWebhooks(pr.Issue.Repo, webhook.HookEventPullRequestSync, &api.PullRequestPayload{
 		Action:      api.HookIssueSynchronized,
 		Index:       pr.Issue.Index,
 		PullRequest: convert.ToAPIPullRequest(pr, nil),
@@ -750,7 +751,7 @@ func (m *webhookNotifier) NotifyDeleteRef(pusher *models.User, repo *models.Repo
 	apiRepo := convert.ToRepo(repo, models.AccessModeNone)
 	refName := git.RefEndName(refFullName)
 
-	if err := webhook_services.PrepareWebhooks(repo, models.HookEventDelete, &api.DeletePayload{
+	if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventDelete, &api.DeletePayload{
 		Ref:        refName,
 		RefType:    refType,
 		PusherType: api.PusherTypeUser,
@@ -768,7 +769,7 @@ func sendReleaseHook(doer *models.User, rel *models.Release, action api.HookRele
 	}
 
 	mode, _ := models.AccessLevel(rel.Publisher, rel.Repo)
-	if err := webhook_services.PrepareWebhooks(rel.Repo, models.HookEventRelease, &api.ReleasePayload{
+	if err := webhook_services.PrepareWebhooks(rel.Repo, webhook.HookEventRelease, &api.ReleasePayload{
 		Action:     action,
 		Release:    convert.ToRelease(rel),
 		Repository: convert.ToRepo(rel.Repo, mode),
@@ -798,7 +799,7 @@ func (m *webhookNotifier) NotifySyncPushCommits(pusher *models.User, repo *model
 		return
 	}
 
-	if err := webhook_services.PrepareWebhooks(repo, models.HookEventPush, &api.PushPayload{
+	if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventPush, &api.PushPayload{
 		Ref:        opts.RefFullName,
 		Before:     opts.OldCommitID,
 		After:      opts.NewCommitID,
diff --git a/modules/repository/check.go b/modules/repository/check.go
index 78e6f8a901..1857242f50 100644
--- a/modules/repository/check.go
+++ b/modules/repository/check.go
@@ -31,7 +31,7 @@ func GitFsck(ctx context.Context, timeout time.Duration, args []string) error {
 			repo := bean.(*models.Repository)
 			select {
 			case <-ctx.Done():
-				return models.ErrCancelledf("before fsck of %s", repo.FullName())
+				return db.ErrCancelledf("before fsck of %s", repo.FullName())
 			default:
 			}
 			log.Trace("Running health check on repository %v", repo)
@@ -66,7 +66,7 @@ func GitGcRepos(ctx context.Context, timeout time.Duration, args ...string) erro
 			repo := bean.(*models.Repository)
 			select {
 			case <-ctx.Done():
-				return models.ErrCancelledf("before GC of %s", repo.FullName())
+				return db.ErrCancelledf("before GC of %s", repo.FullName())
 			default:
 			}
 			log.Trace("Running git gc on %v", repo)
@@ -123,7 +123,7 @@ func gatherMissingRepoRecords(ctx context.Context) ([]*models.Repository, error)
 			repo := bean.(*models.Repository)
 			select {
 			case <-ctx.Done():
-				return models.ErrCancelledf("during gathering missing repo records before checking %s", repo.FullName())
+				return db.ErrCancelledf("during gathering missing repo records before checking %s", repo.FullName())
 			default:
 			}
 			isDir, err := util.IsDir(repo.RepoPath())
@@ -161,7 +161,7 @@ func DeleteMissingRepositories(ctx context.Context, doer *models.User) error {
 	for _, repo := range repos {
 		select {
 		case <-ctx.Done():
-			return models.ErrCancelledf("during DeleteMissingRepositories before %s", repo.FullName())
+			return db.ErrCancelledf("during DeleteMissingRepositories before %s", repo.FullName())
 		default:
 		}
 		log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID)
@@ -189,7 +189,7 @@ func ReinitMissingRepositories(ctx context.Context) error {
 	for _, repo := range repos {
 		select {
 		case <-ctx.Done():
-			return models.ErrCancelledf("during ReinitMissingRepositories before %s", repo.FullName())
+			return db.ErrCancelledf("during ReinitMissingRepositories before %s", repo.FullName())
 		default:
 		}
 		log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID)
diff --git a/modules/repository/hooks.go b/modules/repository/hooks.go
index 63f00b8f80..23eee8897d 100644
--- a/modules/repository/hooks.go
+++ b/modules/repository/hooks.go
@@ -254,7 +254,7 @@ func SyncRepositoryHooks(ctx context.Context) error {
 			repo := bean.(*models.Repository)
 			select {
 			case <-ctx.Done():
-				return models.ErrCancelledf("before sync repository hooks for %s", repo.FullName())
+				return db.ErrCancelledf("before sync repository hooks for %s", repo.FullName())
 			default:
 			}
 
diff --git a/routers/api/v1/org/hook.go b/routers/api/v1/org/hook.go
index c5982300eb..6c81616170 100644
--- a/routers/api/v1/org/hook.go
+++ b/routers/api/v1/org/hook.go
@@ -7,7 +7,7 @@ package org
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
@@ -40,18 +40,18 @@ func ListHooks(ctx *context.APIContext) {
 	//   "200":
 	//     "$ref": "#/responses/HookList"
 
-	opts := &models.ListWebhookOptions{
+	opts := &webhook.ListWebhookOptions{
 		ListOptions: utils.GetListOptions(ctx),
 		OrgID:       ctx.Org.Organization.ID,
 	}
 
-	count, err := models.CountWebhooksByOpts(opts)
+	count, err := webhook.CountWebhooksByOpts(opts)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
 	}
 
-	orgHooks, err := models.ListWebhooksByOpts(opts)
+	orgHooks, err := webhook.ListWebhooksByOpts(opts)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
@@ -191,8 +191,8 @@ func DeleteHook(ctx *context.APIContext) {
 
 	org := ctx.Org.Organization
 	hookID := ctx.ParamsInt64(":id")
-	if err := models.DeleteWebhookByOrgID(org.ID, hookID); err != nil {
-		if models.IsErrWebhookNotExist(err) {
+	if err := webhook.DeleteWebhookByOrgID(org.ID, hookID); err != nil {
+		if webhook.IsErrWebhookNotExist(err) {
 			ctx.NotFound()
 		} else {
 			ctx.Error(http.StatusInternalServerError, "DeleteWebhookByOrgID", err)
diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go
index 74860fd72f..2969b92502 100644
--- a/routers/api/v1/repo/hook.go
+++ b/routers/api/v1/repo/hook.go
@@ -9,13 +9,14 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/git"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/web"
 	"code.gitea.io/gitea/routers/api/v1/utils"
-	"code.gitea.io/gitea/services/webhook"
+	webhook_service "code.gitea.io/gitea/services/webhook"
 )
 
 // ListHooks list all hooks of a repository
@@ -48,18 +49,18 @@ func ListHooks(ctx *context.APIContext) {
 	//   "200":
 	//     "$ref": "#/responses/HookList"
 
-	opts := &models.ListWebhookOptions{
+	opts := &webhook.ListWebhookOptions{
 		ListOptions: utils.GetListOptions(ctx),
 		RepoID:      ctx.Repo.Repository.ID,
 	}
 
-	count, err := models.CountWebhooksByOpts(opts)
+	count, err := webhook.CountWebhooksByOpts(opts)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
 	}
 
-	hooks, err := models.ListWebhooksByOpts(opts)
+	hooks, err := webhook.ListWebhooksByOpts(opts)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
@@ -155,7 +156,7 @@ func TestHook(ctx *context.APIContext) {
 
 	commit := convert.ToPayloadCommit(ctx.Repo.Repository, ctx.Repo.Commit)
 
-	if err := webhook.PrepareWebhook(hook, ctx.Repo.Repository, models.HookEventPush, &api.PushPayload{
+	if err := webhook_service.PrepareWebhook(hook, ctx.Repo.Repository, webhook.HookEventPush, &api.PushPayload{
 		Ref:        git.BranchPrefix + ctx.Repo.Repository.DefaultBranch,
 		Before:     ctx.Repo.Commit.ID.String(),
 		After:      ctx.Repo.Commit.ID.String(),
@@ -272,8 +273,8 @@ func DeleteHook(ctx *context.APIContext) {
 	//     "$ref": "#/responses/empty"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
-	if err := models.DeleteWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")); err != nil {
-		if models.IsErrWebhookNotExist(err) {
+	if err := webhook.DeleteWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")); err != nil {
+		if webhook.IsErrWebhookNotExist(err) {
 			ctx.NotFound()
 		} else {
 			ctx.Error(http.StatusInternalServerError, "DeleteWebhookByRepoID", err)
diff --git a/routers/api/v1/repo/hook_test.go b/routers/api/v1/repo/hook_test.go
index 79b8ec171c..60fa6cead3 100644
--- a/routers/api/v1/repo/hook_test.go
+++ b/routers/api/v1/repo/hook_test.go
@@ -8,8 +8,8 @@ import (
 	"net/http"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/test"
 
@@ -27,7 +27,7 @@ func TestTestHook(t *testing.T) {
 	TestHook(&context.APIContext{Context: ctx, Org: nil})
 	assert.EqualValues(t, http.StatusNoContent, ctx.Resp.Status())
 
-	db.AssertExistsAndLoadBean(t, &models.HookTask{
+	db.AssertExistsAndLoadBean(t, &webhook.HookTask{
 		RepoID: 1,
 		HookID: 1,
 	}, db.Cond("is_delivered=?", false))
diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go
index a257260cad..aef9b8f4ce 100644
--- a/routers/api/v1/utils/hook.go
+++ b/routers/api/v1/utils/hook.go
@@ -9,22 +9,23 @@ import (
 	"net/http"
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/json"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/routers/utils"
-	"code.gitea.io/gitea/services/webhook"
+	webhook_service "code.gitea.io/gitea/services/webhook"
 )
 
 // GetOrgHook get an organization's webhook. If there is an error, write to
 // `ctx` accordingly and return the error
-func GetOrgHook(ctx *context.APIContext, orgID, hookID int64) (*models.Webhook, error) {
-	w, err := models.GetWebhookByOrgID(orgID, hookID)
+func GetOrgHook(ctx *context.APIContext, orgID, hookID int64) (*webhook.Webhook, error) {
+	w, err := webhook.GetWebhookByOrgID(orgID, hookID)
 	if err != nil {
-		if models.IsErrWebhookNotExist(err) {
+		if webhook.IsErrWebhookNotExist(err) {
 			ctx.NotFound()
 		} else {
 			ctx.Error(http.StatusInternalServerError, "GetWebhookByOrgID", err)
@@ -36,10 +37,10 @@ func GetOrgHook(ctx *context.APIContext, orgID, hookID int64) (*models.Webhook,
 
 // GetRepoHook get a repo's webhook. If there is an error, write to `ctx`
 // accordingly and return the error
-func GetRepoHook(ctx *context.APIContext, repoID, hookID int64) (*models.Webhook, error) {
-	w, err := models.GetWebhookByRepoID(repoID, hookID)
+func GetRepoHook(ctx *context.APIContext, repoID, hookID int64) (*webhook.Webhook, error) {
+	w, err := webhook.GetWebhookByRepoID(repoID, hookID)
 	if err != nil {
-		if models.IsErrWebhookNotExist(err) {
+		if webhook.IsErrWebhookNotExist(err) {
 			ctx.NotFound()
 		} else {
 			ctx.Error(http.StatusInternalServerError, "GetWebhookByID", err)
@@ -52,7 +53,7 @@ func GetRepoHook(ctx *context.APIContext, repoID, hookID int64) (*models.Webhook
 // CheckCreateHookOption check if a CreateHookOption form is valid. If invalid,
 // write the appropriate error to `ctx`. Return whether the form is valid
 func CheckCreateHookOption(ctx *context.APIContext, form *api.CreateHookOption) bool {
-	if !webhook.IsValidHookTaskType(form.Type) {
+	if !webhook_service.IsValidHookTaskType(form.Type) {
 		ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Invalid hook type: %s", form.Type))
 		return false
 	}
@@ -62,7 +63,7 @@ func CheckCreateHookOption(ctx *context.APIContext, form *api.CreateHookOption)
 			return false
 		}
 	}
-	if !models.IsValidHookContentType(form.Config["content_type"]) {
+	if !webhook.IsValidHookContentType(form.Config["content_type"]) {
 		ctx.Error(http.StatusUnprocessableEntity, "", "Invalid content type")
 		return false
 	}
@@ -88,54 +89,54 @@ func AddRepoHook(ctx *context.APIContext, form *api.CreateHookOption) {
 }
 
 func issuesHook(events []string, event string) bool {
-	return util.IsStringInSlice(event, events, true) || util.IsStringInSlice(string(models.HookEventIssues), events, true)
+	return util.IsStringInSlice(event, events, true) || util.IsStringInSlice(string(webhook.HookEventIssues), events, true)
 }
 
 func pullHook(events []string, event string) bool {
-	return util.IsStringInSlice(event, events, true) || util.IsStringInSlice(string(models.HookEventPullRequest), events, true)
+	return util.IsStringInSlice(event, events, true) || util.IsStringInSlice(string(webhook.HookEventPullRequest), events, true)
 }
 
 // addHook add the hook specified by `form`, `orgID` and `repoID`. If there is
 // an error, write to `ctx` accordingly. Return (webhook, ok)
-func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID int64) (*models.Webhook, bool) {
+func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID int64) (*webhook.Webhook, bool) {
 	if len(form.Events) == 0 {
 		form.Events = []string{"push"}
 	}
-	w := &models.Webhook{
+	w := &webhook.Webhook{
 		OrgID:       orgID,
 		RepoID:      repoID,
 		URL:         form.Config["url"],
-		ContentType: models.ToHookContentType(form.Config["content_type"]),
+		ContentType: webhook.ToHookContentType(form.Config["content_type"]),
 		Secret:      form.Config["secret"],
 		HTTPMethod:  "POST",
-		HookEvent: &models.HookEvent{
+		HookEvent: &webhook.HookEvent{
 			ChooseEvents: true,
-			HookEvents: models.HookEvents{
-				Create:               util.IsStringInSlice(string(models.HookEventCreate), form.Events, true),
-				Delete:               util.IsStringInSlice(string(models.HookEventDelete), form.Events, true),
-				Fork:                 util.IsStringInSlice(string(models.HookEventFork), form.Events, true),
+			HookEvents: webhook.HookEvents{
+				Create:               util.IsStringInSlice(string(webhook.HookEventCreate), form.Events, true),
+				Delete:               util.IsStringInSlice(string(webhook.HookEventDelete), form.Events, true),
+				Fork:                 util.IsStringInSlice(string(webhook.HookEventFork), form.Events, true),
 				Issues:               issuesHook(form.Events, "issues_only"),
-				IssueAssign:          issuesHook(form.Events, string(models.HookEventIssueAssign)),
-				IssueLabel:           issuesHook(form.Events, string(models.HookEventIssueLabel)),
-				IssueMilestone:       issuesHook(form.Events, string(models.HookEventIssueMilestone)),
-				IssueComment:         issuesHook(form.Events, string(models.HookEventIssueComment)),
-				Push:                 util.IsStringInSlice(string(models.HookEventPush), form.Events, true),
+				IssueAssign:          issuesHook(form.Events, string(webhook.HookEventIssueAssign)),
+				IssueLabel:           issuesHook(form.Events, string(webhook.HookEventIssueLabel)),
+				IssueMilestone:       issuesHook(form.Events, string(webhook.HookEventIssueMilestone)),
+				IssueComment:         issuesHook(form.Events, string(webhook.HookEventIssueComment)),
+				Push:                 util.IsStringInSlice(string(webhook.HookEventPush), form.Events, true),
 				PullRequest:          pullHook(form.Events, "pull_request_only"),
-				PullRequestAssign:    pullHook(form.Events, string(models.HookEventPullRequestAssign)),
-				PullRequestLabel:     pullHook(form.Events, string(models.HookEventPullRequestLabel)),
-				PullRequestMilestone: pullHook(form.Events, string(models.HookEventPullRequestMilestone)),
-				PullRequestComment:   pullHook(form.Events, string(models.HookEventPullRequestComment)),
+				PullRequestAssign:    pullHook(form.Events, string(webhook.HookEventPullRequestAssign)),
+				PullRequestLabel:     pullHook(form.Events, string(webhook.HookEventPullRequestLabel)),
+				PullRequestMilestone: pullHook(form.Events, string(webhook.HookEventPullRequestMilestone)),
+				PullRequestComment:   pullHook(form.Events, string(webhook.HookEventPullRequestComment)),
 				PullRequestReview:    pullHook(form.Events, "pull_request_review"),
-				PullRequestSync:      pullHook(form.Events, string(models.HookEventPullRequestSync)),
-				Repository:           util.IsStringInSlice(string(models.HookEventRepository), form.Events, true),
-				Release:              util.IsStringInSlice(string(models.HookEventRelease), form.Events, true),
+				PullRequestSync:      pullHook(form.Events, string(webhook.HookEventPullRequestSync)),
+				Repository:           util.IsStringInSlice(string(webhook.HookEventRepository), form.Events, true),
+				Release:              util.IsStringInSlice(string(webhook.HookEventRelease), form.Events, true),
 			},
 			BranchFilter: form.BranchFilter,
 		},
 		IsActive: form.Active,
-		Type:     models.HookType(form.Type),
+		Type:     webhook.HookType(form.Type),
 	}
-	if w.Type == models.SLACK {
+	if w.Type == webhook.SLACK {
 		channel, ok := form.Config["channel"]
 		if !ok {
 			ctx.Error(http.StatusUnprocessableEntity, "", "Missing config option: channel")
@@ -147,7 +148,7 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
 			return nil, false
 		}
 
-		meta, err := json.Marshal(&webhook.SlackMeta{
+		meta, err := json.Marshal(&webhook_service.SlackMeta{
 			Channel:  strings.TrimSpace(channel),
 			Username: form.Config["username"],
 			IconURL:  form.Config["icon_url"],
@@ -163,7 +164,7 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
 	if err := w.UpdateEvent(); err != nil {
 		ctx.Error(http.StatusInternalServerError, "UpdateEvent", err)
 		return nil, false
-	} else if err := models.CreateWebhook(w); err != nil {
+	} else if err := webhook.CreateWebhook(db.DefaultContext, w); err != nil {
 		ctx.Error(http.StatusInternalServerError, "CreateWebhook", err)
 		return nil, false
 	}
@@ -206,22 +207,22 @@ func EditRepoHook(ctx *context.APIContext, form *api.EditHookOption, hookID int6
 
 // editHook edit the webhook `w` according to `form`. If an error occurs, write
 // to `ctx` accordingly and return the error. Return whether successful
-func editHook(ctx *context.APIContext, form *api.EditHookOption, w *models.Webhook) bool {
+func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webhook) bool {
 	if form.Config != nil {
 		if url, ok := form.Config["url"]; ok {
 			w.URL = url
 		}
 		if ct, ok := form.Config["content_type"]; ok {
-			if !models.IsValidHookContentType(ct) {
+			if !webhook.IsValidHookContentType(ct) {
 				ctx.Error(http.StatusUnprocessableEntity, "", "Invalid content type")
 				return false
 			}
-			w.ContentType = models.ToHookContentType(ct)
+			w.ContentType = webhook.ToHookContentType(ct)
 		}
 
-		if w.Type == models.SLACK {
+		if w.Type == webhook.SLACK {
 			if channel, ok := form.Config["channel"]; ok {
-				meta, err := json.Marshal(&webhook.SlackMeta{
+				meta, err := json.Marshal(&webhook_service.SlackMeta{
 					Channel:  channel,
 					Username: form.Config["username"],
 					IconURL:  form.Config["icon_url"],
@@ -243,18 +244,18 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *models.Webho
 	w.PushOnly = false
 	w.SendEverything = false
 	w.ChooseEvents = true
-	w.Create = util.IsStringInSlice(string(models.HookEventCreate), form.Events, true)
-	w.Push = util.IsStringInSlice(string(models.HookEventPush), form.Events, true)
-	w.PullRequest = util.IsStringInSlice(string(models.HookEventPullRequest), form.Events, true)
-	w.Create = util.IsStringInSlice(string(models.HookEventCreate), form.Events, true)
-	w.Delete = util.IsStringInSlice(string(models.HookEventDelete), form.Events, true)
-	w.Fork = util.IsStringInSlice(string(models.HookEventFork), form.Events, true)
-	w.Issues = util.IsStringInSlice(string(models.HookEventIssues), form.Events, true)
-	w.IssueComment = util.IsStringInSlice(string(models.HookEventIssueComment), form.Events, true)
-	w.Push = util.IsStringInSlice(string(models.HookEventPush), form.Events, true)
-	w.PullRequest = util.IsStringInSlice(string(models.HookEventPullRequest), form.Events, true)
-	w.Repository = util.IsStringInSlice(string(models.HookEventRepository), form.Events, true)
-	w.Release = util.IsStringInSlice(string(models.HookEventRelease), form.Events, true)
+	w.Create = util.IsStringInSlice(string(webhook.HookEventCreate), form.Events, true)
+	w.Push = util.IsStringInSlice(string(webhook.HookEventPush), form.Events, true)
+	w.PullRequest = util.IsStringInSlice(string(webhook.HookEventPullRequest), form.Events, true)
+	w.Create = util.IsStringInSlice(string(webhook.HookEventCreate), form.Events, true)
+	w.Delete = util.IsStringInSlice(string(webhook.HookEventDelete), form.Events, true)
+	w.Fork = util.IsStringInSlice(string(webhook.HookEventFork), form.Events, true)
+	w.Issues = util.IsStringInSlice(string(webhook.HookEventIssues), form.Events, true)
+	w.IssueComment = util.IsStringInSlice(string(webhook.HookEventIssueComment), form.Events, true)
+	w.Push = util.IsStringInSlice(string(webhook.HookEventPush), form.Events, true)
+	w.PullRequest = util.IsStringInSlice(string(webhook.HookEventPullRequest), form.Events, true)
+	w.Repository = util.IsStringInSlice(string(webhook.HookEventRepository), form.Events, true)
+	w.Release = util.IsStringInSlice(string(webhook.HookEventRelease), form.Events, true)
 	w.BranchFilter = form.BranchFilter
 
 	if err := w.UpdateEvent(); err != nil {
@@ -266,7 +267,7 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *models.Webho
 		w.IsActive = *form.Active
 	}
 
-	if err := models.UpdateWebhook(w); err != nil {
+	if err := webhook.UpdateWebhook(w); err != nil {
 		ctx.Error(http.StatusInternalServerError, "UpdateWebhook", err)
 		return false
 	}
diff --git a/routers/web/admin/hooks.go b/routers/web/admin/hooks.go
index 13daa9ac9e..8cb99e1d1e 100644
--- a/routers/web/admin/hooks.go
+++ b/routers/web/admin/hooks.go
@@ -7,7 +7,7 @@ package admin
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/setting"
@@ -34,7 +34,7 @@ func DefaultOrSystemWebhooks(ctx *context.Context) {
 
 	sys["Title"] = ctx.Tr("admin.systemhooks")
 	sys["Description"] = ctx.Tr("admin.systemhooks.desc")
-	sys["Webhooks"], err = models.GetSystemWebhooks()
+	sys["Webhooks"], err = webhook.GetSystemWebhooks()
 	sys["BaseLink"] = setting.AppSubURL + "/admin/hooks"
 	sys["BaseLinkNew"] = setting.AppSubURL + "/admin/system-hooks"
 	if err != nil {
@@ -44,7 +44,7 @@ func DefaultOrSystemWebhooks(ctx *context.Context) {
 
 	def["Title"] = ctx.Tr("admin.defaulthooks")
 	def["Description"] = ctx.Tr("admin.defaulthooks.desc")
-	def["Webhooks"], err = models.GetDefaultWebhooks()
+	def["Webhooks"], err = webhook.GetDefaultWebhooks()
 	def["BaseLink"] = setting.AppSubURL + "/admin/hooks"
 	def["BaseLinkNew"] = setting.AppSubURL + "/admin/default-hooks"
 	if err != nil {
@@ -60,7 +60,7 @@ func DefaultOrSystemWebhooks(ctx *context.Context) {
 
 // DeleteDefaultOrSystemWebhook handler to delete an admin-defined system or default webhook
 func DeleteDefaultOrSystemWebhook(ctx *context.Context) {
-	if err := models.DeleteDefaultSystemWebhook(ctx.FormInt64("id")); err != nil {
+	if err := webhook.DeleteDefaultSystemWebhook(ctx.FormInt64("id")); err != nil {
 		ctx.Flash.Error("DeleteDefaultWebhook: " + err.Error())
 	} else {
 		ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go
index 277ff9d973..d8add77f66 100644
--- a/routers/web/org/setting.go
+++ b/routers/web/org/setting.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/log"
@@ -187,7 +188,7 @@ func Webhooks(ctx *context.Context) {
 	ctx.Data["BaseLinkNew"] = ctx.Org.OrgLink + "/settings/hooks"
 	ctx.Data["Description"] = ctx.Tr("org.settings.hooks_desc")
 
-	ws, err := models.ListWebhooksByOpts(&models.ListWebhookOptions{OrgID: ctx.Org.Organization.ID})
+	ws, err := webhook.ListWebhooksByOpts(&webhook.ListWebhookOptions{OrgID: ctx.Org.Organization.ID})
 	if err != nil {
 		ctx.ServerError("GetWebhooksByOrgId", err)
 		return
@@ -199,7 +200,7 @@ func Webhooks(ctx *context.Context) {
 
 // DeleteWebhook response for delete webhook
 func DeleteWebhook(ctx *context.Context) {
-	if err := models.DeleteWebhookByOrgID(ctx.Org.Organization.ID, ctx.FormInt64("id")); err != nil {
+	if err := webhook.DeleteWebhookByOrgID(ctx.Org.Organization.ID, ctx.FormInt64("id")); err != nil {
 		ctx.Flash.Error("DeleteWebhookByOrgID: " + err.Error())
 	} else {
 		ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
diff --git a/routers/web/repo/webhook.go b/routers/web/repo/webhook.go
index 0ceb9bc1dc..f47f8d651d 100644
--- a/routers/web/repo/webhook.go
+++ b/routers/web/repo/webhook.go
@@ -13,6 +13,8 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
@@ -23,7 +25,7 @@ import (
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/modules/web"
 	"code.gitea.io/gitea/services/forms"
-	"code.gitea.io/gitea/services/webhook"
+	webhook_service "code.gitea.io/gitea/services/webhook"
 )
 
 const (
@@ -41,7 +43,7 @@ func Webhooks(ctx *context.Context) {
 	ctx.Data["BaseLinkNew"] = ctx.Repo.RepoLink + "/settings/hooks"
 	ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://docs.gitea.io/en-us/webhooks/")
 
-	ws, err := models.ListWebhooksByOpts(&models.ListWebhookOptions{RepoID: ctx.Repo.Repository.ID})
+	ws, err := webhook.ListWebhooksByOpts(&webhook.ListWebhookOptions{RepoID: ctx.Repo.Repository.ID})
 	if err != nil {
 		ctx.ServerError("GetWebhooksByRepoID", err)
 		return
@@ -117,7 +119,7 @@ func checkHookType(ctx *context.Context) string {
 // WebhooksNew render creating webhook page
 func WebhooksNew(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
-	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
+	ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
 
 	orCtx, err := getOrgRepoCtx(ctx)
 	if err != nil {
@@ -152,13 +154,13 @@ func WebhooksNew(ctx *context.Context) {
 	ctx.HTML(http.StatusOK, orCtx.NewTemplate)
 }
 
-// ParseHookEvent convert web form content to models.HookEvent
-func ParseHookEvent(form forms.WebhookForm) *models.HookEvent {
-	return &models.HookEvent{
+// ParseHookEvent convert web form content to webhook.HookEvent
+func ParseHookEvent(form forms.WebhookForm) *webhook.HookEvent {
+	return &webhook.HookEvent{
 		PushOnly:       form.PushOnly(),
 		SendEverything: form.SendEverything(),
 		ChooseEvents:   form.ChooseEvents(),
-		HookEvents: models.HookEvents{
+		HookEvents: webhook.HookEvents{
 			Create:               form.Create,
 			Delete:               form.Delete,
 			Fork:                 form.Fork,
@@ -188,8 +190,8 @@ func GiteaHooksNewPost(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
 	ctx.Data["PageIsSettingsHooks"] = true
 	ctx.Data["PageIsSettingsHooksNew"] = true
-	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
-	ctx.Data["HookType"] = models.GITEA
+	ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
+	ctx.Data["HookType"] = webhook.GITEA
 
 	orCtx, err := getOrgRepoCtx(ctx)
 	if err != nil {
@@ -203,12 +205,12 @@ func GiteaHooksNewPost(ctx *context.Context) {
 		return
 	}
 
-	contentType := models.ContentTypeJSON
-	if models.HookContentType(form.ContentType) == models.ContentTypeForm {
-		contentType = models.ContentTypeForm
+	contentType := webhook.ContentTypeJSON
+	if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
+		contentType = webhook.ContentTypeForm
 	}
 
-	w := &models.Webhook{
+	w := &webhook.Webhook{
 		RepoID:          orCtx.RepoID,
 		URL:             form.PayloadURL,
 		HTTPMethod:      form.HTTPMethod,
@@ -216,14 +218,14 @@ func GiteaHooksNewPost(ctx *context.Context) {
 		Secret:          form.Secret,
 		HookEvent:       ParseHookEvent(form.WebhookForm),
 		IsActive:        form.Active,
-		Type:            models.GITEA,
+		Type:            webhook.GITEA,
 		OrgID:           orCtx.OrgID,
 		IsSystemWebhook: orCtx.IsSystemWebhook,
 	}
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.CreateWebhook(w); err != nil {
+	} else if err := webhook.CreateWebhook(db.DefaultContext, w); err != nil {
 		ctx.ServerError("CreateWebhook", err)
 		return
 	}
@@ -235,16 +237,16 @@ func GiteaHooksNewPost(ctx *context.Context) {
 // GogsHooksNewPost response for creating webhook
 func GogsHooksNewPost(ctx *context.Context) {
 	form := web.GetForm(ctx).(*forms.NewGogshookForm)
-	newGogsWebhookPost(ctx, *form, models.GOGS)
+	newGogsWebhookPost(ctx, *form, webhook.GOGS)
 }
 
 // newGogsWebhookPost response for creating gogs hook
-func newGogsWebhookPost(ctx *context.Context, form forms.NewGogshookForm, kind models.HookType) {
+func newGogsWebhookPost(ctx *context.Context, form forms.NewGogshookForm, kind webhook.HookType) {
 	ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
 	ctx.Data["PageIsSettingsHooks"] = true
 	ctx.Data["PageIsSettingsHooksNew"] = true
-	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
-	ctx.Data["HookType"] = models.GOGS
+	ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
+	ctx.Data["HookType"] = webhook.GOGS
 
 	orCtx, err := getOrgRepoCtx(ctx)
 	if err != nil {
@@ -258,12 +260,12 @@ func newGogsWebhookPost(ctx *context.Context, form forms.NewGogshookForm, kind m
 		return
 	}
 
-	contentType := models.ContentTypeJSON
-	if models.HookContentType(form.ContentType) == models.ContentTypeForm {
-		contentType = models.ContentTypeForm
+	contentType := webhook.ContentTypeJSON
+	if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
+		contentType = webhook.ContentTypeForm
 	}
 
-	w := &models.Webhook{
+	w := &webhook.Webhook{
 		RepoID:          orCtx.RepoID,
 		URL:             form.PayloadURL,
 		ContentType:     contentType,
@@ -277,7 +279,7 @@ func newGogsWebhookPost(ctx *context.Context, form forms.NewGogshookForm, kind m
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.CreateWebhook(w); err != nil {
+	} else if err := webhook.CreateWebhook(db.DefaultContext, w); err != nil {
 		ctx.ServerError("CreateWebhook", err)
 		return
 	}
@@ -292,8 +294,8 @@ func DiscordHooksNewPost(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.settings")
 	ctx.Data["PageIsSettingsHooks"] = true
 	ctx.Data["PageIsSettingsHooksNew"] = true
-	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
-	ctx.Data["HookType"] = models.DISCORD
+	ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
+	ctx.Data["HookType"] = webhook.DISCORD
 
 	orCtx, err := getOrgRepoCtx(ctx)
 	if err != nil {
@@ -306,7 +308,7 @@ func DiscordHooksNewPost(ctx *context.Context) {
 		return
 	}
 
-	meta, err := json.Marshal(&webhook.DiscordMeta{
+	meta, err := json.Marshal(&webhook_service.DiscordMeta{
 		Username: form.Username,
 		IconURL:  form.IconURL,
 	})
@@ -315,13 +317,13 @@ func DiscordHooksNewPost(ctx *context.Context) {
 		return
 	}
 
-	w := &models.Webhook{
+	w := &webhook.Webhook{
 		RepoID:          orCtx.RepoID,
 		URL:             form.PayloadURL,
-		ContentType:     models.ContentTypeJSON,
+		ContentType:     webhook.ContentTypeJSON,
 		HookEvent:       ParseHookEvent(form.WebhookForm),
 		IsActive:        form.Active,
-		Type:            models.DISCORD,
+		Type:            webhook.DISCORD,
 		Meta:            string(meta),
 		OrgID:           orCtx.OrgID,
 		IsSystemWebhook: orCtx.IsSystemWebhook,
@@ -329,7 +331,7 @@ func DiscordHooksNewPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.CreateWebhook(w); err != nil {
+	} else if err := webhook.CreateWebhook(db.DefaultContext, w); err != nil {
 		ctx.ServerError("CreateWebhook", err)
 		return
 	}
@@ -344,8 +346,8 @@ func DingtalkHooksNewPost(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.settings")
 	ctx.Data["PageIsSettingsHooks"] = true
 	ctx.Data["PageIsSettingsHooksNew"] = true
-	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
-	ctx.Data["HookType"] = models.DINGTALK
+	ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
+	ctx.Data["HookType"] = webhook.DINGTALK
 
 	orCtx, err := getOrgRepoCtx(ctx)
 	if err != nil {
@@ -358,13 +360,13 @@ func DingtalkHooksNewPost(ctx *context.Context) {
 		return
 	}
 
-	w := &models.Webhook{
+	w := &webhook.Webhook{
 		RepoID:          orCtx.RepoID,
 		URL:             form.PayloadURL,
-		ContentType:     models.ContentTypeJSON,
+		ContentType:     webhook.ContentTypeJSON,
 		HookEvent:       ParseHookEvent(form.WebhookForm),
 		IsActive:        form.Active,
-		Type:            models.DINGTALK,
+		Type:            webhook.DINGTALK,
 		Meta:            "",
 		OrgID:           orCtx.OrgID,
 		IsSystemWebhook: orCtx.IsSystemWebhook,
@@ -372,7 +374,7 @@ func DingtalkHooksNewPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.CreateWebhook(w); err != nil {
+	} else if err := webhook.CreateWebhook(db.DefaultContext, w); err != nil {
 		ctx.ServerError("CreateWebhook", err)
 		return
 	}
@@ -387,8 +389,8 @@ func TelegramHooksNewPost(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.settings")
 	ctx.Data["PageIsSettingsHooks"] = true
 	ctx.Data["PageIsSettingsHooksNew"] = true
-	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
-	ctx.Data["HookType"] = models.TELEGRAM
+	ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
+	ctx.Data["HookType"] = webhook.TELEGRAM
 
 	orCtx, err := getOrgRepoCtx(ctx)
 	if err != nil {
@@ -401,7 +403,7 @@ func TelegramHooksNewPost(ctx *context.Context) {
 		return
 	}
 
-	meta, err := json.Marshal(&webhook.TelegramMeta{
+	meta, err := json.Marshal(&webhook_service.TelegramMeta{
 		BotToken: form.BotToken,
 		ChatID:   form.ChatID,
 	})
@@ -410,13 +412,13 @@ func TelegramHooksNewPost(ctx *context.Context) {
 		return
 	}
 
-	w := &models.Webhook{
+	w := &webhook.Webhook{
 		RepoID:          orCtx.RepoID,
 		URL:             fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", form.BotToken, form.ChatID),
-		ContentType:     models.ContentTypeJSON,
+		ContentType:     webhook.ContentTypeJSON,
 		HookEvent:       ParseHookEvent(form.WebhookForm),
 		IsActive:        form.Active,
-		Type:            models.TELEGRAM,
+		Type:            webhook.TELEGRAM,
 		Meta:            string(meta),
 		OrgID:           orCtx.OrgID,
 		IsSystemWebhook: orCtx.IsSystemWebhook,
@@ -424,7 +426,7 @@ func TelegramHooksNewPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.CreateWebhook(w); err != nil {
+	} else if err := webhook.CreateWebhook(db.DefaultContext, w); err != nil {
 		ctx.ServerError("CreateWebhook", err)
 		return
 	}
@@ -439,8 +441,8 @@ func MatrixHooksNewPost(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.settings")
 	ctx.Data["PageIsSettingsHooks"] = true
 	ctx.Data["PageIsSettingsHooksNew"] = true
-	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
-	ctx.Data["HookType"] = models.MATRIX
+	ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
+	ctx.Data["HookType"] = webhook.MATRIX
 
 	orCtx, err := getOrgRepoCtx(ctx)
 	if err != nil {
@@ -453,7 +455,7 @@ func MatrixHooksNewPost(ctx *context.Context) {
 		return
 	}
 
-	meta, err := json.Marshal(&webhook.MatrixMeta{
+	meta, err := json.Marshal(&webhook_service.MatrixMeta{
 		HomeserverURL: form.HomeserverURL,
 		Room:          form.RoomID,
 		AccessToken:   form.AccessToken,
@@ -464,14 +466,14 @@ func MatrixHooksNewPost(ctx *context.Context) {
 		return
 	}
 
-	w := &models.Webhook{
+	w := &webhook.Webhook{
 		RepoID:          orCtx.RepoID,
 		URL:             fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, form.RoomID),
-		ContentType:     models.ContentTypeJSON,
+		ContentType:     webhook.ContentTypeJSON,
 		HTTPMethod:      "PUT",
 		HookEvent:       ParseHookEvent(form.WebhookForm),
 		IsActive:        form.Active,
-		Type:            models.MATRIX,
+		Type:            webhook.MATRIX,
 		Meta:            string(meta),
 		OrgID:           orCtx.OrgID,
 		IsSystemWebhook: orCtx.IsSystemWebhook,
@@ -479,7 +481,7 @@ func MatrixHooksNewPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.CreateWebhook(w); err != nil {
+	} else if err := webhook.CreateWebhook(db.DefaultContext, w); err != nil {
 		ctx.ServerError("CreateWebhook", err)
 		return
 	}
@@ -494,8 +496,8 @@ func MSTeamsHooksNewPost(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.settings")
 	ctx.Data["PageIsSettingsHooks"] = true
 	ctx.Data["PageIsSettingsHooksNew"] = true
-	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
-	ctx.Data["HookType"] = models.MSTEAMS
+	ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
+	ctx.Data["HookType"] = webhook.MSTEAMS
 
 	orCtx, err := getOrgRepoCtx(ctx)
 	if err != nil {
@@ -508,13 +510,13 @@ func MSTeamsHooksNewPost(ctx *context.Context) {
 		return
 	}
 
-	w := &models.Webhook{
+	w := &webhook.Webhook{
 		RepoID:          orCtx.RepoID,
 		URL:             form.PayloadURL,
-		ContentType:     models.ContentTypeJSON,
+		ContentType:     webhook.ContentTypeJSON,
 		HookEvent:       ParseHookEvent(form.WebhookForm),
 		IsActive:        form.Active,
-		Type:            models.MSTEAMS,
+		Type:            webhook.MSTEAMS,
 		Meta:            "",
 		OrgID:           orCtx.OrgID,
 		IsSystemWebhook: orCtx.IsSystemWebhook,
@@ -522,7 +524,7 @@ func MSTeamsHooksNewPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.CreateWebhook(w); err != nil {
+	} else if err := webhook.CreateWebhook(db.DefaultContext, w); err != nil {
 		ctx.ServerError("CreateWebhook", err)
 		return
 	}
@@ -537,8 +539,8 @@ func SlackHooksNewPost(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.settings")
 	ctx.Data["PageIsSettingsHooks"] = true
 	ctx.Data["PageIsSettingsHooksNew"] = true
-	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
-	ctx.Data["HookType"] = models.SLACK
+	ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
+	ctx.Data["HookType"] = webhook.SLACK
 
 	orCtx, err := getOrgRepoCtx(ctx)
 	if err != nil {
@@ -557,7 +559,7 @@ func SlackHooksNewPost(ctx *context.Context) {
 		return
 	}
 
-	meta, err := json.Marshal(&webhook.SlackMeta{
+	meta, err := json.Marshal(&webhook_service.SlackMeta{
 		Channel:  strings.TrimSpace(form.Channel),
 		Username: form.Username,
 		IconURL:  form.IconURL,
@@ -568,13 +570,13 @@ func SlackHooksNewPost(ctx *context.Context) {
 		return
 	}
 
-	w := &models.Webhook{
+	w := &webhook.Webhook{
 		RepoID:          orCtx.RepoID,
 		URL:             form.PayloadURL,
-		ContentType:     models.ContentTypeJSON,
+		ContentType:     webhook.ContentTypeJSON,
 		HookEvent:       ParseHookEvent(form.WebhookForm),
 		IsActive:        form.Active,
-		Type:            models.SLACK,
+		Type:            webhook.SLACK,
 		Meta:            string(meta),
 		OrgID:           orCtx.OrgID,
 		IsSystemWebhook: orCtx.IsSystemWebhook,
@@ -582,7 +584,7 @@ func SlackHooksNewPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.CreateWebhook(w); err != nil {
+	} else if err := webhook.CreateWebhook(db.DefaultContext, w); err != nil {
 		ctx.ServerError("CreateWebhook", err)
 		return
 	}
@@ -597,8 +599,8 @@ func FeishuHooksNewPost(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.settings")
 	ctx.Data["PageIsSettingsHooks"] = true
 	ctx.Data["PageIsSettingsHooksNew"] = true
-	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
-	ctx.Data["HookType"] = models.FEISHU
+	ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
+	ctx.Data["HookType"] = webhook.FEISHU
 
 	orCtx, err := getOrgRepoCtx(ctx)
 	if err != nil {
@@ -611,13 +613,13 @@ func FeishuHooksNewPost(ctx *context.Context) {
 		return
 	}
 
-	w := &models.Webhook{
+	w := &webhook.Webhook{
 		RepoID:          orCtx.RepoID,
 		URL:             form.PayloadURL,
-		ContentType:     models.ContentTypeJSON,
+		ContentType:     webhook.ContentTypeJSON,
 		HookEvent:       ParseHookEvent(form.WebhookForm),
 		IsActive:        form.Active,
-		Type:            models.FEISHU,
+		Type:            webhook.FEISHU,
 		Meta:            "",
 		OrgID:           orCtx.OrgID,
 		IsSystemWebhook: orCtx.IsSystemWebhook,
@@ -625,7 +627,7 @@ func FeishuHooksNewPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.CreateWebhook(w); err != nil {
+	} else if err := webhook.CreateWebhook(db.DefaultContext, w); err != nil {
 		ctx.ServerError("CreateWebhook", err)
 		return
 	}
@@ -641,8 +643,8 @@ func WechatworkHooksNewPost(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.settings")
 	ctx.Data["PageIsSettingsHooks"] = true
 	ctx.Data["PageIsSettingsHooksNew"] = true
-	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
-	ctx.Data["HookType"] = models.WECHATWORK
+	ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
+	ctx.Data["HookType"] = webhook.WECHATWORK
 
 	orCtx, err := getOrgRepoCtx(ctx)
 	if err != nil {
@@ -655,13 +657,13 @@ func WechatworkHooksNewPost(ctx *context.Context) {
 		return
 	}
 
-	w := &models.Webhook{
+	w := &webhook.Webhook{
 		RepoID:          orCtx.RepoID,
 		URL:             form.PayloadURL,
-		ContentType:     models.ContentTypeJSON,
+		ContentType:     webhook.ContentTypeJSON,
 		HookEvent:       ParseHookEvent(form.WebhookForm),
 		IsActive:        form.Active,
-		Type:            models.WECHATWORK,
+		Type:            webhook.WECHATWORK,
 		Meta:            "",
 		OrgID:           orCtx.OrgID,
 		IsSystemWebhook: orCtx.IsSystemWebhook,
@@ -669,7 +671,7 @@ func WechatworkHooksNewPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.CreateWebhook(w); err != nil {
+	} else if err := webhook.CreateWebhook(db.DefaultContext, w); err != nil {
 		ctx.ServerError("CreateWebhook", err)
 		return
 	}
@@ -678,7 +680,7 @@ func WechatworkHooksNewPost(ctx *context.Context) {
 	ctx.Redirect(orCtx.Link)
 }
 
-func checkWebhook(ctx *context.Context) (*orgRepoCtx, *models.Webhook) {
+func checkWebhook(ctx *context.Context) (*orgRepoCtx, *webhook.Webhook) {
 	ctx.Data["RequireHighlightJS"] = true
 
 	orCtx, err := getOrgRepoCtx(ctx)
@@ -688,16 +690,16 @@ func checkWebhook(ctx *context.Context) (*orgRepoCtx, *models.Webhook) {
 	}
 	ctx.Data["BaseLink"] = orCtx.Link
 
-	var w *models.Webhook
+	var w *webhook.Webhook
 	if orCtx.RepoID > 0 {
-		w, err = models.GetWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
+		w, err = webhook.GetWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
 	} else if orCtx.OrgID > 0 {
-		w, err = models.GetWebhookByOrgID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
+		w, err = webhook.GetWebhookByOrgID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
 	} else if orCtx.IsAdmin {
-		w, err = models.GetSystemOrDefaultWebhook(ctx.ParamsInt64(":id"))
+		w, err = webhook.GetSystemOrDefaultWebhook(ctx.ParamsInt64(":id"))
 	}
 	if err != nil || w == nil {
-		if models.IsErrWebhookNotExist(err) {
+		if webhook.IsErrWebhookNotExist(err) {
 			ctx.NotFound("GetWebhookByID", nil)
 		} else {
 			ctx.ServerError("GetWebhookByID", err)
@@ -707,14 +709,14 @@ func checkWebhook(ctx *context.Context) (*orgRepoCtx, *models.Webhook) {
 
 	ctx.Data["HookType"] = w.Type
 	switch w.Type {
-	case models.SLACK:
-		ctx.Data["SlackHook"] = webhook.GetSlackHook(w)
-	case models.DISCORD:
-		ctx.Data["DiscordHook"] = webhook.GetDiscordHook(w)
-	case models.TELEGRAM:
-		ctx.Data["TelegramHook"] = webhook.GetTelegramHook(w)
-	case models.MATRIX:
-		ctx.Data["MatrixHook"] = webhook.GetMatrixHook(w)
+	case webhook.SLACK:
+		ctx.Data["SlackHook"] = webhook_service.GetSlackHook(w)
+	case webhook.DISCORD:
+		ctx.Data["DiscordHook"] = webhook_service.GetDiscordHook(w)
+	case webhook.TELEGRAM:
+		ctx.Data["TelegramHook"] = webhook_service.GetTelegramHook(w)
+	case webhook.MATRIX:
+		ctx.Data["MatrixHook"] = webhook_service.GetMatrixHook(w)
 	}
 
 	ctx.Data["History"], err = w.History(1)
@@ -757,9 +759,9 @@ func WebHooksEditPost(ctx *context.Context) {
 		return
 	}
 
-	contentType := models.ContentTypeJSON
-	if models.HookContentType(form.ContentType) == models.ContentTypeForm {
-		contentType = models.ContentTypeForm
+	contentType := webhook.ContentTypeJSON
+	if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
+		contentType = webhook.ContentTypeForm
 	}
 
 	w.URL = form.PayloadURL
@@ -771,7 +773,7 @@ func WebHooksEditPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.UpdateWebhook(w); err != nil {
+	} else if err := webhook.UpdateWebhook(w); err != nil {
 		ctx.ServerError("WebHooksEditPost", err)
 		return
 	}
@@ -798,9 +800,9 @@ func GogsHooksEditPost(ctx *context.Context) {
 		return
 	}
 
-	contentType := models.ContentTypeJSON
-	if models.HookContentType(form.ContentType) == models.ContentTypeForm {
-		contentType = models.ContentTypeForm
+	contentType := webhook.ContentTypeJSON
+	if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
+		contentType = webhook.ContentTypeForm
 	}
 
 	w.URL = form.PayloadURL
@@ -811,7 +813,7 @@ func GogsHooksEditPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.UpdateWebhook(w); err != nil {
+	} else if err := webhook.UpdateWebhook(w); err != nil {
 		ctx.ServerError("GogsHooksEditPost", err)
 		return
 	}
@@ -844,7 +846,7 @@ func SlackHooksEditPost(ctx *context.Context) {
 		return
 	}
 
-	meta, err := json.Marshal(&webhook.SlackMeta{
+	meta, err := json.Marshal(&webhook_service.SlackMeta{
 		Channel:  strings.TrimSpace(form.Channel),
 		Username: form.Username,
 		IconURL:  form.IconURL,
@@ -862,7 +864,7 @@ func SlackHooksEditPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.UpdateWebhook(w); err != nil {
+	} else if err := webhook.UpdateWebhook(w); err != nil {
 		ctx.ServerError("UpdateWebhook", err)
 		return
 	}
@@ -889,7 +891,7 @@ func DiscordHooksEditPost(ctx *context.Context) {
 		return
 	}
 
-	meta, err := json.Marshal(&webhook.DiscordMeta{
+	meta, err := json.Marshal(&webhook_service.DiscordMeta{
 		Username: form.Username,
 		IconURL:  form.IconURL,
 	})
@@ -905,7 +907,7 @@ func DiscordHooksEditPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.UpdateWebhook(w); err != nil {
+	} else if err := webhook.UpdateWebhook(w); err != nil {
 		ctx.ServerError("UpdateWebhook", err)
 		return
 	}
@@ -938,7 +940,7 @@ func DingtalkHooksEditPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.UpdateWebhook(w); err != nil {
+	} else if err := webhook.UpdateWebhook(w); err != nil {
 		ctx.ServerError("UpdateWebhook", err)
 		return
 	}
@@ -965,7 +967,7 @@ func TelegramHooksEditPost(ctx *context.Context) {
 		return
 	}
 
-	meta, err := json.Marshal(&webhook.TelegramMeta{
+	meta, err := json.Marshal(&webhook_service.TelegramMeta{
 		BotToken: form.BotToken,
 		ChatID:   form.ChatID,
 	})
@@ -980,7 +982,7 @@ func TelegramHooksEditPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.UpdateWebhook(w); err != nil {
+	} else if err := webhook.UpdateWebhook(w); err != nil {
 		ctx.ServerError("UpdateWebhook", err)
 		return
 	}
@@ -1007,7 +1009,7 @@ func MatrixHooksEditPost(ctx *context.Context) {
 		return
 	}
 
-	meta, err := json.Marshal(&webhook.MatrixMeta{
+	meta, err := json.Marshal(&webhook_service.MatrixMeta{
 		HomeserverURL: form.HomeserverURL,
 		Room:          form.RoomID,
 		AccessToken:   form.AccessToken,
@@ -1025,7 +1027,7 @@ func MatrixHooksEditPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.UpdateWebhook(w); err != nil {
+	} else if err := webhook.UpdateWebhook(w); err != nil {
 		ctx.ServerError("UpdateWebhook", err)
 		return
 	}
@@ -1058,7 +1060,7 @@ func MSTeamsHooksEditPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.UpdateWebhook(w); err != nil {
+	} else if err := webhook.UpdateWebhook(w); err != nil {
 		ctx.ServerError("UpdateWebhook", err)
 		return
 	}
@@ -1091,7 +1093,7 @@ func FeishuHooksEditPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.UpdateWebhook(w); err != nil {
+	} else if err := webhook.UpdateWebhook(w); err != nil {
 		ctx.ServerError("UpdateWebhook", err)
 		return
 	}
@@ -1124,7 +1126,7 @@ func WechatworkHooksEditPost(ctx *context.Context) {
 	if err := w.UpdateEvent(); err != nil {
 		ctx.ServerError("UpdateEvent", err)
 		return
-	} else if err := models.UpdateWebhook(w); err != nil {
+	} else if err := webhook.UpdateWebhook(w); err != nil {
 		ctx.ServerError("UpdateWebhook", err)
 		return
 	}
@@ -1136,7 +1138,7 @@ func WechatworkHooksEditPost(ctx *context.Context) {
 // TestWebhook test if web hook is work fine
 func TestWebhook(ctx *context.Context) {
 	hookID := ctx.ParamsInt64(":id")
-	w, err := models.GetWebhookByRepoID(ctx.Repo.Repository.ID, hookID)
+	w, err := webhook.GetWebhookByRepoID(ctx.Repo.Repository.ID, hookID)
 	if err != nil {
 		ctx.Flash.Error("GetWebhookByID: " + err.Error())
 		ctx.Status(500)
@@ -1181,7 +1183,7 @@ func TestWebhook(ctx *context.Context) {
 		Pusher:     apiUser,
 		Sender:     apiUser,
 	}
-	if err := webhook.PrepareWebhook(w, ctx.Repo.Repository, models.HookEventPush, p); err != nil {
+	if err := webhook_service.PrepareWebhook(w, ctx.Repo.Repository, webhook.HookEventPush, p); err != nil {
 		ctx.Flash.Error("PrepareWebhook: " + err.Error())
 		ctx.Status(500)
 	} else {
@@ -1192,7 +1194,7 @@ func TestWebhook(ctx *context.Context) {
 
 // DeleteWebhook delete a webhook
 func DeleteWebhook(ctx *context.Context) {
-	if err := models.DeleteWebhookByRepoID(ctx.Repo.Repository.ID, ctx.FormInt64("id")); err != nil {
+	if err := webhook.DeleteWebhookByRepoID(ctx.Repo.Repository.ID, ctx.FormInt64("id")); err != nil {
 		ctx.Flash.Error("DeleteWebhookByRepoID: " + err.Error())
 	} else {
 		ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
diff --git a/services/auth/source/ldap/source_sync.go b/services/auth/source/ldap/source_sync.go
index 2df97aabd3..4508d1514a 100644
--- a/services/auth/source/ldap/source_sync.go
+++ b/services/auth/source/ldap/source_sync.go
@@ -11,6 +11,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/modules/log"
 )
 
@@ -31,7 +32,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
 	select {
 	case <-ctx.Done():
 		log.Warn("SyncExternalUsers: Cancelled before update of %s", source.loginSource.Name)
-		return models.ErrCancelledf("Before update of %s", source.loginSource.Name)
+		return db.ErrCancelledf("Before update of %s", source.loginSource.Name)
 	default:
 	}
 
@@ -70,7 +71,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
 					log.Error("RewriteAllPublicKeys: %v", err)
 				}
 			}
-			return models.ErrCancelledf("During update of %s before completed update of users", source.loginSource.Name)
+			return db.ErrCancelledf("During update of %s before completed update of users", source.loginSource.Name)
 		default:
 		}
 		if len(su.Username) == 0 {
@@ -177,7 +178,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
 	select {
 	case <-ctx.Done():
 		log.Warn("SyncExternalUsers: Cancelled during update of %s before delete users", source.loginSource.Name)
-		return models.ErrCancelledf("During update of %s before delete users", source.loginSource.Name)
+		return db.ErrCancelledf("During update of %s before delete users", source.loginSource.Name)
 	default:
 	}
 
diff --git a/services/auth/sync.go b/services/auth/sync.go
index 6d69650e5b..494dfc3a38 100644
--- a/services/auth/sync.go
+++ b/services/auth/sync.go
@@ -7,7 +7,7 @@ package auth
 import (
 	"context"
 
-	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/login"
 	"code.gitea.io/gitea/modules/log"
 )
@@ -29,7 +29,7 @@ func SyncExternalUsers(ctx context.Context, updateExisting bool) error {
 		select {
 		case <-ctx.Done():
 			log.Warn("SyncExternalUsers: Cancelled before update of %s", s.Name)
-			return models.ErrCancelledf("Before update of %s", s.Name)
+			return db.ErrCancelledf("Before update of %s", s.Name)
 		default:
 		}
 
diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go
index 04cec4c1c4..da7052fd9b 100644
--- a/services/webhook/deliver.go
+++ b/services/webhook/deliver.go
@@ -22,19 +22,20 @@ import (
 	"syscall"
 	"time"
 
-	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/proxy"
 	"code.gitea.io/gitea/modules/setting"
+
 	"github.com/gobwas/glob"
 )
 
 var contextKeyWebhookRequest interface{} = "contextKeyWebhookRequest"
 
 // Deliver deliver hook task
-func Deliver(t *models.HookTask) error {
-	w, err := models.GetWebhookByID(t.HookID)
+func Deliver(t *webhook_model.HookTask) error {
+	w, err := webhook_model.GetWebhookByID(t.HookID)
 	if err != nil {
 		return err
 	}
@@ -58,14 +59,14 @@ func Deliver(t *models.HookTask) error {
 		fallthrough
 	case http.MethodPost:
 		switch w.ContentType {
-		case models.ContentTypeJSON:
+		case webhook_model.ContentTypeJSON:
 			req, err = http.NewRequest("POST", w.URL, strings.NewReader(t.PayloadContent))
 			if err != nil {
 				return err
 			}
 
 			req.Header.Set("Content-Type", "application/json")
-		case models.ContentTypeForm:
+		case webhook_model.ContentTypeForm:
 			var forms = url.Values{
 				"payload": []string{t.PayloadContent},
 			}
@@ -91,7 +92,7 @@ func Deliver(t *models.HookTask) error {
 		}
 	case http.MethodPut:
 		switch w.Type {
-		case models.MATRIX:
+		case webhook_model.MATRIX:
 			req, err = getMatrixHookRequest(w, t)
 			if err != nil {
 				return err
@@ -133,7 +134,7 @@ func Deliver(t *models.HookTask) error {
 	req.Header["X-GitHub-Event-Type"] = []string{eventType}
 
 	// Record delivery information.
-	t.RequestInfo = &models.HookRequest{
+	t.RequestInfo = &webhook_model.HookRequest{
 		URL:        req.URL.String(),
 		HTTPMethod: req.Method,
 		Headers:    map[string]string{},
@@ -142,7 +143,7 @@ func Deliver(t *models.HookTask) error {
 		t.RequestInfo.Headers[k] = strings.Join(vals, ",")
 	}
 
-	t.ResponseInfo = &models.HookResponse{
+	t.ResponseInfo = &webhook_model.HookResponse{
 		Headers: map[string]string{},
 	}
 
@@ -154,17 +155,17 @@ func Deliver(t *models.HookTask) error {
 			log.Trace("Hook delivery failed: %s", t.UUID)
 		}
 
-		if err := models.UpdateHookTask(t); err != nil {
+		if err := webhook_model.UpdateHookTask(t); err != nil {
 			log.Error("UpdateHookTask [%d]: %v", t.ID, err)
 		}
 
 		// Update webhook last delivery status.
 		if t.IsSucceed {
-			w.LastStatus = models.HookStatusSucceed
+			w.LastStatus = webhook_model.HookStatusSucceed
 		} else {
-			w.LastStatus = models.HookStatusFail
+			w.LastStatus = webhook_model.HookStatusFail
 		}
-		if err = models.UpdateWebhookLastStatus(w); err != nil {
+		if err = webhook_model.UpdateWebhookLastStatus(w); err != nil {
 			log.Error("UpdateWebhookLastStatus: %v", err)
 			return
 		}
@@ -206,7 +207,7 @@ func DeliverHooks(ctx context.Context) {
 		return
 	default:
 	}
-	tasks, err := models.FindUndeliveredHookTasks()
+	tasks, err := webhook_model.FindUndeliveredHookTasks()
 	if err != nil {
 		log.Error("DeliverHooks: %v", err)
 		return
@@ -240,7 +241,7 @@ func DeliverHooks(ctx context.Context) {
 				continue
 			}
 
-			tasks, err := models.FindRepoUndeliveredHookTasks(repoID)
+			tasks, err := webhook_model.FindRepoUndeliveredHookTasks(repoID)
 			if err != nil {
 				log.Error("Get repository [%d] hook tasks: %v", repoID, err)
 				continue
diff --git a/services/webhook/dingtalk.go b/services/webhook/dingtalk.go
index 7d352db18c..88e4078922 100644
--- a/services/webhook/dingtalk.go
+++ b/services/webhook/dingtalk.go
@@ -9,7 +9,7 @@ import (
 	"net/url"
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/json"
 	api "code.gitea.io/gitea/modules/structs"
@@ -123,7 +123,7 @@ func (d *DingtalkPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader,
 }
 
 // Review implements PayloadConvertor Review method
-func (d *DingtalkPayload) Review(p *api.PullRequestPayload, event models.HookEventType) (api.Payloader, error) {
+func (d *DingtalkPayload) Review(p *api.PullRequestPayload, event webhook_model.HookEventType) (api.Payloader, error) {
 	var text, title string
 	switch p.Action {
 	case api.HookIssueReviewed:
@@ -185,6 +185,6 @@ func createDingtalkPayload(title, text, singleTitle, singleURL string) *Dingtalk
 }
 
 // GetDingtalkPayload converts a ding talk webhook into a DingtalkPayload
-func GetDingtalkPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) {
+func GetDingtalkPayload(p api.Payloader, event webhook_model.HookEventType, meta string) (api.Payloader, error) {
 	return convertPayloader(new(DingtalkPayload), p, event)
 }
diff --git a/services/webhook/dingtalk_test.go b/services/webhook/dingtalk_test.go
index 5c1d187523..b66b5e43a8 100644
--- a/services/webhook/dingtalk_test.go
+++ b/services/webhook/dingtalk_test.go
@@ -8,7 +8,7 @@ import (
 	"net/url"
 	"testing"
 
-	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -163,7 +163,7 @@ func TestDingTalkPayload(t *testing.T) {
 		p.Action = api.HookIssueReviewed
 
 		d := new(DingtalkPayload)
-		pl, err := d.Review(p, models.HookEventPullRequestReviewApproved)
+		pl, err := d.Review(p, webhook_model.HookEventPullRequestReviewApproved)
 		require.NoError(t, err)
 		require.NotNil(t, pl)
 		require.IsType(t, &DingtalkPayload{}, pl)
diff --git a/services/webhook/discord.go b/services/webhook/discord.go
index 9a8f8a27a5..3de50a8a2f 100644
--- a/services/webhook/discord.go
+++ b/services/webhook/discord.go
@@ -10,7 +10,7 @@ import (
 	"strconv"
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
@@ -66,7 +66,7 @@ type (
 )
 
 // GetDiscordHook returns discord metadata
-func GetDiscordHook(w *models.Webhook) *DiscordMeta {
+func GetDiscordHook(w *webhook_model.Webhook) *DiscordMeta {
 	s := &DiscordMeta{}
 	if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
 		log.Error("webhook.GetDiscordHook(%d): %v", w.ID, err)
@@ -191,7 +191,7 @@ func (d *DiscordPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader,
 }
 
 // Review implements PayloadConvertor Review method
-func (d *DiscordPayload) Review(p *api.PullRequestPayload, event models.HookEventType) (api.Payloader, error) {
+func (d *DiscordPayload) Review(p *api.PullRequestPayload, event webhook_model.HookEventType) (api.Payloader, error) {
 	var text, title string
 	var color int
 	switch p.Action {
@@ -205,11 +205,11 @@ func (d *DiscordPayload) Review(p *api.PullRequestPayload, event models.HookEven
 		text = p.Review.Content
 
 		switch event {
-		case models.HookEventPullRequestReviewApproved:
+		case webhook_model.HookEventPullRequestReviewApproved:
 			color = greenColor
-		case models.HookEventPullRequestReviewRejected:
+		case webhook_model.HookEventPullRequestReviewRejected:
 			color = redColor
-		case models.HookEventPullRequestComment:
+		case webhook_model.HookEventPullRequestComment:
 			color = greyColor
 		default:
 			color = yellowColor
@@ -244,7 +244,7 @@ func (d *DiscordPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
 }
 
 // GetDiscordPayload converts a discord webhook into a DiscordPayload
-func GetDiscordPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) {
+func GetDiscordPayload(p api.Payloader, event webhook_model.HookEventType, meta string) (api.Payloader, error) {
 	s := new(DiscordPayload)
 
 	discord := &DiscordMeta{}
@@ -257,14 +257,14 @@ func GetDiscordPayload(p api.Payloader, event models.HookEventType, meta string)
 	return convertPayloader(s, p, event)
 }
 
-func parseHookPullRequestEventType(event models.HookEventType) (string, error) {
+func parseHookPullRequestEventType(event webhook_model.HookEventType) (string, error) {
 	switch event {
 
-	case models.HookEventPullRequestReviewApproved:
+	case webhook_model.HookEventPullRequestReviewApproved:
 		return "approved", nil
-	case models.HookEventPullRequestReviewRejected:
+	case webhook_model.HookEventPullRequestReviewRejected:
 		return "rejected", nil
-	case models.HookEventPullRequestComment:
+	case webhook_model.HookEventPullRequestComment:
 		return "comment", nil
 
 	default:
diff --git a/services/webhook/discord_test.go b/services/webhook/discord_test.go
index fd7d2856c7..8fe20c0102 100644
--- a/services/webhook/discord_test.go
+++ b/services/webhook/discord_test.go
@@ -7,7 +7,7 @@ package webhook
 import (
 	"testing"
 
-	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
 
@@ -180,7 +180,7 @@ func TestDiscordPayload(t *testing.T) {
 		p.Action = api.HookIssueReviewed
 
 		d := new(DiscordPayload)
-		pl, err := d.Review(p, models.HookEventPullRequestReviewApproved)
+		pl, err := d.Review(p, webhook_model.HookEventPullRequestReviewApproved)
 		require.NoError(t, err)
 		require.NotNil(t, pl)
 		require.IsType(t, &DiscordPayload{}, pl)
diff --git a/services/webhook/feishu.go b/services/webhook/feishu.go
index 4428d54f92..18858c0994 100644
--- a/services/webhook/feishu.go
+++ b/services/webhook/feishu.go
@@ -8,7 +8,7 @@ import (
 	"fmt"
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/json"
 	api "code.gitea.io/gitea/modules/structs"
@@ -120,7 +120,7 @@ func (f *FeishuPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, e
 }
 
 // Review implements PayloadConvertor Review method
-func (f *FeishuPayload) Review(p *api.PullRequestPayload, event models.HookEventType) (api.Payloader, error) {
+func (f *FeishuPayload) Review(p *api.PullRequestPayload, event webhook_model.HookEventType) (api.Payloader, error) {
 	action, err := parseHookPullRequestEventType(event)
 	if err != nil {
 		return nil, err
@@ -155,6 +155,6 @@ func (f *FeishuPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
 }
 
 // GetFeishuPayload converts a ding talk webhook into a FeishuPayload
-func GetFeishuPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) {
+func GetFeishuPayload(p api.Payloader, event webhook_model.HookEventType, meta string) (api.Payloader, error) {
 	return convertPayloader(new(FeishuPayload), p, event)
 }
diff --git a/services/webhook/feishu_test.go b/services/webhook/feishu_test.go
index 7f3508c145..d862b015e7 100644
--- a/services/webhook/feishu_test.go
+++ b/services/webhook/feishu_test.go
@@ -7,7 +7,7 @@ package webhook
 import (
 	"testing"
 
-	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -125,7 +125,7 @@ func TestFeishuPayload(t *testing.T) {
 		p.Action = api.HookIssueReviewed
 
 		d := new(FeishuPayload)
-		pl, err := d.Review(p, models.HookEventPullRequestReviewApproved)
+		pl, err := d.Review(p, webhook_model.HookEventPullRequestReviewApproved)
 		require.NoError(t, err)
 		require.NotNil(t, pl)
 		require.IsType(t, &FeishuPayload{}, pl)
diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go
index 10e0c8fd15..08adaef6fd 100644
--- a/services/webhook/matrix.go
+++ b/services/webhook/matrix.go
@@ -13,7 +13,7 @@ import (
 	"regexp"
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
@@ -37,7 +37,7 @@ var messageTypeText = map[int]string{
 }
 
 // GetMatrixHook returns Matrix metadata
-func GetMatrixHook(w *models.Webhook) *MatrixMeta {
+func GetMatrixHook(w *webhook_model.Webhook) *MatrixMeta {
 	s := &MatrixMeta{}
 	if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
 		log.Error("webhook.GetMatrixHook(%d): %v", w.ID, err)
@@ -185,7 +185,7 @@ func (m *MatrixPayloadUnsafe) PullRequest(p *api.PullRequestPayload) (api.Payloa
 }
 
 // Review implements PayloadConvertor Review method
-func (m *MatrixPayloadUnsafe) Review(p *api.PullRequestPayload, event models.HookEventType) (api.Payloader, error) {
+func (m *MatrixPayloadUnsafe) Review(p *api.PullRequestPayload, event webhook_model.HookEventType) (api.Payloader, error) {
 	senderLink := MatrixLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
 	title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
 	titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index)
@@ -222,7 +222,7 @@ func (m *MatrixPayloadUnsafe) Repository(p *api.RepositoryPayload) (api.Payloade
 }
 
 // GetMatrixPayload converts a Matrix webhook into a MatrixPayloadUnsafe
-func GetMatrixPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) {
+func GetMatrixPayload(p api.Payloader, event webhook_model.HookEventType, meta string) (api.Payloader, error) {
 	s := new(MatrixPayloadUnsafe)
 
 	matrix := &MatrixMeta{}
@@ -257,7 +257,7 @@ func getMessageBody(htmlText string) string {
 
 // getMatrixHookRequest creates a new request which contains an Authorization header.
 // The access_token is removed from t.PayloadContent
-func getMatrixHookRequest(w *models.Webhook, t *models.HookTask) (*http.Request, error) {
+func getMatrixHookRequest(w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, error) {
 	payloadunsafe := MatrixPayloadUnsafe{}
 	if err := json.Unmarshal([]byte(t.PayloadContent), &payloadunsafe); err != nil {
 		log.Error("Matrix Hook delivery failed: %v", err)
diff --git a/services/webhook/matrix_test.go b/services/webhook/matrix_test.go
index 451dff6949..3cc7c7518f 100644
--- a/services/webhook/matrix_test.go
+++ b/services/webhook/matrix_test.go
@@ -7,7 +7,7 @@ package webhook
 import (
 	"testing"
 
-	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -134,7 +134,7 @@ func TestMatrixPayload(t *testing.T) {
 		p.Action = api.HookIssueReviewed
 
 		d := new(MatrixPayloadUnsafe)
-		pl, err := d.Review(p, models.HookEventPullRequestReviewApproved)
+		pl, err := d.Review(p, webhook_model.HookEventPullRequestReviewApproved)
 		require.NoError(t, err)
 		require.NotNil(t, pl)
 		require.IsType(t, &MatrixPayloadUnsafe{}, pl)
@@ -184,9 +184,9 @@ func TestMatrixJSONPayload(t *testing.T) {
 }
 
 func TestMatrixHookRequest(t *testing.T) {
-	w := &models.Webhook{}
+	w := &webhook_model.Webhook{}
 
-	h := &models.HookTask{
+	h := &webhook_model.HookTask{
 		PayloadContent: `{
   "body": "[[user1/test](http://localhost:3000/user1/test)] user1 pushed 1 commit to [master](http://localhost:3000/user1/test/src/branch/master):\n[5175ef2](http://localhost:3000/user1/test/commit/5175ef26201c58b035a3404b3fe02b4e8d436eee): Merge pull request 'Change readme.md' (#2) from add-matrix-webhook into master\n\nReviewed-on: http://localhost:3000/user1/test/pulls/2\n - user1",
   "msgtype": "m.notice",
diff --git a/services/webhook/msteams.go b/services/webhook/msteams.go
index ed5e659058..2b88bb23ff 100644
--- a/services/webhook/msteams.go
+++ b/services/webhook/msteams.go
@@ -8,7 +8,7 @@ import (
 	"fmt"
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/json"
 	api "code.gitea.io/gitea/modules/structs"
@@ -206,7 +206,7 @@ func (m *MSTeamsPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader,
 }
 
 // Review implements PayloadConvertor Review method
-func (m *MSTeamsPayload) Review(p *api.PullRequestPayload, event models.HookEventType) (api.Payloader, error) {
+func (m *MSTeamsPayload) Review(p *api.PullRequestPayload, event webhook_model.HookEventType) (api.Payloader, error) {
 	var text, title string
 	var color int
 	switch p.Action {
@@ -220,11 +220,11 @@ func (m *MSTeamsPayload) Review(p *api.PullRequestPayload, event models.HookEven
 		text = p.Review.Content
 
 		switch event {
-		case models.HookEventPullRequestReviewApproved:
+		case webhook_model.HookEventPullRequestReviewApproved:
 			color = greenColor
-		case models.HookEventPullRequestReviewRejected:
+		case webhook_model.HookEventPullRequestReviewRejected:
 			color = redColor
-		case models.HookEventPullRequestComment:
+		case webhook_model.HookEventPullRequestComment:
 			color = greyColor
 		default:
 			color = yellowColor
@@ -283,7 +283,7 @@ func (m *MSTeamsPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
 }
 
 // GetMSTeamsPayload converts a MSTeams webhook into a MSTeamsPayload
-func GetMSTeamsPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) {
+func GetMSTeamsPayload(p api.Payloader, event webhook_model.HookEventType, meta string) (api.Payloader, error) {
 	return convertPayloader(new(MSTeamsPayload), p, event)
 }
 
diff --git a/services/webhook/msteams_test.go b/services/webhook/msteams_test.go
index 2f54c39d39..3fdf47c1ae 100644
--- a/services/webhook/msteams_test.go
+++ b/services/webhook/msteams_test.go
@@ -7,7 +7,7 @@ package webhook
 import (
 	"testing"
 
-	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -278,7 +278,7 @@ func TestMSTeamsPayload(t *testing.T) {
 		p.Action = api.HookIssueReviewed
 
 		d := new(MSTeamsPayload)
-		pl, err := d.Review(p, models.HookEventPullRequestReviewApproved)
+		pl, err := d.Review(p, webhook_model.HookEventPullRequestReviewApproved)
 		require.NoError(t, err)
 		require.NotNil(t, pl)
 		require.IsType(t, &MSTeamsPayload{}, pl)
diff --git a/services/webhook/payloader.go b/services/webhook/payloader.go
index f1cdaf6595..0e09dd1b1e 100644
--- a/services/webhook/payloader.go
+++ b/services/webhook/payloader.go
@@ -5,7 +5,7 @@
 package webhook
 
 import (
-	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	api "code.gitea.io/gitea/modules/structs"
 )
 
@@ -19,37 +19,37 @@ type PayloadConvertor interface {
 	IssueComment(*api.IssueCommentPayload) (api.Payloader, error)
 	Push(*api.PushPayload) (api.Payloader, error)
 	PullRequest(*api.PullRequestPayload) (api.Payloader, error)
-	Review(*api.PullRequestPayload, models.HookEventType) (api.Payloader, error)
+	Review(*api.PullRequestPayload, webhook_model.HookEventType) (api.Payloader, error)
 	Repository(*api.RepositoryPayload) (api.Payloader, error)
 	Release(*api.ReleasePayload) (api.Payloader, error)
 }
 
-func convertPayloader(s PayloadConvertor, p api.Payloader, event models.HookEventType) (api.Payloader, error) {
+func convertPayloader(s PayloadConvertor, p api.Payloader, event webhook_model.HookEventType) (api.Payloader, error) {
 	switch event {
-	case models.HookEventCreate:
+	case webhook_model.HookEventCreate:
 		return s.Create(p.(*api.CreatePayload))
-	case models.HookEventDelete:
+	case webhook_model.HookEventDelete:
 		return s.Delete(p.(*api.DeletePayload))
-	case models.HookEventFork:
+	case webhook_model.HookEventFork:
 		return s.Fork(p.(*api.ForkPayload))
-	case models.HookEventIssues, models.HookEventIssueAssign, models.HookEventIssueLabel, models.HookEventIssueMilestone:
+	case webhook_model.HookEventIssues, webhook_model.HookEventIssueAssign, webhook_model.HookEventIssueLabel, webhook_model.HookEventIssueMilestone:
 		return s.Issue(p.(*api.IssuePayload))
-	case models.HookEventIssueComment, models.HookEventPullRequestComment:
+	case webhook_model.HookEventIssueComment, webhook_model.HookEventPullRequestComment:
 		pl, ok := p.(*api.IssueCommentPayload)
 		if ok {
 			return s.IssueComment(pl)
 		}
 		return s.PullRequest(p.(*api.PullRequestPayload))
-	case models.HookEventPush:
+	case webhook_model.HookEventPush:
 		return s.Push(p.(*api.PushPayload))
-	case models.HookEventPullRequest, models.HookEventPullRequestAssign, models.HookEventPullRequestLabel,
-		models.HookEventPullRequestMilestone, models.HookEventPullRequestSync:
+	case webhook_model.HookEventPullRequest, webhook_model.HookEventPullRequestAssign, webhook_model.HookEventPullRequestLabel,
+		webhook_model.HookEventPullRequestMilestone, webhook_model.HookEventPullRequestSync:
 		return s.PullRequest(p.(*api.PullRequestPayload))
-	case models.HookEventPullRequestReviewApproved, models.HookEventPullRequestReviewRejected, models.HookEventPullRequestReviewComment:
+	case webhook_model.HookEventPullRequestReviewApproved, webhook_model.HookEventPullRequestReviewRejected, webhook_model.HookEventPullRequestReviewComment:
 		return s.Review(p.(*api.PullRequestPayload), event)
-	case models.HookEventRepository:
+	case webhook_model.HookEventRepository:
 		return s.Repository(p.(*api.RepositoryPayload))
-	case models.HookEventRelease:
+	case webhook_model.HookEventRelease:
 		return s.Release(p.(*api.ReleasePayload))
 	}
 	return s, nil
diff --git a/services/webhook/slack.go b/services/webhook/slack.go
index 803b5de0b2..575aedf0c0 100644
--- a/services/webhook/slack.go
+++ b/services/webhook/slack.go
@@ -9,7 +9,7 @@ import (
 	"fmt"
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
@@ -26,7 +26,7 @@ type SlackMeta struct {
 }
 
 // GetSlackHook returns slack metadata
-func GetSlackHook(w *models.Webhook) *SlackMeta {
+func GetSlackHook(w *webhook_model.Webhook) *SlackMeta {
 	s := &SlackMeta{}
 	if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
 		log.Error("webhook.GetSlackHook(%d): %v", w.ID, err)
@@ -226,7 +226,7 @@ func (s *SlackPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, er
 }
 
 // Review implements PayloadConvertor Review method
-func (s *SlackPayload) Review(p *api.PullRequestPayload, event models.HookEventType) (api.Payloader, error) {
+func (s *SlackPayload) Review(p *api.PullRequestPayload, event webhook_model.HookEventType) (api.Payloader, error) {
 	senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
 	title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
 	titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index)
@@ -273,7 +273,7 @@ func (s *SlackPayload) createPayload(text string, attachments []SlackAttachment)
 }
 
 // GetSlackPayload converts a slack webhook into a SlackPayload
-func GetSlackPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) {
+func GetSlackPayload(p api.Payloader, event webhook_model.HookEventType, meta string) (api.Payloader, error) {
 	s := new(SlackPayload)
 
 	slack := &SlackMeta{}
diff --git a/services/webhook/slack_test.go b/services/webhook/slack_test.go
index 3f279810c9..1fa7777328 100644
--- a/services/webhook/slack_test.go
+++ b/services/webhook/slack_test.go
@@ -7,7 +7,7 @@ package webhook
 import (
 	"testing"
 
-	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -125,7 +125,7 @@ func TestSlackPayload(t *testing.T) {
 		p.Action = api.HookIssueReviewed
 
 		d := new(SlackPayload)
-		pl, err := d.Review(p, models.HookEventPullRequestReviewApproved)
+		pl, err := d.Review(p, webhook_model.HookEventPullRequestReviewApproved)
 		require.NoError(t, err)
 		require.NotNil(t, pl)
 		require.IsType(t, &SlackPayload{}, pl)
diff --git a/services/webhook/telegram.go b/services/webhook/telegram.go
index c5a26f687d..93c464fe73 100644
--- a/services/webhook/telegram.go
+++ b/services/webhook/telegram.go
@@ -8,7 +8,7 @@ import (
 	"fmt"
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
@@ -32,7 +32,7 @@ type (
 )
 
 // GetTelegramHook returns telegram metadata
-func GetTelegramHook(w *models.Webhook) *TelegramMeta {
+func GetTelegramHook(w *webhook_model.Webhook) *TelegramMeta {
 	s := &TelegramMeta{}
 	if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
 		log.Error("webhook.GetTelegramHook(%d): %v", w.ID, err)
@@ -143,7 +143,7 @@ func (t *TelegramPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader,
 }
 
 // Review implements PayloadConvertor Review method
-func (t *TelegramPayload) Review(p *api.PullRequestPayload, event models.HookEventType) (api.Payloader, error) {
+func (t *TelegramPayload) Review(p *api.PullRequestPayload, event webhook_model.HookEventType) (api.Payloader, error) {
 	var text, attachmentText string
 	switch p.Action {
 	case api.HookIssueReviewed:
@@ -181,7 +181,7 @@ func (t *TelegramPayload) Release(p *api.ReleasePayload) (api.Payloader, error)
 }
 
 // GetTelegramPayload converts a telegram webhook into a TelegramPayload
-func GetTelegramPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) {
+func GetTelegramPayload(p api.Payloader, event webhook_model.HookEventType, meta string) (api.Payloader, error) {
 	return convertPayloader(new(TelegramPayload), p, event)
 }
 
diff --git a/services/webhook/telegram_test.go b/services/webhook/telegram_test.go
index 037a2481d6..6a3682847c 100644
--- a/services/webhook/telegram_test.go
+++ b/services/webhook/telegram_test.go
@@ -7,7 +7,7 @@ package webhook
 import (
 	"testing"
 
-	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -125,7 +125,7 @@ func TestTelegramPayload(t *testing.T) {
 		p.Action = api.HookIssueReviewed
 
 		d := new(TelegramPayload)
-		pl, err := d.Review(p, models.HookEventPullRequestReviewApproved)
+		pl, err := d.Review(p, webhook_model.HookEventPullRequestReviewApproved)
 		require.NoError(t, err)
 		require.NotNil(t, pl)
 		require.IsType(t, &TelegramPayload{}, pl)
diff --git a/services/webhook/webhook.go b/services/webhook/webhook.go
index 00b2ef05b8..d4fe4e3bcc 100644
--- a/services/webhook/webhook.go
+++ b/services/webhook/webhook.go
@@ -9,6 +9,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
@@ -20,42 +21,42 @@ import (
 )
 
 type webhook struct {
-	name           models.HookType
-	payloadCreator func(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error)
+	name           webhook_model.HookType
+	payloadCreator func(p api.Payloader, event webhook_model.HookEventType, meta string) (api.Payloader, error)
 }
 
 var (
-	webhooks = map[models.HookType]*webhook{
-		models.SLACK: {
-			name:           models.SLACK,
+	webhooks = map[webhook_model.HookType]*webhook{
+		webhook_model.SLACK: {
+			name:           webhook_model.SLACK,
 			payloadCreator: GetSlackPayload,
 		},
-		models.DISCORD: {
-			name:           models.DISCORD,
+		webhook_model.DISCORD: {
+			name:           webhook_model.DISCORD,
 			payloadCreator: GetDiscordPayload,
 		},
-		models.DINGTALK: {
-			name:           models.DINGTALK,
+		webhook_model.DINGTALK: {
+			name:           webhook_model.DINGTALK,
 			payloadCreator: GetDingtalkPayload,
 		},
-		models.TELEGRAM: {
-			name:           models.TELEGRAM,
+		webhook_model.TELEGRAM: {
+			name:           webhook_model.TELEGRAM,
 			payloadCreator: GetTelegramPayload,
 		},
-		models.MSTEAMS: {
-			name:           models.MSTEAMS,
+		webhook_model.MSTEAMS: {
+			name:           webhook_model.MSTEAMS,
 			payloadCreator: GetMSTeamsPayload,
 		},
-		models.FEISHU: {
-			name:           models.FEISHU,
+		webhook_model.FEISHU: {
+			name:           webhook_model.FEISHU,
 			payloadCreator: GetFeishuPayload,
 		},
-		models.MATRIX: {
-			name:           models.MATRIX,
+		webhook_model.MATRIX: {
+			name:           webhook_model.MATRIX,
 			payloadCreator: GetMatrixPayload,
 		},
-		models.WECHATWORK: {
-			name:           models.WECHATWORK,
+		webhook_model.WECHATWORK: {
+			name:           webhook_model.WECHATWORK,
 			payloadCreator: GetWechatworkPayload,
 		},
 	}
@@ -63,15 +64,15 @@ var (
 
 // RegisterWebhook registers a webhook
 func RegisterWebhook(name string, webhook *webhook) {
-	webhooks[models.HookType(name)] = webhook
+	webhooks[webhook_model.HookType(name)] = webhook
 }
 
 // IsValidHookTaskType returns true if a webhook registered
 func IsValidHookTaskType(name string) bool {
-	if name == models.GITEA || name == models.GOGS {
+	if name == webhook_model.GITEA || name == webhook_model.GOGS {
 		return true
 	}
-	_, ok := webhooks[models.HookType(name)]
+	_, ok := webhooks[webhook_model.HookType(name)]
 	return ok
 }
 
@@ -98,7 +99,7 @@ func getPayloadBranch(p api.Payloader) string {
 }
 
 // PrepareWebhook adds special webhook to task queue for given payload.
-func PrepareWebhook(w *models.Webhook, repo *models.Repository, event models.HookEventType, p api.Payloader) error {
+func PrepareWebhook(w *webhook_model.Webhook, repo *models.Repository, event webhook_model.HookEventType, p api.Payloader) error {
 	if err := prepareWebhook(w, repo, event, p); err != nil {
 		return err
 	}
@@ -107,7 +108,7 @@ func PrepareWebhook(w *models.Webhook, repo *models.Repository, event models.Hoo
 	return nil
 }
 
-func checkBranch(w *models.Webhook, branch string) bool {
+func checkBranch(w *webhook_model.Webhook, branch string) bool {
 	if w.BranchFilter == "" || w.BranchFilter == "*" {
 		return true
 	}
@@ -122,7 +123,7 @@ func checkBranch(w *models.Webhook, branch string) bool {
 	return g.Match(branch)
 }
 
-func prepareWebhook(w *models.Webhook, repo *models.Repository, event models.HookEventType, p api.Payloader) error {
+func prepareWebhook(w *webhook_model.Webhook, repo *models.Repository, event webhook_model.HookEventType, p api.Payloader) error {
 	// Skip sending if webhooks are disabled.
 	if setting.DisableWebhooks {
 		return nil
@@ -141,7 +142,7 @@ func prepareWebhook(w *models.Webhook, repo *models.Repository, event models.Hoo
 	// Avoid sending "0 new commits" to non-integration relevant webhooks (e.g. slack, discord, etc.).
 	// Integration webhooks (e.g. drone) still receive the required data.
 	if pushEvent, ok := p.(*api.PushPayload); ok &&
-		w.Type != models.GITEA && w.Type != models.GOGS &&
+		w.Type != webhook_model.GITEA && w.Type != webhook_model.GOGS &&
 		len(pushEvent.Commits) == 0 {
 		return nil
 	}
@@ -167,7 +168,7 @@ func prepareWebhook(w *models.Webhook, repo *models.Repository, event models.Hoo
 		payloader = p
 	}
 
-	if err = models.CreateHookTask(&models.HookTask{
+	if err = webhook_model.CreateHookTask(&webhook_model.HookTask{
 		RepoID:    repo.ID,
 		HookID:    w.ID,
 		Payloader: payloader,
@@ -179,7 +180,7 @@ func prepareWebhook(w *models.Webhook, repo *models.Repository, event models.Hoo
 }
 
 // PrepareWebhooks adds new webhooks to task queue for given payload.
-func PrepareWebhooks(repo *models.Repository, event models.HookEventType, p api.Payloader) error {
+func PrepareWebhooks(repo *models.Repository, event webhook_model.HookEventType, p api.Payloader) error {
 	if err := prepareWebhooks(repo, event, p); err != nil {
 		return err
 	}
@@ -188,8 +189,8 @@ func PrepareWebhooks(repo *models.Repository, event models.HookEventType, p api.
 	return nil
 }
 
-func prepareWebhooks(repo *models.Repository, event models.HookEventType, p api.Payloader) error {
-	ws, err := models.ListWebhooksByOpts(&models.ListWebhookOptions{
+func prepareWebhooks(repo *models.Repository, event webhook_model.HookEventType, p api.Payloader) error {
+	ws, err := webhook_model.ListWebhooksByOpts(&webhook_model.ListWebhookOptions{
 		RepoID:   repo.ID,
 		IsActive: util.OptionalBoolTrue,
 	})
@@ -200,7 +201,7 @@ func prepareWebhooks(repo *models.Repository, event models.HookEventType, p api.
 	// check if repo belongs to org and append additional webhooks
 	if repo.MustOwner().IsOrganization() {
 		// get hooks for org
-		orgHooks, err := models.ListWebhooksByOpts(&models.ListWebhookOptions{
+		orgHooks, err := webhook_model.ListWebhooksByOpts(&webhook_model.ListWebhookOptions{
 			OrgID:    repo.OwnerID,
 			IsActive: util.OptionalBoolTrue,
 		})
@@ -211,7 +212,7 @@ func prepareWebhooks(repo *models.Repository, event models.HookEventType, p api.
 	}
 
 	// Add any admin-defined system webhooks
-	systemHooks, err := models.GetSystemWebhooks()
+	systemHooks, err := webhook_model.GetSystemWebhooks()
 	if err != nil {
 		return fmt.Errorf("GetSystemWebhooks: %v", err)
 	}
diff --git a/services/webhook/webhook_test.go b/services/webhook/webhook_test.go
index 095b30713c..75f19e50ce 100644
--- a/services/webhook/webhook_test.go
+++ b/services/webhook/webhook_test.go
@@ -9,12 +9,13 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	api "code.gitea.io/gitea/modules/structs"
 	"github.com/stretchr/testify/assert"
 )
 
 func TestWebhook_GetSlackHook(t *testing.T) {
-	w := &models.Webhook{
+	w := &webhook_model.Webhook{
 		Meta: `{"channel": "foo", "username": "username", "color": "blue"}`,
 	}
 	slackHook := GetSlackHook(w)
@@ -29,13 +30,13 @@ func TestPrepareWebhooks(t *testing.T) {
 	assert.NoError(t, db.PrepareTestDatabase())
 
 	repo := db.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	hookTasks := []*models.HookTask{
-		{RepoID: repo.ID, HookID: 1, EventType: models.HookEventPush},
+	hookTasks := []*webhook_model.HookTask{
+		{RepoID: repo.ID, HookID: 1, EventType: webhook_model.HookEventPush},
 	}
 	for _, hookTask := range hookTasks {
 		db.AssertNotExistsBean(t, hookTask)
 	}
-	assert.NoError(t, PrepareWebhooks(repo, models.HookEventPush, &api.PushPayload{Commits: []*api.PayloadCommit{{}}}))
+	assert.NoError(t, PrepareWebhooks(repo, webhook_model.HookEventPush, &api.PushPayload{Commits: []*api.PayloadCommit{{}}}))
 	for _, hookTask := range hookTasks {
 		db.AssertExistsAndLoadBean(t, hookTask)
 	}
@@ -45,14 +46,14 @@ func TestPrepareWebhooksBranchFilterMatch(t *testing.T) {
 	assert.NoError(t, db.PrepareTestDatabase())
 
 	repo := db.AssertExistsAndLoadBean(t, &models.Repository{ID: 2}).(*models.Repository)
-	hookTasks := []*models.HookTask{
-		{RepoID: repo.ID, HookID: 4, EventType: models.HookEventPush},
+	hookTasks := []*webhook_model.HookTask{
+		{RepoID: repo.ID, HookID: 4, EventType: webhook_model.HookEventPush},
 	}
 	for _, hookTask := range hookTasks {
 		db.AssertNotExistsBean(t, hookTask)
 	}
 	// this test also ensures that * doesn't handle / in any special way (like shell would)
-	assert.NoError(t, PrepareWebhooks(repo, models.HookEventPush, &api.PushPayload{Ref: "refs/heads/feature/7791", Commits: []*api.PayloadCommit{{}}}))
+	assert.NoError(t, PrepareWebhooks(repo, webhook_model.HookEventPush, &api.PushPayload{Ref: "refs/heads/feature/7791", Commits: []*api.PayloadCommit{{}}}))
 	for _, hookTask := range hookTasks {
 		db.AssertExistsAndLoadBean(t, hookTask)
 	}
@@ -62,13 +63,13 @@ func TestPrepareWebhooksBranchFilterNoMatch(t *testing.T) {
 	assert.NoError(t, db.PrepareTestDatabase())
 
 	repo := db.AssertExistsAndLoadBean(t, &models.Repository{ID: 2}).(*models.Repository)
-	hookTasks := []*models.HookTask{
-		{RepoID: repo.ID, HookID: 4, EventType: models.HookEventPush},
+	hookTasks := []*webhook_model.HookTask{
+		{RepoID: repo.ID, HookID: 4, EventType: webhook_model.HookEventPush},
 	}
 	for _, hookTask := range hookTasks {
 		db.AssertNotExistsBean(t, hookTask)
 	}
-	assert.NoError(t, PrepareWebhooks(repo, models.HookEventPush, &api.PushPayload{Ref: "refs/heads/fix_weird_bug"}))
+	assert.NoError(t, PrepareWebhooks(repo, webhook_model.HookEventPush, &api.PushPayload{Ref: "refs/heads/fix_weird_bug"}))
 
 	for _, hookTask := range hookTasks {
 		db.AssertNotExistsBean(t, hookTask)
diff --git a/services/webhook/wechatwork.go b/services/webhook/wechatwork.go
index 08c77ff2a5..d28e70aded 100644
--- a/services/webhook/wechatwork.go
+++ b/services/webhook/wechatwork.go
@@ -8,7 +8,7 @@ import (
 	"fmt"
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	webhook_model "code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/json"
 	api "code.gitea.io/gitea/modules/structs"
@@ -144,7 +144,7 @@ func (f *WechatworkPayload) PullRequest(p *api.PullRequestPayload) (api.Payloade
 }
 
 // Review implements PayloadConvertor Review method
-func (f *WechatworkPayload) Review(p *api.PullRequestPayload, event models.HookEventType) (api.Payloader, error) {
+func (f *WechatworkPayload) Review(p *api.PullRequestPayload, event webhook_model.HookEventType) (api.Payloader, error) {
 	var text, title string
 	switch p.Action {
 	case api.HookIssueSynchronized:
@@ -183,6 +183,6 @@ func (f *WechatworkPayload) Release(p *api.ReleasePayload) (api.Payloader, error
 }
 
 // GetWechatworkPayload GetWechatworkPayload converts a ding talk webhook into a WechatworkPayload
-func GetWechatworkPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) {
+func GetWechatworkPayload(p api.Payloader, event webhook_model.HookEventType, meta string) (api.Payloader, error) {
 	return convertPayloader(new(WechatworkPayload), p, event)
 }