From 16138d423102e3eaa966ace055576e7e413cd294 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Tue, 2 May 2023 01:21:05 +0000
Subject: [PATCH 1/6] fix

---
 models/webhook/hooktask.go                   | 20 +++++++++-----------
 models/webhook/webhook_test.go               |  1 -
 modules/templates/helper.go                  | 13 +++++++------
 modules/timeutil/since.go                    |  5 +++++
 templates/repo/settings/webhook/history.tmpl |  2 +-
 5 files changed, 22 insertions(+), 19 deletions(-)

diff --git a/models/webhook/hooktask.go b/models/webhook/hooktask.go
index 8688fe5de3698..49be3c2c9aa0e 100644
--- a/models/webhook/hooktask.go
+++ b/models/webhook/hooktask.go
@@ -40,15 +40,14 @@ type HookResponse struct {
 
 // HookTask represents a hook task.
 type HookTask struct {
-	ID              int64  `xorm:"pk autoincr"`
-	HookID          int64  `xorm:"index"`
-	UUID            string `xorm:"unique"`
-	api.Payloader   `xorm:"-"`
-	PayloadContent  string `xorm:"LONGTEXT"`
-	EventType       webhook_module.HookEventType
-	IsDelivered     bool
-	Delivered       int64
-	DeliveredString string `xorm:"-"`
+	ID             int64  `xorm:"pk autoincr"`
+	HookID         int64  `xorm:"index"`
+	UUID           string `xorm:"unique"`
+	api.Payloader  `xorm:"-"`
+	PayloadContent string `xorm:"LONGTEXT"`
+	EventType      webhook_module.HookEventType
+	IsDelivered    bool
+	Delivered      int64
 
 	// History info.
 	IsSucceed       bool
@@ -75,8 +74,6 @@ func (t *HookTask) BeforeUpdate() {
 
 // 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
 	}
@@ -166,6 +163,7 @@ func ReplayHookTask(ctx context.Context, hookID int64, uuid string) (*HookTask,
 		HookID:         task.HookID,
 		PayloadContent: task.PayloadContent,
 		EventType:      task.EventType,
+		Delivered:      time.Now().UnixNano(),
 	}
 	return newTask, db.Insert(ctx, newTask)
 }
diff --git a/models/webhook/webhook_test.go b/models/webhook/webhook_test.go
index 74f7aeaa03028..dbc94cd05543c 100644
--- a/models/webhook/webhook_test.go
+++ b/models/webhook/webhook_test.go
@@ -222,7 +222,6 @@ func TestUpdateHookTask(t *testing.T) {
 
 	hook := unittest.AssertExistsAndLoadBean(t, &HookTask{ID: 1})
 	hook.PayloadContent = "new payload content"
-	hook.DeliveredString = "new delivered string"
 	hook.IsDelivered = true
 	unittest.AssertNotExistsBean(t, hook)
 	assert.NoError(t, UpdateHookTask(hook))
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 4abd94d46e59e..1c86a0bba77df 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -66,12 +66,13 @@ func NewFuncMap() template.FuncMap {
 
 		// -----------------------------------------------------------------
 		// time / number / format
-		"FileSize":      base.FileSize,
-		"CountFmt":      base.FormatNumberSI,
-		"TimeSince":     timeutil.TimeSince,
-		"TimeSinceUnix": timeutil.TimeSinceUnix,
-		"DateTime":      timeutil.DateTime,
-		"Sec2Time":      util.SecToTime,
+		"FileSize":       base.FileSize,
+		"CountFmt":       base.FormatNumberSI,
+		"Int64TimeSince": timeutil.Int64TimeSince,
+		"TimeSince":      timeutil.TimeSince,
+		"TimeSinceUnix":  timeutil.TimeSinceUnix,
+		"DateTime":       timeutil.DateTime,
+		"Sec2Time":       util.SecToTime,
 		"LoadTimes": func(startTime time.Time) string {
 			return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
 		},
diff --git a/modules/timeutil/since.go b/modules/timeutil/since.go
index 04fcff54a3384..e0f6af1581ac7 100644
--- a/modules/timeutil/since.go
+++ b/modules/timeutil/since.go
@@ -130,6 +130,11 @@ func timeSinceUnix(then, now time.Time, lang translation.Locale) template.HTML {
 	return template.HTML(htm)
 }
 
+// TimeSince renders relative time HTML given a time.Time
+func Int64TimeSince(then int64, lang translation.Locale) template.HTML {
+	return timeSinceUnix(time.Unix(0, then), time.Now(), lang)
+}
+
 // TimeSince renders relative time HTML given a time.Time
 func TimeSince(then time.Time, lang translation.Locale) template.HTML {
 	return timeSinceUnix(then, time.Now(), lang)
diff --git a/templates/repo/settings/webhook/history.tmpl b/templates/repo/settings/webhook/history.tmpl
index e573d221d14be..28c1beddd200a 100644
--- a/templates/repo/settings/webhook/history.tmpl
+++ b/templates/repo/settings/webhook/history.tmpl
@@ -21,7 +21,7 @@
 						<a class="ui primary sha label toggle button show-panel" data-panel="#info-{{.ID}}">{{.UUID}}</a>
 						<div class="ui right">
 							<span class="text grey time">
-								{{.DeliveredString}}
+								{{Int64TimeSince .Delivered $.lcoale}}
 							</span>
 						</div>
 					</div>

From 8d446412a3988c8194b846a8803a96816a34ac25 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Tue, 2 May 2023 01:40:24 +0000
Subject: [PATCH 2/6] improve logic

---
 models/webhook/hooktask.go | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/models/webhook/hooktask.go b/models/webhook/hooktask.go
index 49be3c2c9aa0e..c98d30e33ae47 100644
--- a/models/webhook/hooktask.go
+++ b/models/webhook/hooktask.go
@@ -112,12 +112,15 @@ func HookTasks(hookID int64, page int) ([]*HookTask, error) {
 // CreateHookTask creates a new hook task,
 // it handles conversion from Payload to PayloadContent.
 func CreateHookTask(ctx context.Context, t *HookTask) (*HookTask, error) {
-	data, err := t.Payloader.JSONPayload()
-	if err != nil {
-		return nil, err
-	}
 	t.UUID = gouuid.New().String()
-	t.PayloadContent = string(data)
+	if t.Payloader != nil {
+		data, err := t.Payloader.JSONPayload()
+		if err != nil {
+			return nil, err
+		}
+		t.PayloadContent = string(data)
+	}
+	t.Delivered = time.Now().UnixNano()
 	return t, db.Insert(ctx, t)
 }
 
@@ -158,14 +161,11 @@ func ReplayHookTask(ctx context.Context, hookID int64, uuid string) (*HookTask,
 		}
 	}
 
-	newTask := &HookTask{
-		UUID:           gouuid.New().String(),
+	return CreateHookTask(ctx, &HookTask{
 		HookID:         task.HookID,
 		PayloadContent: task.PayloadContent,
 		EventType:      task.EventType,
-		Delivered:      time.Now().UnixNano(),
-	}
-	return newTask, db.Insert(ctx, newTask)
+	})
 }
 
 // FindUndeliveredHookTaskIDs will find the next 100 undelivered hook tasks with ID greater than the provided lowerID

From 71fb115a00de042d91cc0e9e409573271cacb026 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Tue, 2 May 2023 02:33:29 +0000
Subject: [PATCH 3/6] add timeutil.timestampnano

---
 models/webhook/hooktask.go                   |  5 +-
 models/webhook/webhook_test.go               |  9 +-
 modules/templates/helper.go                  | 13 ++-
 modules/timeutil/since.go                    |  5 -
 modules/timeutil/timestampnano.go            | 97 ++++++++++++++++++++
 services/webhook/deliver.go                  |  3 +-
 templates/repo/settings/webhook/history.tmpl |  2 +-
 7 files changed, 114 insertions(+), 20 deletions(-)
 create mode 100644 modules/timeutil/timestampnano.go

diff --git a/models/webhook/hooktask.go b/models/webhook/hooktask.go
index c98d30e33ae47..9a12e69c3c9e8 100644
--- a/models/webhook/hooktask.go
+++ b/models/webhook/hooktask.go
@@ -12,6 +12,7 @@ import (
 	"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"
 	webhook_module "code.gitea.io/gitea/modules/webhook"
 
 	gouuid "github.com/google/uuid"
@@ -47,7 +48,7 @@ type HookTask struct {
 	PayloadContent string `xorm:"LONGTEXT"`
 	EventType      webhook_module.HookEventType
 	IsDelivered    bool
-	Delivered      int64
+	Delivered      timeutil.TimeStampNano
 
 	// History info.
 	IsSucceed       bool
@@ -120,7 +121,7 @@ func CreateHookTask(ctx context.Context, t *HookTask) (*HookTask, error) {
 		}
 		t.PayloadContent = string(data)
 	}
-	t.Delivered = time.Now().UnixNano()
+	t.Delivered = timeutil.TimeStampNanoNow()
 	return t, db.Insert(ctx, t)
 }
 
diff --git a/models/webhook/webhook_test.go b/models/webhook/webhook_test.go
index dbc94cd05543c..e05dcaba01a48 100644
--- a/models/webhook/webhook_test.go
+++ b/models/webhook/webhook_test.go
@@ -12,6 +12,7 @@ import (
 	"code.gitea.io/gitea/models/unittest"
 	"code.gitea.io/gitea/modules/json"
 	api "code.gitea.io/gitea/modules/structs"
+	"code.gitea.io/gitea/modules/timeutil"
 	"code.gitea.io/gitea/modules/util"
 	webhook_module "code.gitea.io/gitea/modules/webhook"
 
@@ -234,7 +235,7 @@ func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) {
 		HookID:      3,
 		Payloader:   &api.PushPayload{},
 		IsDelivered: true,
-		Delivered:   time.Now().UnixNano(),
+		Delivered:   timeutil.TimeStampNanoNow(),
 	}
 	unittest.AssertNotExistsBean(t, hookTask)
 	_, err := CreateHookTask(db.DefaultContext, hookTask)
@@ -267,7 +268,7 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) {
 		HookID:      4,
 		Payloader:   &api.PushPayload{},
 		IsDelivered: true,
-		Delivered:   time.Now().UnixNano(),
+		Delivered:   timeutil.TimeStampNanoNow(),
 	}
 	unittest.AssertNotExistsBean(t, hookTask)
 	_, err := CreateHookTask(db.DefaultContext, hookTask)
@@ -284,7 +285,7 @@ func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) {
 		HookID:      3,
 		Payloader:   &api.PushPayload{},
 		IsDelivered: true,
-		Delivered:   time.Now().AddDate(0, 0, -8).UnixNano(),
+		Delivered:   timeutil.TimeStampNano(time.Now().AddDate(0, 0, -8).UnixNano()),
 	}
 	unittest.AssertNotExistsBean(t, hookTask)
 	_, err := CreateHookTask(db.DefaultContext, hookTask)
@@ -317,7 +318,7 @@ func TestCleanupHookTaskTable_OlderThan_LeavesTaskEarlierThanAgeToDelete(t *test
 		HookID:      4,
 		Payloader:   &api.PushPayload{},
 		IsDelivered: true,
-		Delivered:   time.Now().AddDate(0, 0, -6).UnixNano(),
+		Delivered:   timeutil.TimeStampNano(time.Now().AddDate(0, 0, -6).UnixNano()),
 	}
 	unittest.AssertNotExistsBean(t, hookTask)
 	_, err := CreateHookTask(db.DefaultContext, hookTask)
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 1c86a0bba77df..4abd94d46e59e 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -66,13 +66,12 @@ func NewFuncMap() template.FuncMap {
 
 		// -----------------------------------------------------------------
 		// time / number / format
-		"FileSize":       base.FileSize,
-		"CountFmt":       base.FormatNumberSI,
-		"Int64TimeSince": timeutil.Int64TimeSince,
-		"TimeSince":      timeutil.TimeSince,
-		"TimeSinceUnix":  timeutil.TimeSinceUnix,
-		"DateTime":       timeutil.DateTime,
-		"Sec2Time":       util.SecToTime,
+		"FileSize":      base.FileSize,
+		"CountFmt":      base.FormatNumberSI,
+		"TimeSince":     timeutil.TimeSince,
+		"TimeSinceUnix": timeutil.TimeSinceUnix,
+		"DateTime":      timeutil.DateTime,
+		"Sec2Time":      util.SecToTime,
 		"LoadTimes": func(startTime time.Time) string {
 			return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
 		},
diff --git a/modules/timeutil/since.go b/modules/timeutil/since.go
index e0f6af1581ac7..04fcff54a3384 100644
--- a/modules/timeutil/since.go
+++ b/modules/timeutil/since.go
@@ -130,11 +130,6 @@ func timeSinceUnix(then, now time.Time, lang translation.Locale) template.HTML {
 	return template.HTML(htm)
 }
 
-// TimeSince renders relative time HTML given a time.Time
-func Int64TimeSince(then int64, lang translation.Locale) template.HTML {
-	return timeSinceUnix(time.Unix(0, then), time.Now(), lang)
-}
-
 // TimeSince renders relative time HTML given a time.Time
 func TimeSince(then time.Time, lang translation.Locale) template.HTML {
 	return timeSinceUnix(then, time.Now(), lang)
diff --git a/modules/timeutil/timestampnano.go b/modules/timeutil/timestampnano.go
new file mode 100644
index 0000000000000..62034e8e85a78
--- /dev/null
+++ b/modules/timeutil/timestampnano.go
@@ -0,0 +1,97 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package timeutil
+
+import (
+	"time"
+
+	"code.gitea.io/gitea/modules/setting"
+)
+
+// TimeStampNano defines a nano timestamp
+type TimeStampNano int64
+
+var (
+	// Used for IsZero, to check if timestamp is the zero time instant.
+	timeZeroUnixNano = time.Time{}.UnixNano()
+)
+
+// TimeStampNanoNow returns now nano int64
+func TimeStampNanoNow() TimeStampNano {
+	if !mock.IsZero() {
+		return TimeStampNano(mock.UnixNano())
+	}
+	return TimeStampNano(time.Now().UnixNano())
+}
+
+// Add adds nanos and return sum
+func (tsn TimeStampNano) Add(nanos int64) TimeStampNano {
+	return tsn + TimeStampNano(nanos)
+}
+
+// AddDuration adds time.Duration and return sum
+func (tsn TimeStampNano) AddDuration(interval time.Duration) TimeStampNano {
+	return tsn + TimeStampNano(interval/time.Nanosecond)
+}
+
+// Year returns the time's year
+func (tsn TimeStampNano) Year() int {
+	return tsn.AsTime().Year()
+}
+
+// AsTime convert timestamp as time.Time in Local locale
+func (tsn TimeStampNano) AsTime() (tm time.Time) {
+	return tsn.AsTimeInLocation(setting.DefaultUILocation)
+}
+
+// AsLocalTime convert timestamp as time.Time in local location
+func (tsn TimeStampNano) AsLocalTime() time.Time {
+	return time.Unix(0, int64(tsn))
+}
+
+// AsTimeInLocation convert timestamp as time.Time in Local locale
+func (tsn TimeStampNano) AsTimeInLocation(loc *time.Location) time.Time {
+	return time.Unix(0, int64(tsn)).In(loc)
+}
+
+// AsTimePtr convert timestamp as *time.Time in Local locale
+func (tsn TimeStampNano) AsTimePtr() *time.Time {
+	return tsn.AsTimePtrInLocation(setting.DefaultUILocation)
+}
+
+// AsTimePtrInLocation convert timestamp as *time.Time in customize location
+func (tsn TimeStampNano) AsTimePtrInLocation(loc *time.Location) *time.Time {
+	tm := time.Unix(0, int64(tsn)).In(loc)
+	return &tm
+}
+
+// Format formats timestamp as given format
+func (tsn TimeStampNano) Format(f string) string {
+	return tsn.FormatInLocation(f, setting.DefaultUILocation)
+}
+
+// FormatInLocation formats timestamp as given format with spiecific location
+func (tsn TimeStampNano) FormatInLocation(f string, loc *time.Location) string {
+	return tsn.AsTimeInLocation(loc).Format(f)
+}
+
+// FormatLong formats as RFC1123Z
+func (tsn TimeStampNano) FormatLong() string {
+	return tsn.Format(time.RFC1123Z)
+}
+
+// FormatShort formats as short
+func (tsn TimeStampNano) FormatShort() string {
+	return tsn.Format("Jan 02, 2006")
+}
+
+// FormatDate formats a date in YYYY-MM-DD server time zone
+func (tsn TimeStampNano) FormatDate() string {
+	return time.Unix(0, int64(tsn)).String()[:10]
+}
+
+// IsZero is zero time
+func (tsn TimeStampNano) IsZero() bool {
+	return int64(tsn) == 0 || int64(tsn) == timeZeroUnixNano
+}
diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go
index e389b1f9fe670..31246c155553f 100644
--- a/services/webhook/deliver.go
+++ b/services/webhook/deliver.go
@@ -25,6 +25,7 @@ import (
 	"code.gitea.io/gitea/modules/proxy"
 	"code.gitea.io/gitea/modules/queue"
 	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/timeutil"
 	webhook_module "code.gitea.io/gitea/modules/webhook"
 
 	"github.com/gobwas/glob"
@@ -175,7 +176,7 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error {
 
 	// All code from this point will update the hook task
 	defer func() {
-		t.Delivered = time.Now().UnixNano()
+		t.Delivered = timeutil.TimeStampNanoNow()
 		if t.IsSucceed {
 			log.Trace("Hook delivered: %s", t.UUID)
 		} else if !w.IsActive {
diff --git a/templates/repo/settings/webhook/history.tmpl b/templates/repo/settings/webhook/history.tmpl
index 28c1beddd200a..fb328c76e8a57 100644
--- a/templates/repo/settings/webhook/history.tmpl
+++ b/templates/repo/settings/webhook/history.tmpl
@@ -21,7 +21,7 @@
 						<a class="ui primary sha label toggle button show-panel" data-panel="#info-{{.ID}}">{{.UUID}}</a>
 						<div class="ui right">
 							<span class="text grey time">
-								{{Int64TimeSince .Delivered $.lcoale}}
+								{{TimeSince .Delivered.AsTime $.lcoale}}
 							</span>
 						</div>
 					</div>

From d7e04ad1e393f5340c3e735aadd818c7a8aa9916 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Tue, 2 May 2023 05:01:43 +0000
Subject: [PATCH 4/6] simplify timestampnano

---
 modules/timeutil/timestampnano.go | 71 +------------------------------
 1 file changed, 1 insertion(+), 70 deletions(-)

diff --git a/modules/timeutil/timestampnano.go b/modules/timeutil/timestampnano.go
index 62034e8e85a78..4a9f7955b9812 100644
--- a/modules/timeutil/timestampnano.go
+++ b/modules/timeutil/timestampnano.go
@@ -9,89 +9,20 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 )
 
-// TimeStampNano defines a nano timestamp
+// TimeStampNano is for nano time in database, do not use it unless there is a real requirement.
 type TimeStampNano int64
 
-var (
-	// Used for IsZero, to check if timestamp is the zero time instant.
-	timeZeroUnixNano = time.Time{}.UnixNano()
-)
-
 // TimeStampNanoNow returns now nano int64
 func TimeStampNanoNow() TimeStampNano {
-	if !mock.IsZero() {
-		return TimeStampNano(mock.UnixNano())
-	}
 	return TimeStampNano(time.Now().UnixNano())
 }
 
-// Add adds nanos and return sum
-func (tsn TimeStampNano) Add(nanos int64) TimeStampNano {
-	return tsn + TimeStampNano(nanos)
-}
-
-// AddDuration adds time.Duration and return sum
-func (tsn TimeStampNano) AddDuration(interval time.Duration) TimeStampNano {
-	return tsn + TimeStampNano(interval/time.Nanosecond)
-}
-
-// Year returns the time's year
-func (tsn TimeStampNano) Year() int {
-	return tsn.AsTime().Year()
-}
-
 // AsTime convert timestamp as time.Time in Local locale
 func (tsn TimeStampNano) AsTime() (tm time.Time) {
 	return tsn.AsTimeInLocation(setting.DefaultUILocation)
 }
 
-// AsLocalTime convert timestamp as time.Time in local location
-func (tsn TimeStampNano) AsLocalTime() time.Time {
-	return time.Unix(0, int64(tsn))
-}
-
 // AsTimeInLocation convert timestamp as time.Time in Local locale
 func (tsn TimeStampNano) AsTimeInLocation(loc *time.Location) time.Time {
 	return time.Unix(0, int64(tsn)).In(loc)
 }
-
-// AsTimePtr convert timestamp as *time.Time in Local locale
-func (tsn TimeStampNano) AsTimePtr() *time.Time {
-	return tsn.AsTimePtrInLocation(setting.DefaultUILocation)
-}
-
-// AsTimePtrInLocation convert timestamp as *time.Time in customize location
-func (tsn TimeStampNano) AsTimePtrInLocation(loc *time.Location) *time.Time {
-	tm := time.Unix(0, int64(tsn)).In(loc)
-	return &tm
-}
-
-// Format formats timestamp as given format
-func (tsn TimeStampNano) Format(f string) string {
-	return tsn.FormatInLocation(f, setting.DefaultUILocation)
-}
-
-// FormatInLocation formats timestamp as given format with spiecific location
-func (tsn TimeStampNano) FormatInLocation(f string, loc *time.Location) string {
-	return tsn.AsTimeInLocation(loc).Format(f)
-}
-
-// FormatLong formats as RFC1123Z
-func (tsn TimeStampNano) FormatLong() string {
-	return tsn.Format(time.RFC1123Z)
-}
-
-// FormatShort formats as short
-func (tsn TimeStampNano) FormatShort() string {
-	return tsn.Format("Jan 02, 2006")
-}
-
-// FormatDate formats a date in YYYY-MM-DD server time zone
-func (tsn TimeStampNano) FormatDate() string {
-	return time.Unix(0, int64(tsn)).String()[:10]
-}
-
-// IsZero is zero time
-func (tsn TimeStampNano) IsZero() bool {
-	return int64(tsn) == 0 || int64(tsn) == timeZeroUnixNano
-}

From 3358035a40b1de8f071fd2e7d8c78ad0903b51ae Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Tue, 2 May 2023 06:59:54 +0000
Subject: [PATCH 5/6] fix test

---
 models/webhook/hooktask.go | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/models/webhook/hooktask.go b/models/webhook/hooktask.go
index 9a12e69c3c9e8..f9fc886826394 100644
--- a/models/webhook/hooktask.go
+++ b/models/webhook/hooktask.go
@@ -121,7 +121,9 @@ func CreateHookTask(ctx context.Context, t *HookTask) (*HookTask, error) {
 		}
 		t.PayloadContent = string(data)
 	}
-	t.Delivered = timeutil.TimeStampNanoNow()
+	if t.Delivered == 0 {
+		t.Delivered = timeutil.TimeStampNanoNow()
+	}
 	return t, db.Insert(ctx, t)
 }
 

From 7d76ac387645c168f9960628c89cb20b65ec52cc Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Wed, 3 May 2023 09:39:11 +0800
Subject: [PATCH 6/6] Update templates/repo/settings/webhook/history.tmpl

---
 templates/repo/settings/webhook/history.tmpl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/templates/repo/settings/webhook/history.tmpl b/templates/repo/settings/webhook/history.tmpl
index fb328c76e8a57..3aec5469924ad 100644
--- a/templates/repo/settings/webhook/history.tmpl
+++ b/templates/repo/settings/webhook/history.tmpl
@@ -21,7 +21,7 @@
 						<a class="ui primary sha label toggle button show-panel" data-panel="#info-{{.ID}}">{{.UUID}}</a>
 						<div class="ui right">
 							<span class="text grey time">
-								{{TimeSince .Delivered.AsTime $.lcoale}}
+								{{TimeSince .Delivered.AsTime $.locale}}
 							</span>
 						</div>
 					</div>