mirror of
https://github.com/go-gitea/gitea.git
synced 2025-10-03 00:02:14 -04:00
Compare commits
25 Commits
f0e8c4fe85
...
d7dcd0c6ce
Author | SHA1 | Date | |
---|---|---|---|
|
d7dcd0c6ce | ||
|
b907b9fb1a | ||
|
ef6aedad3b | ||
|
7f9debb359 | ||
|
46c08243db | ||
|
94225ed539 | ||
|
98d4da1bea | ||
|
f4ea1a1293 | ||
|
10d0450a1d | ||
|
a3581be4f8 | ||
|
80f8d2ce8a | ||
|
eec07ee0c5 | ||
|
6d932c8a67 | ||
|
6b635dc2ec | ||
|
655bfcd505 | ||
|
9767573a4f | ||
|
b718c54e7c | ||
|
11cdba1b13 | ||
|
6e07c6f338 | ||
|
42a880fa89 | ||
|
544e445024 | ||
|
9682610d9a | ||
|
39718b2ee3 | ||
|
5472d66d16 | ||
|
1915361b42 |
@ -394,6 +394,7 @@ func prepareMigrationTasks() []*migration {
|
|||||||
// Gitea 1.24.0 ends at database version 321
|
// Gitea 1.24.0 ends at database version 321
|
||||||
newMigration(321, "Use LONGTEXT for some columns and fix review_state.updated_files column", v1_25.UseLongTextInSomeColumnsAndFixBugs),
|
newMigration(321, "Use LONGTEXT for some columns and fix review_state.updated_files column", v1_25.UseLongTextInSomeColumnsAndFixBugs),
|
||||||
newMigration(322, "Extend comment tree_path length limit", v1_25.ExtendCommentTreePathLength),
|
newMigration(322, "Extend comment tree_path length limit", v1_25.ExtendCommentTreePathLength),
|
||||||
|
newMigration(323, "Add webhook payload optimization JSON field", v1_25.AddWebhookPayloadOptimizationColumns),
|
||||||
}
|
}
|
||||||
return preparedMigrations
|
return preparedMigrations
|
||||||
}
|
}
|
||||||
|
22
models/migrations/v1_25/v323.go
Normal file
22
models/migrations/v1_25/v323.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_25
|
||||||
|
|
||||||
|
import (
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AddWebhookPayloadOptimizationColumns(x *xorm.Engine) error {
|
||||||
|
type Webhook struct {
|
||||||
|
MetaSettings string `xorm:"meta_settings TEXT"`
|
||||||
|
}
|
||||||
|
_, err := x.SyncWithOptions(
|
||||||
|
xorm.SyncOptions{
|
||||||
|
IgnoreConstrains: true,
|
||||||
|
IgnoreIndices: true,
|
||||||
|
},
|
||||||
|
new(Webhook),
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
@ -22,6 +22,35 @@ import (
|
|||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MetaSettings represents the metadata settings for webhook
|
||||||
|
type MetaSettings struct {
|
||||||
|
PayloadConfig PayloadConfig `json:"payload_config"` // Payload configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
// PayloadConfig represents the configuration for webhook payload
|
||||||
|
type PayloadConfig struct {
|
||||||
|
Files PayloadConfigItem `json:"files"` // Files configuration
|
||||||
|
Commits PayloadConfigItem `json:"commits"` // Commits configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
// PayloadConfigItem represents a single payload configuration item
|
||||||
|
type PayloadConfigItem struct {
|
||||||
|
Enable bool `json:"enable"` // Whether to enable this configuration
|
||||||
|
Limit int `json:"limit"` // 0: trim all (none kept), >0: keep N items (forward order), <0: keep N items (reverse order)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultMetaSettings returns the default webhook meta settings
|
||||||
|
func DefaultMetaSettings() MetaSettings {
|
||||||
|
return MetaSettings{
|
||||||
|
PayloadConfig: DefaultPayloadConfig(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultPayloadConfig returns the default payload configuration
|
||||||
|
func DefaultPayloadConfig() PayloadConfig {
|
||||||
|
return PayloadConfig{}
|
||||||
|
}
|
||||||
|
|
||||||
// ErrWebhookNotExist represents a "WebhookNotExist" kind of error.
|
// ErrWebhookNotExist represents a "WebhookNotExist" kind of error.
|
||||||
type ErrWebhookNotExist struct {
|
type ErrWebhookNotExist struct {
|
||||||
ID int64
|
ID int64
|
||||||
@ -139,6 +168,9 @@ type Webhook struct {
|
|||||||
// HeaderAuthorizationEncrypted should be accessed using HeaderAuthorization() and SetHeaderAuthorization()
|
// HeaderAuthorizationEncrypted should be accessed using HeaderAuthorization() and SetHeaderAuthorization()
|
||||||
HeaderAuthorizationEncrypted string `xorm:"TEXT"`
|
HeaderAuthorizationEncrypted string `xorm:"TEXT"`
|
||||||
|
|
||||||
|
// Webhook metadata settings (JSON format)
|
||||||
|
MetaSettings string `xorm:"meta_settings TEXT"` // JSON: webhook metadata configuration
|
||||||
|
|
||||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
||||||
}
|
}
|
||||||
@ -346,3 +378,83 @@ func DeleteWebhookByOwnerID(ctx context.Context, ownerID, id int64) error {
|
|||||||
}
|
}
|
||||||
return DeleteWebhookByID(ctx, id)
|
return DeleteWebhookByID(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMetaSettings returns the webhook meta settings
|
||||||
|
func (w *Webhook) GetMetaSettings() MetaSettings {
|
||||||
|
if w.MetaSettings == "" {
|
||||||
|
return DefaultMetaSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
var settings MetaSettings
|
||||||
|
if err := json.Unmarshal([]byte(w.MetaSettings), &settings); err != nil {
|
||||||
|
log.Error("Failed to unmarshal webhook meta settings: %v", err)
|
||||||
|
return DefaultMetaSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPayloadConfig returns the payload configuration
|
||||||
|
func (w *Webhook) GetPayloadConfig() PayloadConfig {
|
||||||
|
return w.GetMetaSettings().PayloadConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMetaSettings sets the webhook meta settings
|
||||||
|
func (w *Webhook) SetMetaSettings(settings MetaSettings) error {
|
||||||
|
data, err := json.Marshal(settings)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal webhook meta settings: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.MetaSettings = string(data)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPayloadConfig sets the payload configuration
|
||||||
|
func (w *Webhook) SetPayloadConfig(config PayloadConfig) error {
|
||||||
|
settings := w.GetMetaSettings()
|
||||||
|
settings.PayloadConfig = config
|
||||||
|
return w.SetMetaSettings(settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPayloadConfigEnabled returns whether payload configuration is enabled
|
||||||
|
func (w *Webhook) IsPayloadConfigEnabled() bool {
|
||||||
|
config := w.GetPayloadConfig()
|
||||||
|
return config.Files.Enable || config.Commits.Enable
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPayloadConfigLimit returns the payload configuration limit
|
||||||
|
func (w *Webhook) GetPayloadConfigLimit() int {
|
||||||
|
config := w.GetPayloadConfig()
|
||||||
|
if config.Files.Enable {
|
||||||
|
return config.Files.Limit
|
||||||
|
}
|
||||||
|
if config.Commits.Enable {
|
||||||
|
return config.Commits.Limit
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFilesConfigEnabled returns whether files configuration is enabled
|
||||||
|
func (w *Webhook) IsFilesConfigEnabled() bool {
|
||||||
|
config := w.GetPayloadConfig()
|
||||||
|
return config.Files.Enable
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFilesConfigLimit returns the files configuration limit
|
||||||
|
func (w *Webhook) GetFilesConfigLimit() int {
|
||||||
|
config := w.GetPayloadConfig()
|
||||||
|
return config.Files.Limit
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCommitsConfigEnabled returns whether commits configuration is enabled
|
||||||
|
func (w *Webhook) IsCommitsConfigEnabled() bool {
|
||||||
|
config := w.GetPayloadConfig()
|
||||||
|
return config.Commits.Enable
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCommitsConfigLimit returns the commits configuration limit
|
||||||
|
func (w *Webhook) GetCommitsConfigLimit() int {
|
||||||
|
config := w.GetPayloadConfig()
|
||||||
|
return config.Commits.Limit
|
||||||
|
}
|
||||||
|
@ -330,3 +330,63 @@ func TestCleanupHookTaskTable_OlderThan_LeavesTaskEarlierThanAgeToDelete(t *test
|
|||||||
assert.NoError(t, CleanupHookTaskTable(t.Context(), OlderThan, 168*time.Hour, 0))
|
assert.NoError(t, CleanupHookTaskTable(t.Context(), OlderThan, 168*time.Hour, 0))
|
||||||
unittest.AssertExistsAndLoadBean(t, hookTask)
|
unittest.AssertExistsAndLoadBean(t, hookTask)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWebhookPayloadOptimization(t *testing.T) {
|
||||||
|
webhook := &Webhook{}
|
||||||
|
|
||||||
|
// Test default configuration
|
||||||
|
config := webhook.GetPayloadConfig()
|
||||||
|
assert.False(t, config.Files.Enable)
|
||||||
|
assert.Equal(t, 0, config.Files.Limit)
|
||||||
|
assert.False(t, config.Commits.Enable)
|
||||||
|
assert.Equal(t, 0, config.Commits.Limit)
|
||||||
|
|
||||||
|
// Test setting configuration via meta settings
|
||||||
|
metaSettings := MetaSettings{
|
||||||
|
PayloadConfig: PayloadConfig{
|
||||||
|
Files: PayloadConfigItem{
|
||||||
|
Enable: true,
|
||||||
|
Limit: 5,
|
||||||
|
},
|
||||||
|
Commits: PayloadConfigItem{
|
||||||
|
Enable: true,
|
||||||
|
Limit: -3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
webhook.SetMetaSettings(metaSettings)
|
||||||
|
|
||||||
|
// Test getting configuration
|
||||||
|
config = webhook.GetPayloadConfig()
|
||||||
|
assert.True(t, config.Files.Enable)
|
||||||
|
assert.Equal(t, 5, config.Files.Limit)
|
||||||
|
assert.True(t, config.Commits.Enable)
|
||||||
|
assert.Equal(t, -3, config.Commits.Limit)
|
||||||
|
|
||||||
|
// Test individual methods
|
||||||
|
assert.True(t, webhook.IsFilesConfigEnabled())
|
||||||
|
assert.Equal(t, 5, webhook.GetFilesConfigLimit())
|
||||||
|
assert.True(t, webhook.IsCommitsConfigEnabled())
|
||||||
|
assert.Equal(t, -3, webhook.GetCommitsConfigLimit())
|
||||||
|
assert.True(t, webhook.IsPayloadConfigEnabled())
|
||||||
|
|
||||||
|
// Test backward compatibility with direct payload config setting
|
||||||
|
newConfig := PayloadConfig{
|
||||||
|
Files: PayloadConfigItem{
|
||||||
|
Enable: false,
|
||||||
|
Limit: 10,
|
||||||
|
},
|
||||||
|
Commits: PayloadConfigItem{
|
||||||
|
Enable: false,
|
||||||
|
Limit: 20,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
webhook.SetPayloadConfig(newConfig)
|
||||||
|
|
||||||
|
// Verify the config is properly set through meta settings
|
||||||
|
config = webhook.GetPayloadConfig()
|
||||||
|
assert.False(t, config.Files.Enable)
|
||||||
|
assert.Equal(t, 10, config.Files.Limit)
|
||||||
|
assert.False(t, config.Commits.Enable)
|
||||||
|
assert.Equal(t, 20, config.Commits.Limit)
|
||||||
|
}
|
||||||
|
@ -33,6 +33,8 @@ type Hook struct {
|
|||||||
AuthorizationHeader string `json:"authorization_header"`
|
AuthorizationHeader string `json:"authorization_header"`
|
||||||
// Whether the webhook is active and will be triggered
|
// Whether the webhook is active and will be triggered
|
||||||
Active bool `json:"active"`
|
Active bool `json:"active"`
|
||||||
|
// MetaSettings webhook metadata settings including payload optimization
|
||||||
|
MetaSettings map[string]any `json:"meta_settings"`
|
||||||
// swagger:strfmt date-time
|
// swagger:strfmt date-time
|
||||||
// The date and time when the webhook was last updated
|
// The date and time when the webhook was last updated
|
||||||
Updated time.Time `json:"updated_at"`
|
Updated time.Time `json:"updated_at"`
|
||||||
@ -63,6 +65,8 @@ type CreateHookOption struct {
|
|||||||
BranchFilter string `json:"branch_filter" binding:"GlobPattern"`
|
BranchFilter string `json:"branch_filter" binding:"GlobPattern"`
|
||||||
// Authorization header to include in webhook requests
|
// Authorization header to include in webhook requests
|
||||||
AuthorizationHeader string `json:"authorization_header"`
|
AuthorizationHeader string `json:"authorization_header"`
|
||||||
|
// Webhook metadata settings including payload optimization
|
||||||
|
MetaSettings map[string]any `json:"meta_settings"` // {"payload_config": {"files": {"enable": bool, "limit": int}, "commits": {"enable": bool, "limit": int}}}
|
||||||
// default: false
|
// default: false
|
||||||
// Whether the webhook should be active upon creation
|
// Whether the webhook should be active upon creation
|
||||||
Active bool `json:"active"`
|
Active bool `json:"active"`
|
||||||
@ -78,6 +82,8 @@ type EditHookOption struct {
|
|||||||
BranchFilter string `json:"branch_filter" binding:"GlobPattern"`
|
BranchFilter string `json:"branch_filter" binding:"GlobPattern"`
|
||||||
// Authorization header to include in webhook requests
|
// Authorization header to include in webhook requests
|
||||||
AuthorizationHeader string `json:"authorization_header"`
|
AuthorizationHeader string `json:"authorization_header"`
|
||||||
|
// Webhook metadata settings including payload optimization
|
||||||
|
MetaSettings *map[string]any `json:"meta_settings"` // {"payload_config": {"files": {"enable": bool, "limit": int}, "commits": {"enable": bool, "limit": int}}}
|
||||||
// Whether the webhook is active and will be triggered
|
// Whether the webhook is active and will be triggered
|
||||||
Active *bool `json:"active"`
|
Active *bool `json:"active"`
|
||||||
}
|
}
|
||||||
|
@ -2434,6 +2434,13 @@ settings.event_package = Package
|
|||||||
settings.event_package_desc = Package created or deleted in a repository.
|
settings.event_package_desc = Package created or deleted in a repository.
|
||||||
settings.branch_filter = Branch filter
|
settings.branch_filter = Branch filter
|
||||||
settings.branch_filter_desc = Branch whitelist for push, branch creation and branch deletion events, specified as glob pattern. If empty or <code>*</code>, events for all branches are reported. See <a href="%[1]s">%[2]s</a> documentation for syntax. Examples: <code>master</code>, <code>{master,release*}</code>.
|
settings.branch_filter_desc = Branch whitelist for push, branch creation and branch deletion events, specified as glob pattern. If empty or <code>*</code>, events for all branches are reported. See <a href="%[1]s">%[2]s</a> documentation for syntax. Examples: <code>master</code>, <code>{master,release*}</code>.
|
||||||
|
settings.payload_optimization = Payload Size Optimization
|
||||||
|
settings.payload_optimization_files = Files
|
||||||
|
settings.payload_optimization_commits = Commits
|
||||||
|
settings.payload_optimization_enable = Enable optimization
|
||||||
|
settings.payload_optimization_enable_desc = Enable payload size optimization for this item
|
||||||
|
settings.payload_optimization_limit = Limit
|
||||||
|
settings.payload_optimization_limit_desc = 0: trim all (none kept), >0: keep N items (forward order), <0: keep N items (reverse order)
|
||||||
settings.authorization_header = Authorization Header
|
settings.authorization_header = Authorization Header
|
||||||
settings.authorization_header_desc = Will be included as authorization header for requests when present. Examples: %s.
|
settings.authorization_header_desc = Will be included as authorization header for requests when present. Examples: %s.
|
||||||
settings.active = Active
|
settings.active = Active
|
||||||
@ -3297,7 +3304,7 @@ auths.tip.github = Register a new OAuth application on %s
|
|||||||
auths.tip.gitlab_new = Register a new application on %s
|
auths.tip.gitlab_new = Register a new application on %s
|
||||||
auths.tip.google_plus = Obtain OAuth2 client credentials from the Google API console at %s
|
auths.tip.google_plus = Obtain OAuth2 client credentials from the Google API console at %s
|
||||||
auths.tip.openid_connect = Use the OpenID Connect Discovery URL "https://{server}/.well-known/openid-configuration" to specify the endpoints
|
auths.tip.openid_connect = Use the OpenID Connect Discovery URL "https://{server}/.well-known/openid-configuration" to specify the endpoints
|
||||||
auths.tip.twitter = Go to %s, create an application and ensure that the “Allow this application to be used to Sign in with Twitter” option is enabled
|
auths.tip.twitter = Go to %s, create an application and ensure that the "Allow this application to be used to Sign in with Twitter" option is enabled
|
||||||
auths.tip.discord = Register a new application on %s
|
auths.tip.discord = Register a new application on %s
|
||||||
auths.tip.gitea = Register a new OAuth2 application. Guide can be found at %s
|
auths.tip.gitea = Register a new OAuth2 application. Guide can be found at %s
|
||||||
auths.tip.yandex = Create a new application at %s. Select following permissions from the "Yandex.Passport API" section: "Access to email address", "Access to user avatar" and "Access to username, first name and surname, gender"
|
auths.tip.yandex = Create a new application at %s. Select following permissions from the "Yandex.Passport API" section: "Access to email address", "Access to user avatar" and "Access to username, first name and surname, gender"
|
||||||
|
@ -21,6 +21,33 @@ import (
|
|||||||
webhook_service "code.gitea.io/gitea/services/webhook"
|
webhook_service "code.gitea.io/gitea/services/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// getPayloadConfigEnable extracts the "enable" boolean value from a payload config map
|
||||||
|
func getPayloadConfigEnable(m map[string]any) bool {
|
||||||
|
if val, ok := m["enable"]; ok {
|
||||||
|
if boolVal, ok := val.(bool); ok {
|
||||||
|
return boolVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPayloadConfigLimit extracts the "limit" integer value from a payload config map
|
||||||
|
func getPayloadConfigLimit(m map[string]any) int {
|
||||||
|
if val, ok := m["limit"]; ok {
|
||||||
|
switch v := val.(type) {
|
||||||
|
case int:
|
||||||
|
return v
|
||||||
|
case float64:
|
||||||
|
return int(v)
|
||||||
|
case string:
|
||||||
|
if intVal, err := strconv.Atoi(v); err == nil {
|
||||||
|
return intVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
// ListOwnerHooks lists the webhooks of the provided owner
|
// ListOwnerHooks lists the webhooks of the provided owner
|
||||||
func ListOwnerHooks(ctx *context.APIContext, owner *user_model.User) {
|
func ListOwnerHooks(ctx *context.APIContext, owner *user_model.User) {
|
||||||
opts := &webhook.ListWebhookOptions{
|
opts := &webhook.ListWebhookOptions{
|
||||||
@ -227,6 +254,44 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoI
|
|||||||
IsActive: form.Active,
|
IsActive: form.Active,
|
||||||
Type: form.Type,
|
Type: form.Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set webhook meta settings
|
||||||
|
if form.MetaSettings != nil {
|
||||||
|
metaSettings := webhook.MetaSettings{}
|
||||||
|
|
||||||
|
// Parse payload config
|
||||||
|
if payloadOptMap, ok := form.MetaSettings["payload_config"].(map[string]any); ok {
|
||||||
|
payloadOptConfig := webhook.PayloadConfig{}
|
||||||
|
|
||||||
|
// Parse files config
|
||||||
|
if filesConfig, ok := payloadOptMap["files"].(map[string]any); ok {
|
||||||
|
payloadOptConfig.Files = webhook.PayloadConfigItem{
|
||||||
|
Enable: getPayloadConfigEnable(filesConfig),
|
||||||
|
Limit: getPayloadConfigLimit(filesConfig),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
payloadOptConfig.Files = webhook.PayloadConfigItem{Enable: false, Limit: 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse commits config
|
||||||
|
if commitsConfig, ok := payloadOptMap["commits"].(map[string]any); ok {
|
||||||
|
payloadOptConfig.Commits = webhook.PayloadConfigItem{
|
||||||
|
Enable: getPayloadConfigEnable(commitsConfig),
|
||||||
|
Limit: getPayloadConfigLimit(commitsConfig),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
payloadOptConfig.Commits = webhook.PayloadConfigItem{Enable: false, Limit: 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
metaSettings.PayloadConfig = payloadOptConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := w.SetMetaSettings(metaSettings); err != nil {
|
||||||
|
ctx.APIErrorInternal(err)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err := w.SetHeaderAuthorization(form.AuthorizationHeader)
|
err := w.SetHeaderAuthorization(form.AuthorizationHeader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.APIErrorInternal(err)
|
ctx.APIErrorInternal(err)
|
||||||
@ -391,6 +456,43 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webh
|
|||||||
w.IsActive = *form.Active
|
w.IsActive = *form.Active
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update webhook meta settings
|
||||||
|
if form.MetaSettings != nil {
|
||||||
|
metaSettings := webhook.MetaSettings{}
|
||||||
|
|
||||||
|
// Parse payload config
|
||||||
|
if payloadOptMap, ok := (*form.MetaSettings)["payload_config"].(map[string]any); ok {
|
||||||
|
payloadOptConfig := webhook.PayloadConfig{}
|
||||||
|
|
||||||
|
// Parse files config
|
||||||
|
if filesConfig, ok := payloadOptMap["files"].(map[string]any); ok {
|
||||||
|
payloadOptConfig.Files = webhook.PayloadConfigItem{
|
||||||
|
Enable: getPayloadConfigEnable(filesConfig),
|
||||||
|
Limit: getPayloadConfigLimit(filesConfig),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
payloadOptConfig.Files = webhook.PayloadConfigItem{Enable: false, Limit: 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse commits config
|
||||||
|
if commitsConfig, ok := payloadOptMap["commits"].(map[string]any); ok {
|
||||||
|
payloadOptConfig.Commits = webhook.PayloadConfigItem{
|
||||||
|
Enable: getPayloadConfigEnable(commitsConfig),
|
||||||
|
Limit: getPayloadConfigLimit(commitsConfig),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
payloadOptConfig.Commits = webhook.PayloadConfigItem{Enable: false, Limit: 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
metaSettings.PayloadConfig = payloadOptConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := w.SetMetaSettings(metaSettings); err != nil {
|
||||||
|
ctx.APIErrorInternal(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := webhook.UpdateWebhook(ctx, w); err != nil {
|
if err := webhook.UpdateWebhook(ctx, w); err != nil {
|
||||||
ctx.APIErrorInternal(err)
|
ctx.APIErrorInternal(err)
|
||||||
return false
|
return false
|
||||||
|
@ -121,7 +121,15 @@ func checkHookType(ctx *context.Context) string {
|
|||||||
// WebhooksNew render creating webhook page
|
// WebhooksNew render creating webhook page
|
||||||
func WebhooksNew(ctx *context.Context) {
|
func WebhooksNew(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
|
ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
|
||||||
ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook_module.HookEvent{}}
|
|
||||||
|
// Create a new webhook with default meta settings
|
||||||
|
newWebhook := &webhook.Webhook{HookEvent: &webhook_module.HookEvent{}}
|
||||||
|
// Initialize meta settings with default values
|
||||||
|
if err := newWebhook.SetMetaSettings(webhook.DefaultMetaSettings()); err != nil {
|
||||||
|
ctx.ServerError("SetMetaSettings", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["Webhook"] = newWebhook
|
||||||
|
|
||||||
orCtx, err := getOwnerRepoCtx(ctx)
|
orCtx, err := getOwnerRepoCtx(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -207,7 +215,14 @@ func createWebhook(ctx *context.Context, params webhookParams) {
|
|||||||
ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
|
ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
|
||||||
ctx.Data["PageIsSettingsHooks"] = true
|
ctx.Data["PageIsSettingsHooks"] = true
|
||||||
ctx.Data["PageIsSettingsHooksNew"] = true
|
ctx.Data["PageIsSettingsHooksNew"] = true
|
||||||
ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook_module.HookEvent{}}
|
|
||||||
|
// Create a webhook with default meta settings for template rendering
|
||||||
|
newWebhook := &webhook.Webhook{HookEvent: &webhook_module.HookEvent{}}
|
||||||
|
if err := newWebhook.SetMetaSettings(webhook.DefaultMetaSettings()); err != nil {
|
||||||
|
ctx.ServerError("SetMetaSettings", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["Webhook"] = newWebhook
|
||||||
ctx.Data["HookType"] = params.Type
|
ctx.Data["HookType"] = params.Type
|
||||||
|
|
||||||
orCtx, err := getOwnerRepoCtx(ctx)
|
orCtx, err := getOwnerRepoCtx(ctx)
|
||||||
@ -244,6 +259,25 @@ func createWebhook(ctx *context.Context, params webhookParams) {
|
|||||||
OwnerID: orCtx.OwnerID,
|
OwnerID: orCtx.OwnerID,
|
||||||
IsSystemWebhook: orCtx.IsSystemWebhook,
|
IsSystemWebhook: orCtx.IsSystemWebhook,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set webhook meta settings with payload config
|
||||||
|
metaSettings := webhook.MetaSettings{
|
||||||
|
PayloadConfig: webhook.PayloadConfig{
|
||||||
|
Files: webhook.PayloadConfigItem{
|
||||||
|
Enable: params.WebhookForm.PayloadOptimizationFilesEnable,
|
||||||
|
Limit: params.WebhookForm.PayloadOptimizationFilesLimit,
|
||||||
|
},
|
||||||
|
Commits: webhook.PayloadConfigItem{
|
||||||
|
Enable: params.WebhookForm.PayloadOptimizationCommitsEnable,
|
||||||
|
Limit: params.WebhookForm.PayloadOptimizationCommitsLimit,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := w.SetMetaSettings(metaSettings); err != nil {
|
||||||
|
ctx.ServerError("SetMetaSettings", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
err = w.SetHeaderAuthorization(params.WebhookForm.AuthorizationHeader)
|
err = w.SetHeaderAuthorization(params.WebhookForm.AuthorizationHeader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("SetHeaderAuthorization", err)
|
ctx.ServerError("SetHeaderAuthorization", err)
|
||||||
@ -295,6 +329,24 @@ func editWebhook(ctx *context.Context, params webhookParams) {
|
|||||||
w.HTTPMethod = params.HTTPMethod
|
w.HTTPMethod = params.HTTPMethod
|
||||||
w.Meta = string(meta)
|
w.Meta = string(meta)
|
||||||
|
|
||||||
|
// Set webhook meta settings with payload config
|
||||||
|
metaSettings := webhook.MetaSettings{
|
||||||
|
PayloadConfig: webhook.PayloadConfig{
|
||||||
|
Files: webhook.PayloadConfigItem{
|
||||||
|
Enable: params.WebhookForm.PayloadOptimizationFilesEnable,
|
||||||
|
Limit: params.WebhookForm.PayloadOptimizationFilesLimit,
|
||||||
|
},
|
||||||
|
Commits: webhook.PayloadConfigItem{
|
||||||
|
Enable: params.WebhookForm.PayloadOptimizationCommitsEnable,
|
||||||
|
Limit: params.WebhookForm.PayloadOptimizationCommitsLimit,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := w.SetMetaSettings(metaSettings); err != nil {
|
||||||
|
ctx.ServerError("SetMetaSettings", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
err = w.SetHeaderAuthorization(params.WebhookForm.AuthorizationHeader)
|
err = w.SetHeaderAuthorization(params.WebhookForm.AuthorizationHeader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("SetHeaderAuthorization", err)
|
ctx.ServerError("SetHeaderAuthorization", err)
|
||||||
|
@ -239,6 +239,11 @@ type WebhookForm struct {
|
|||||||
BranchFilter string `binding:"GlobPattern"`
|
BranchFilter string `binding:"GlobPattern"`
|
||||||
AuthorizationHeader string
|
AuthorizationHeader string
|
||||||
Secret string
|
Secret string
|
||||||
|
// Payload config settings
|
||||||
|
PayloadOptimizationFilesEnable bool `form:"payload_optimization_files_enable"`
|
||||||
|
PayloadOptimizationFilesLimit int `form:"payload_optimization_files_limit"`
|
||||||
|
PayloadOptimizationCommitsEnable bool `form:"payload_optimization_commits_enable"`
|
||||||
|
PayloadOptimizationCommitsLimit int `form:"payload_optimization_commits_limit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushOnly if the hook will be triggered when push
|
// PushOnly if the hook will be triggered when push
|
||||||
|
@ -409,6 +409,21 @@ func ToHook(repoLink string, w *webhook_model.Webhook) (*api.Hook, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert meta settings to map
|
||||||
|
metaSettings := w.GetMetaSettings()
|
||||||
|
metaSettingsMap := map[string]any{
|
||||||
|
"payload_config": map[string]any{
|
||||||
|
"files": map[string]any{
|
||||||
|
"enable": metaSettings.PayloadConfig.Files.Enable,
|
||||||
|
"limit": metaSettings.PayloadConfig.Files.Limit,
|
||||||
|
},
|
||||||
|
"commits": map[string]any{
|
||||||
|
"enable": metaSettings.PayloadConfig.Commits.Enable,
|
||||||
|
"limit": metaSettings.PayloadConfig.Commits.Limit,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
return &api.Hook{
|
return &api.Hook{
|
||||||
ID: w.ID,
|
ID: w.ID,
|
||||||
Type: w.Type,
|
Type: w.Type,
|
||||||
@ -417,6 +432,7 @@ func ToHook(repoLink string, w *webhook_model.Webhook) (*api.Hook, error) {
|
|||||||
Config: config,
|
Config: config,
|
||||||
Events: w.EventsArray(),
|
Events: w.EventsArray(),
|
||||||
AuthorizationHeader: authorizationHeader,
|
AuthorizationHeader: authorizationHeader,
|
||||||
|
MetaSettings: metaSettingsMap,
|
||||||
Updated: w.UpdatedUnix.AsTime(),
|
Updated: w.UpdatedUnix.AsTime(),
|
||||||
Created: w.CreatedUnix.AsTime(),
|
Created: w.CreatedUnix.AsTime(),
|
||||||
BranchFilter: w.BranchFilter,
|
BranchFilter: w.BranchFilter,
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
actions_model "code.gitea.io/gitea/models/actions"
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
@ -15,10 +16,12 @@ import (
|
|||||||
access_model "code.gitea.io/gitea/models/perm/access"
|
access_model "code.gitea.io/gitea/models/perm/access"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
webhook_model "code.gitea.io/gitea/models/webhook"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/gitrepo"
|
"code.gitea.io/gitea/modules/gitrepo"
|
||||||
"code.gitea.io/gitea/modules/httplib"
|
"code.gitea.io/gitea/modules/httplib"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/optional"
|
||||||
"code.gitea.io/gitea/modules/repository"
|
"code.gitea.io/gitea/modules/repository"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
@ -641,6 +644,138 @@ func (m *webhookNotifier) IssueChangeMilestone(ctx context.Context, doer *user_m
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// applyWebhookPayloadOptimizations applies payload optimizations based on webhook configurations
|
||||||
|
func (m *webhookNotifier) applyWebhookPayloadOptimizations(ctx context.Context, repo *repo_model.Repository, apiCommits []*api.PayloadCommit, apiHeadCommit *api.PayloadCommit) ([]*api.PayloadCommit, *api.PayloadCommit) {
|
||||||
|
// Get all webhooks for this repository
|
||||||
|
webhooks, err := db.Find[webhook_model.Webhook](ctx, webhook_model.ListWebhookOptions{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
IsActive: optional.Some(true),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to get webhooks for repository %d: %v", repo.ID, err)
|
||||||
|
// Continue with default behavior if we can't get webhooks
|
||||||
|
return apiCommits, apiHeadCommit
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any webhook has payload optimization options enabled
|
||||||
|
var filesLimit, commitsLimit int
|
||||||
|
hasFilesLimit := false
|
||||||
|
hasCommitsLimit := false
|
||||||
|
optimizationEnabled := false
|
||||||
|
|
||||||
|
for _, webhook := range webhooks {
|
||||||
|
if webhook.HasEvent(webhook_module.HookEventPush) {
|
||||||
|
config := webhook.GetPayloadConfig()
|
||||||
|
|
||||||
|
// Check files optimization
|
||||||
|
if config.Files.Enable {
|
||||||
|
optimizationEnabled = true
|
||||||
|
if !hasFilesLimit || config.Files.Limit < filesLimit {
|
||||||
|
filesLimit = config.Files.Limit
|
||||||
|
hasFilesLimit = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check commits optimization
|
||||||
|
if config.Commits.Enable {
|
||||||
|
optimizationEnabled = true
|
||||||
|
if !hasCommitsLimit || config.Commits.Limit < commitsLimit {
|
||||||
|
commitsLimit = config.Commits.Limit
|
||||||
|
hasCommitsLimit = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply payload optimizations based on webhook configurations
|
||||||
|
// 0: trim all (none kept), >0: trim to N items (forward order), <0: trim to N items (reverse order)
|
||||||
|
if optimizationEnabled {
|
||||||
|
// Apply files optimization to all commits
|
||||||
|
if hasFilesLimit {
|
||||||
|
for _, commit := range apiCommits {
|
||||||
|
if commit.Added != nil {
|
||||||
|
if filesLimit == 0 {
|
||||||
|
commit.Added = nil
|
||||||
|
} else if filesLimit > 0 && len(commit.Added) > filesLimit {
|
||||||
|
commit.Added = commit.Added[:filesLimit]
|
||||||
|
} else if filesLimit < 0 && len(commit.Added) > -filesLimit {
|
||||||
|
// Reverse order: keep the last N items
|
||||||
|
commit.Added = commit.Added[len(commit.Added)+filesLimit:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if commit.Removed != nil {
|
||||||
|
if filesLimit == 0 {
|
||||||
|
commit.Removed = nil
|
||||||
|
} else if filesLimit > 0 && len(commit.Removed) > filesLimit {
|
||||||
|
commit.Removed = commit.Removed[:filesLimit]
|
||||||
|
} else if filesLimit < 0 && len(commit.Removed) > -filesLimit {
|
||||||
|
// Reverse order: keep the last N items
|
||||||
|
commit.Removed = commit.Removed[len(commit.Removed)+filesLimit:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if commit.Modified != nil {
|
||||||
|
if filesLimit == 0 {
|
||||||
|
commit.Modified = nil
|
||||||
|
} else if filesLimit > 0 && len(commit.Modified) > filesLimit {
|
||||||
|
commit.Modified = commit.Modified[:filesLimit]
|
||||||
|
} else if filesLimit < 0 && len(commit.Modified) > -filesLimit {
|
||||||
|
// Reverse order: keep the last N items
|
||||||
|
commit.Modified = commit.Modified[len(commit.Modified)+filesLimit:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply files optimization to head commit
|
||||||
|
if apiHeadCommit != nil {
|
||||||
|
if apiHeadCommit.Added != nil {
|
||||||
|
if filesLimit == 0 {
|
||||||
|
apiHeadCommit.Added = nil
|
||||||
|
} else if filesLimit > 0 && len(apiHeadCommit.Added) > filesLimit {
|
||||||
|
apiHeadCommit.Added = apiHeadCommit.Added[:filesLimit]
|
||||||
|
} else if filesLimit < 0 && len(apiHeadCommit.Added) > -filesLimit {
|
||||||
|
// Reverse order: keep the last N items
|
||||||
|
apiHeadCommit.Added = apiHeadCommit.Added[len(apiHeadCommit.Added)+filesLimit:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if apiHeadCommit.Removed != nil {
|
||||||
|
if filesLimit == 0 {
|
||||||
|
apiHeadCommit.Removed = nil
|
||||||
|
} else if filesLimit > 0 && len(apiHeadCommit.Removed) > filesLimit {
|
||||||
|
apiHeadCommit.Removed = apiHeadCommit.Removed[:filesLimit]
|
||||||
|
} else if filesLimit < 0 && len(apiHeadCommit.Removed) > -filesLimit {
|
||||||
|
// Reverse order: keep the last N items
|
||||||
|
apiHeadCommit.Removed = apiHeadCommit.Removed[len(apiHeadCommit.Removed)+filesLimit:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if apiHeadCommit.Modified != nil {
|
||||||
|
if filesLimit == 0 {
|
||||||
|
apiHeadCommit.Modified = nil
|
||||||
|
} else if filesLimit > 0 && len(apiHeadCommit.Modified) > filesLimit {
|
||||||
|
apiHeadCommit.Modified = apiHeadCommit.Modified[:filesLimit]
|
||||||
|
} else if filesLimit < 0 && len(apiHeadCommit.Modified) > -filesLimit {
|
||||||
|
// Reverse order: keep the last N items
|
||||||
|
apiHeadCommit.Modified = apiHeadCommit.Modified[len(apiHeadCommit.Modified)+filesLimit:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply commits optimization
|
||||||
|
if hasCommitsLimit {
|
||||||
|
if commitsLimit == 0 {
|
||||||
|
apiCommits = nil
|
||||||
|
} else if commitsLimit > 0 && len(apiCommits) > commitsLimit {
|
||||||
|
apiCommits = apiCommits[:commitsLimit]
|
||||||
|
} else if commitsLimit < 0 && len(apiCommits) > -commitsLimit {
|
||||||
|
// Reverse order: keep the last N commits
|
||||||
|
apiCommits = apiCommits[len(apiCommits)+commitsLimit:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiCommits, apiHeadCommit
|
||||||
|
}
|
||||||
|
|
||||||
func (m *webhookNotifier) PushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
|
func (m *webhookNotifier) PushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
|
||||||
apiPusher := convert.ToUser(ctx, pusher, nil)
|
apiPusher := convert.ToUser(ctx, pusher, nil)
|
||||||
apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo)
|
apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo)
|
||||||
@ -649,6 +784,9 @@ func (m *webhookNotifier) PushCommits(ctx context.Context, pusher *user_model.Us
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply payload optimizations
|
||||||
|
apiCommits, apiHeadCommit = m.applyWebhookPayloadOptimizations(ctx, repo, apiCommits, apiHeadCommit)
|
||||||
|
|
||||||
if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{
|
if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{
|
||||||
Ref: opts.RefFullName.String(),
|
Ref: opts.RefFullName.String(),
|
||||||
Before: opts.OldCommitID,
|
Before: opts.OldCommitID,
|
||||||
@ -888,6 +1026,9 @@ func (m *webhookNotifier) SyncPushCommits(ctx context.Context, pusher *user_mode
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply payload optimizations
|
||||||
|
apiCommits, apiHeadCommit = m.applyWebhookPayloadOptimizations(ctx, repo, apiCommits, apiHeadCommit)
|
||||||
|
|
||||||
if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{
|
if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{
|
||||||
Ref: opts.RefFullName.String(),
|
Ref: opts.RefFullName.String(),
|
||||||
Before: opts.OldCommitID,
|
Before: opts.OldCommitID,
|
||||||
|
@ -90,3 +90,171 @@ func TestWebhookUserMail(t *testing.T) {
|
|||||||
assert.Equal(t, user.GetPlaceholderEmail(), convert.ToUser(t.Context(), user, nil).Email)
|
assert.Equal(t, user.GetPlaceholderEmail(), convert.ToUser(t.Context(), user, nil).Email)
|
||||||
assert.Equal(t, user.Email, convert.ToUser(t.Context(), user, user).Email)
|
assert.Equal(t, user.Email, convert.ToUser(t.Context(), user, user).Email)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWebhookPayloadOptimization(t *testing.T) {
|
||||||
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||||
|
|
||||||
|
// Create test webhook
|
||||||
|
webhook := &webhook_model.Webhook{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
URL: "http://example.com/webhook",
|
||||||
|
HTTPMethod: "POST",
|
||||||
|
ContentType: webhook_model.ContentTypeJSON,
|
||||||
|
Secret: "secret",
|
||||||
|
IsActive: true,
|
||||||
|
Type: webhook_module.GITEA,
|
||||||
|
HookEvent: &webhook_module.HookEvent{
|
||||||
|
PushOnly: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case 1: No optimization enabled
|
||||||
|
webhook.SetMetaSettings(webhook_model.MetaSettings{
|
||||||
|
PayloadConfig: webhook_model.PayloadConfig{
|
||||||
|
Files: webhook_model.PayloadConfigItem{Enable: false, Limit: 0},
|
||||||
|
Commits: webhook_model.PayloadConfigItem{Enable: false, Limit: 0},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
err := webhook.UpdateEvent()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = webhook_model.CreateWebhook(t.Context(), webhook)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
apiCommits := []*api.PayloadCommit{
|
||||||
|
{
|
||||||
|
ID: "abc123",
|
||||||
|
Message: "Test commit",
|
||||||
|
Added: []string{"file1.txt", "file2.txt"},
|
||||||
|
Removed: []string{"oldfile.txt"},
|
||||||
|
Modified: []string{"modified.txt"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "def456",
|
||||||
|
Message: "Another commit",
|
||||||
|
Added: []string{"file3.txt"},
|
||||||
|
Removed: []string{},
|
||||||
|
Modified: []string{"file1.txt"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
apiHeadCommit := &api.PayloadCommit{
|
||||||
|
ID: "def456",
|
||||||
|
Message: "Another commit",
|
||||||
|
Added: []string{"file3.txt"},
|
||||||
|
Removed: []string{},
|
||||||
|
Modified: []string{"file1.txt"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not modify anything when optimization is disabled
|
||||||
|
optimizedCommits, optimizedHeadCommit := (&webhookNotifier{}).applyWebhookPayloadOptimizations(t.Context(), repo, apiCommits, apiHeadCommit)
|
||||||
|
if assert.NotNil(t, optimizedCommits) && len(optimizedCommits) == 2 {
|
||||||
|
assert.Equal(t, []string{"file1.txt", "file2.txt"}, optimizedCommits[0].Added)
|
||||||
|
assert.Equal(t, []string{"oldfile.txt"}, optimizedCommits[0].Removed)
|
||||||
|
assert.Equal(t, []string{"modified.txt"}, optimizedCommits[0].Modified)
|
||||||
|
assert.Equal(t, []string{"file3.txt"}, optimizedCommits[1].Added)
|
||||||
|
assert.Equal(t, []string{}, optimizedCommits[1].Removed)
|
||||||
|
assert.Equal(t, []string{"file1.txt"}, optimizedCommits[1].Modified)
|
||||||
|
}
|
||||||
|
if assert.NotNil(t, optimizedHeadCommit) {
|
||||||
|
assert.Equal(t, []string{"file3.txt"}, optimizedHeadCommit.Added)
|
||||||
|
assert.Equal(t, []string{}, optimizedHeadCommit.Removed)
|
||||||
|
assert.Equal(t, []string{"file1.txt"}, optimizedHeadCommit.Modified)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case 2: Files optimization enabled, limit = 0 (trim all)
|
||||||
|
webhook.SetMetaSettings(webhook_model.MetaSettings{
|
||||||
|
PayloadConfig: webhook_model.PayloadConfig{
|
||||||
|
Files: webhook_model.PayloadConfigItem{Enable: true, Limit: 0},
|
||||||
|
Commits: webhook_model.PayloadConfigItem{Enable: false, Limit: 0},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
err = webhook_model.UpdateWebhook(t.Context(), webhook)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
apiCommits = []*api.PayloadCommit{
|
||||||
|
{
|
||||||
|
ID: "abc123",
|
||||||
|
Message: "Test commit",
|
||||||
|
Added: []string{"file1.txt", "file2.txt"},
|
||||||
|
Removed: []string{"oldfile.txt"},
|
||||||
|
Modified: []string{"modified.txt"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "def456",
|
||||||
|
Message: "Another commit",
|
||||||
|
Added: []string{"file3.txt"},
|
||||||
|
Removed: []string{},
|
||||||
|
Modified: []string{"file1.txt"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
apiHeadCommit = &api.PayloadCommit{
|
||||||
|
ID: "def456",
|
||||||
|
Message: "Another commit",
|
||||||
|
Added: []string{"file3.txt"},
|
||||||
|
Removed: []string{},
|
||||||
|
Modified: []string{"file1.txt"},
|
||||||
|
}
|
||||||
|
|
||||||
|
optimizedCommits, optimizedHeadCommit = (&webhookNotifier{}).applyWebhookPayloadOptimizations(t.Context(), repo, apiCommits, apiHeadCommit)
|
||||||
|
if assert.NotNil(t, optimizedCommits) && len(optimizedCommits) == 2 {
|
||||||
|
assert.Nil(t, optimizedCommits[0].Added)
|
||||||
|
assert.Nil(t, optimizedCommits[0].Removed)
|
||||||
|
assert.Nil(t, optimizedCommits[0].Modified)
|
||||||
|
assert.Nil(t, optimizedCommits[1].Added)
|
||||||
|
assert.Nil(t, optimizedCommits[1].Removed)
|
||||||
|
assert.Nil(t, optimizedCommits[1].Modified)
|
||||||
|
}
|
||||||
|
if assert.NotNil(t, optimizedHeadCommit) {
|
||||||
|
assert.Nil(t, optimizedHeadCommit.Added)
|
||||||
|
assert.Nil(t, optimizedHeadCommit.Removed)
|
||||||
|
assert.Nil(t, optimizedHeadCommit.Modified)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case 3: Commits optimization enabled, limit = 1 (keep first)
|
||||||
|
webhook.SetMetaSettings(webhook_model.MetaSettings{
|
||||||
|
PayloadConfig: webhook_model.PayloadConfig{
|
||||||
|
Files: webhook_model.PayloadConfigItem{Enable: false, Limit: 0},
|
||||||
|
Commits: webhook_model.PayloadConfigItem{Enable: true, Limit: 1},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
err = webhook_model.UpdateWebhook(t.Context(), webhook)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
apiCommits = []*api.PayloadCommit{
|
||||||
|
{
|
||||||
|
ID: "abc123",
|
||||||
|
Message: "Test commit",
|
||||||
|
Added: []string{"file1.txt", "file2.txt"},
|
||||||
|
Removed: []string{"oldfile.txt"},
|
||||||
|
Modified: []string{"modified.txt"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "def456",
|
||||||
|
Message: "Another commit",
|
||||||
|
Added: []string{"file3.txt"},
|
||||||
|
Removed: []string{},
|
||||||
|
Modified: []string{"file1.txt"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
apiHeadCommit = &api.PayloadCommit{
|
||||||
|
ID: "def456",
|
||||||
|
Message: "Another commit",
|
||||||
|
Added: []string{"file3.txt"},
|
||||||
|
Removed: []string{},
|
||||||
|
Modified: []string{"file1.txt"},
|
||||||
|
}
|
||||||
|
|
||||||
|
optimizedCommits, optimizedHeadCommit = (&webhookNotifier{}).applyWebhookPayloadOptimizations(t.Context(), repo, apiCommits, apiHeadCommit)
|
||||||
|
if assert.NotNil(t, optimizedCommits) && len(optimizedCommits) == 1 {
|
||||||
|
assert.Equal(t, []string{"file1.txt", "file2.txt"}, optimizedCommits[0].Added)
|
||||||
|
assert.Equal(t, []string{"oldfile.txt"}, optimizedCommits[0].Removed)
|
||||||
|
assert.Equal(t, []string{"modified.txt"}, optimizedCommits[0].Modified)
|
||||||
|
}
|
||||||
|
if assert.NotNil(t, optimizedHeadCommit) {
|
||||||
|
assert.Equal(t, []string{"file3.txt"}, optimizedHeadCommit.Added)
|
||||||
|
assert.Equal(t, []string{}, optimizedHeadCommit.Removed)
|
||||||
|
assert.Equal(t, []string{"file1.txt"}, optimizedHeadCommit.Modified)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -47,6 +47,41 @@
|
|||||||
<span class="help">{{ctx.Locale.Tr "repo.settings.branch_filter_desc" "https://pkg.go.dev/github.com/gobwas/glob#Compile" "github.com/gobwas/glob"}}</span>
|
<span class="help">{{ctx.Locale.Tr "repo.settings.branch_filter_desc" "https://pkg.go.dev/github.com/gobwas/glob#Compile" "github.com/gobwas/glob"}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Payload size optimization options -->
|
||||||
|
<div class="field">
|
||||||
|
<h4>{{ctx.Locale.Tr "repo.settings.payload_optimization"}}</h4>
|
||||||
|
<div class="field">
|
||||||
|
<h5>{{ctx.Locale.Tr "repo.settings.payload_optimization_files"}}</h5>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input name="payload_optimization_files_enable" type="checkbox" {{if .Webhook.IsFilesConfigEnabled}}checked{{end}}>
|
||||||
|
<label>{{ctx.Locale.Tr "repo.settings.payload_optimization_enable"}}</label>
|
||||||
|
<span class="help">{{ctx.Locale.Tr "repo.settings.payload_optimization_enable_desc"}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field {{if not .Webhook.IsFilesConfigEnabled}}tw-hidden{{end}}">
|
||||||
|
<label>{{ctx.Locale.Tr "repo.settings.payload_optimization_limit"}}</label>
|
||||||
|
<input name="payload_optimization_files_limit" type="number" value="{{.Webhook.GetFilesConfigLimit}}" placeholder="0" {{if not .Webhook.IsFilesConfigEnabled}}disabled{{end}}>
|
||||||
|
<span class="help">{{ctx.Locale.Tr "repo.settings.payload_optimization_limit_desc"}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<h5>{{ctx.Locale.Tr "repo.settings.payload_optimization_commits"}}</h5>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input name="payload_optimization_commits_enable" type="checkbox" {{if .Webhook.IsCommitsConfigEnabled}}checked{{end}}>
|
||||||
|
<label>{{ctx.Locale.Tr "repo.settings.payload_optimization_enable"}}</label>
|
||||||
|
<span class="help">{{ctx.Locale.Tr "repo.settings.payload_optimization_enable_desc"}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field {{if not .Webhook.IsCommitsConfigEnabled}}tw-hidden{{end}}">
|
||||||
|
<label>{{ctx.Locale.Tr "repo.settings.payload_optimization_limit"}}</label>
|
||||||
|
<input name="payload_optimization_commits_limit" type="number" value="{{.Webhook.GetCommitsConfigLimit}}" placeholder="0" {{if not .Webhook.IsCommitsConfigEnabled}}disabled{{end}}>
|
||||||
|
<span class="help">{{ctx.Locale.Tr "repo.settings.payload_optimization_limit_desc"}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<h4>{{ctx.Locale.Tr "repo.settings.event_desc"}}</h4>
|
<h4>{{ctx.Locale.Tr "repo.settings.event_desc"}}</h4>
|
||||||
<div class="grouped event type fields">
|
<div class="grouped event type fields">
|
||||||
|
18
templates/swagger/v1_json.tmpl
generated
18
templates/swagger/v1_json.tmpl
generated
@ -22939,6 +22939,12 @@
|
|||||||
},
|
},
|
||||||
"x-go-name": "Events"
|
"x-go-name": "Events"
|
||||||
},
|
},
|
||||||
|
"meta_settings": {
|
||||||
|
"description": "Webhook metadata settings including payload optimization",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {},
|
||||||
|
"x-go-name": "MetaSettings"
|
||||||
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@ -24172,6 +24178,12 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"x-go-name": "Events"
|
"x-go-name": "Events"
|
||||||
|
},
|
||||||
|
"meta_settings": {
|
||||||
|
"description": "Webhook metadata settings including payload optimization",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {},
|
||||||
|
"x-go-name": "MetaSettings"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
@ -25576,6 +25588,12 @@
|
|||||||
"format": "int64",
|
"format": "int64",
|
||||||
"x-go-name": "ID"
|
"x-go-name": "ID"
|
||||||
},
|
},
|
||||||
|
"meta_settings": {
|
||||||
|
"description": "MetaSettings webhook metadata settings including payload optimization",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {},
|
||||||
|
"x-go-name": "MetaSettings"
|
||||||
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"description": "The type of the webhook (e.g., gitea, slack, discord)",
|
"description": "The type of the webhook (e.g., gitea, slack, discord)",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -1716,3 +1716,98 @@ jobs:
|
|||||||
assert.Equal(t, "user2/repo1", webhookData.payloads[i].Repo.FullName)
|
assert.Equal(t, "user2/repo1", webhookData.payloads[i].Repo.FullName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_WebhookPayloadOptimizationAPI(t *testing.T) {
|
||||||
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
|
session := loginUser(t, "user2")
|
||||||
|
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll)
|
||||||
|
|
||||||
|
// Test creating webhook with payload config options via API
|
||||||
|
createHookOption := map[string]any{
|
||||||
|
"type": "gitea",
|
||||||
|
"config": map[string]string{
|
||||||
|
"url": "http://example.com/webhook",
|
||||||
|
"content_type": "json",
|
||||||
|
},
|
||||||
|
"events": []string{"push"},
|
||||||
|
"meta_settings": map[string]any{
|
||||||
|
"payload_config": map[string]any{
|
||||||
|
"files": map[string]any{
|
||||||
|
"enable": true,
|
||||||
|
"limit": 2,
|
||||||
|
},
|
||||||
|
"commits": map[string]any{
|
||||||
|
"enable": true,
|
||||||
|
"limit": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"active": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/hooks", createHookOption).AddTokenAuth(token)
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusCreated)
|
||||||
|
|
||||||
|
var hook api.Hook
|
||||||
|
DecodeJSON(t, resp, &hook)
|
||||||
|
|
||||||
|
// Verify the webhook was created with correct payload config settings
|
||||||
|
assert.NotNil(t, hook.MetaSettings)
|
||||||
|
payloadOptConfig := hook.MetaSettings["payload_config"].(map[string]any)
|
||||||
|
filesConfig := payloadOptConfig["files"].(map[string]any)
|
||||||
|
commitsConfig := payloadOptConfig["commits"].(map[string]any)
|
||||||
|
assert.Equal(t, true, filesConfig["enable"])
|
||||||
|
assert.InEpsilon(t, 2.0, filesConfig["limit"], 0.01)
|
||||||
|
assert.Equal(t, true, commitsConfig["enable"])
|
||||||
|
assert.InEpsilon(t, 1.0, commitsConfig["limit"], 0.01)
|
||||||
|
|
||||||
|
// Test updating webhook with different payload config options
|
||||||
|
editHookOption := map[string]any{
|
||||||
|
"meta_settings": map[string]any{
|
||||||
|
"payload_config": map[string]any{
|
||||||
|
"files": map[string]any{
|
||||||
|
"enable": false,
|
||||||
|
"limit": 0,
|
||||||
|
},
|
||||||
|
"commits": map[string]any{
|
||||||
|
"enable": false,
|
||||||
|
"limit": 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/user2/repo1/hooks/%d", hook.ID), editHookOption).AddTokenAuth(token)
|
||||||
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
var updatedHook api.Hook
|
||||||
|
DecodeJSON(t, resp, &updatedHook)
|
||||||
|
|
||||||
|
// Verify the webhook was updated with correct payload config settings
|
||||||
|
assert.NotNil(t, updatedHook.MetaSettings)
|
||||||
|
payloadOptConfig = updatedHook.MetaSettings["payload_config"].(map[string]any)
|
||||||
|
filesConfig = payloadOptConfig["files"].(map[string]any)
|
||||||
|
commitsConfig = payloadOptConfig["commits"].(map[string]any)
|
||||||
|
assert.Equal(t, false, filesConfig["enable"])
|
||||||
|
assert.EqualValues(t, 0, filesConfig["limit"])
|
||||||
|
assert.Equal(t, false, commitsConfig["enable"])
|
||||||
|
assert.EqualValues(t, 0, commitsConfig["limit"])
|
||||||
|
|
||||||
|
// Test getting webhook to verify the settings are persisted
|
||||||
|
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/user2/repo1/hooks/%d", hook.ID)).AddTokenAuth(token)
|
||||||
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
var retrievedHook api.Hook
|
||||||
|
DecodeJSON(t, resp, &retrievedHook)
|
||||||
|
|
||||||
|
// Verify the webhook settings are correctly retrieved
|
||||||
|
assert.NotNil(t, retrievedHook.MetaSettings)
|
||||||
|
payloadOptConfig = retrievedHook.MetaSettings["payload_config"].(map[string]any)
|
||||||
|
filesConfig = payloadOptConfig["files"].(map[string]any)
|
||||||
|
commitsConfig = payloadOptConfig["commits"].(map[string]any)
|
||||||
|
assert.Equal(t, false, filesConfig["enable"])
|
||||||
|
assert.EqualValues(t, 0, filesConfig["limit"])
|
||||||
|
assert.Equal(t, false, commitsConfig["enable"])
|
||||||
|
assert.EqualValues(t, 0, commitsConfig["limit"])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"include": [
|
"include": [
|
||||||
"${configDir}/.*",
|
|
||||||
"${configDir}/*",
|
"${configDir}/*",
|
||||||
|
"${configDir}/.*",
|
||||||
"${configDir}/tests/e2e/**/*",
|
"${configDir}/tests/e2e/**/*",
|
||||||
"${configDir}/tests/e2e/**/.*",
|
"${configDir}/tests/e2e/**/.*",
|
||||||
"${configDir}/tools/**/*",
|
"${configDir}/tools/**/*",
|
||||||
@ -17,27 +17,31 @@
|
|||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"allowUnreachableCode": false,
|
||||||
|
"allowUnusedLabels": false,
|
||||||
"alwaysStrict": true,
|
"alwaysStrict": true,
|
||||||
"erasableSyntaxOnly": true,
|
"erasableSyntaxOnly": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
|
"exactOptionalPropertyTypes": false,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"libReplacement": false,
|
"libReplacement": false,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": false,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"verbatimModuleSyntax": true,
|
"sourceMap": true,
|
||||||
"stripInternal": true,
|
|
||||||
"strict": false,
|
"strict": false,
|
||||||
"strictBindCallApply": true,
|
"strictBindCallApply": true,
|
||||||
"strictBuiltinIteratorReturn": true,
|
"strictBuiltinIteratorReturn": true,
|
||||||
"strictFunctionTypes": true,
|
"strictFunctionTypes": true,
|
||||||
"noImplicitAny": true,
|
"stripInternal": true,
|
||||||
"noImplicitThis": true,
|
"verbatimModuleSyntax": true,
|
||||||
"noUnusedLocals": true,
|
|
||||||
"noUnusedParameters": true,
|
|
||||||
"noPropertyAccessFromIndexSignature": false,
|
|
||||||
"exactOptionalPropertyTypes": false,
|
|
||||||
"sourceMap": true,
|
|
||||||
"types": [
|
"types": [
|
||||||
"vitest/globals",
|
"vitest/globals",
|
||||||
"./web_src/js/globals.d.ts",
|
"./web_src/js/globals.d.ts",
|
||||||
|
31
web_src/js/features/repo-settings-webhook.ts
Normal file
31
web_src/js/features/repo-settings-webhook.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Webhook settings functionality
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {toggleElemClass} from '../utils/dom.ts';
|
||||||
|
|
||||||
|
function setupOptimizationToggle(enableFieldName: string, limitFieldName: string): void {
|
||||||
|
const enableCheckbox = document.querySelector<HTMLInputElement>(`input[name="${enableFieldName}"]`);
|
||||||
|
if (!enableCheckbox) return;
|
||||||
|
|
||||||
|
enableCheckbox.addEventListener('change', (e) => {
|
||||||
|
const target = e.target as HTMLInputElement;
|
||||||
|
const limitField = document.querySelector<HTMLInputElement>(`input[name="${limitFieldName}"]`);
|
||||||
|
if (limitField) {
|
||||||
|
limitField.disabled = !target.checked;
|
||||||
|
// Use toggleElemClass to show/hide the limit field container
|
||||||
|
const limitFieldContainer = limitField.closest('.field');
|
||||||
|
if (limitFieldContainer) {
|
||||||
|
toggleElemClass(limitFieldContainer, 'tw-hidden', !target.checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initRepoSettingsWebhook(): void {
|
||||||
|
if (!document.querySelector('.page-content.repository.settings.webhook')) return;
|
||||||
|
|
||||||
|
// Setup payload optimization toggles
|
||||||
|
setupOptimizationToggle('payload_optimization_files_enable', 'payload_optimization_files_limit');
|
||||||
|
setupOptimizationToggle('payload_optimization_commits_enable', 'payload_optimization_commits_limit');
|
||||||
|
}
|
@ -2,6 +2,7 @@ import {createMonaco} from './codeeditor.ts';
|
|||||||
import {onInputDebounce, queryElems, toggleElem} from '../utils/dom.ts';
|
import {onInputDebounce, queryElems, toggleElem} from '../utils/dom.ts';
|
||||||
import {POST} from '../modules/fetch.ts';
|
import {POST} from '../modules/fetch.ts';
|
||||||
import {initRepoSettingsBranchesDrag} from './repo-settings-branches.ts';
|
import {initRepoSettingsBranchesDrag} from './repo-settings-branches.ts';
|
||||||
|
import {initRepoSettingsWebhook} from './repo-settings-webhook.ts';
|
||||||
import {fomanticQuery} from '../modules/fomantic/base.ts';
|
import {fomanticQuery} from '../modules/fomantic/base.ts';
|
||||||
import {globMatch} from '../utils/glob.ts';
|
import {globMatch} from '../utils/glob.ts';
|
||||||
|
|
||||||
@ -152,4 +153,5 @@ export function initRepoSettings() {
|
|||||||
initRepoSettingsSearchTeamBox();
|
initRepoSettingsSearchTeamBox();
|
||||||
initRepoSettingsGitHook();
|
initRepoSettingsGitHook();
|
||||||
initRepoSettingsBranchesDrag();
|
initRepoSettingsBranchesDrag();
|
||||||
|
initRepoSettingsWebhook();
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ export function initTableSort() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function tableSort(normSort: string, revSort: string, isDefault: string) {
|
function tableSort(normSort: string, revSort: string, isDefault: string) {
|
||||||
if (!normSort) return false;
|
if (!normSort) return;
|
||||||
if (!revSort) revSort = '';
|
if (!revSort) revSort = '';
|
||||||
|
|
||||||
const url = new URL(window.location.href);
|
const url = new URL(window.location.href);
|
||||||
|
@ -43,7 +43,7 @@ type ToastOpts = {
|
|||||||
type ToastifyElement = HTMLElement & {_giteaToastifyInstance?: Toast};
|
type ToastifyElement = HTMLElement & {_giteaToastifyInstance?: Toast};
|
||||||
|
|
||||||
/** See https://github.com/apvarun/toastify-js#api for options */
|
/** See https://github.com/apvarun/toastify-js#api for options */
|
||||||
function showToast(message: string, level: Intent, {gravity, position, duration, useHtmlBody, preventDuplicates = true, ...other}: ToastOpts = {}): Toast {
|
function showToast(message: string, level: Intent, {gravity, position, duration, useHtmlBody, preventDuplicates = true, ...other}: ToastOpts = {}): Toast | null {
|
||||||
const body = useHtmlBody ? message : htmlEscape(message);
|
const body = useHtmlBody ? message : htmlEscape(message);
|
||||||
const parent = document.querySelector('.ui.dimmer.active') ?? document.body;
|
const parent = document.querySelector('.ui.dimmer.active') ?? document.body;
|
||||||
const duplicateKey = preventDuplicates ? (preventDuplicates === true ? `${level}-${body}` : preventDuplicates) : '';
|
const duplicateKey = preventDuplicates ? (preventDuplicates === true ? `${level}-${body}` : preventDuplicates) : '';
|
||||||
@ -56,7 +56,7 @@ function showToast(message: string, level: Intent, {gravity, position, duration,
|
|||||||
showElem(toastDupNumEl);
|
showElem(toastDupNumEl);
|
||||||
toastDupNumEl.textContent = String(Number(toastDupNumEl.textContent) + 1);
|
toastDupNumEl.textContent = String(Number(toastDupNumEl.textContent) + 1);
|
||||||
animateOnce(toastDupNumEl, 'pulse-1p5-200');
|
animateOnce(toastDupNumEl, 'pulse-1p5-200');
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,15 +83,15 @@ function showToast(message: string, level: Intent, {gravity, position, duration,
|
|||||||
return toast;
|
return toast;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showInfoToast(message: string, opts?: ToastOpts): Toast {
|
export function showInfoToast(message: string, opts?: ToastOpts): Toast | null {
|
||||||
return showToast(message, 'info', opts);
|
return showToast(message, 'info', opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showWarningToast(message: string, opts?: ToastOpts): Toast {
|
export function showWarningToast(message: string, opts?: ToastOpts): Toast | null {
|
||||||
return showToast(message, 'warning', opts);
|
return showToast(message, 'warning', opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showErrorToast(message: string, opts?: ToastOpts): Toast {
|
export function showErrorToast(message: string, opts?: ToastOpts): Toast | null {
|
||||||
return showToast(message, 'error', opts);
|
return showToast(message, 'error', opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user