mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 00:39:13 +00:00
feat(filters): external webhook retry on status codes (#1206)
* feat: external filter retry status codes * chore: go mod tidy * fix(database): migrations --------- Co-authored-by: ze0s <ze0s@riseup.net>
This commit is contained in:
parent
40a1a4c014
commit
2080136669
11 changed files with 330 additions and 165 deletions
1
go.mod
1
go.mod
|
@ -13,6 +13,7 @@ require (
|
||||||
github.com/autobrr/go-qbittorrent v1.6.0
|
github.com/autobrr/go-qbittorrent v1.6.0
|
||||||
github.com/autobrr/go-rtorrent v1.10.0
|
github.com/autobrr/go-rtorrent v1.10.0
|
||||||
github.com/avast/retry-go v3.0.0+incompatible
|
github.com/avast/retry-go v3.0.0+incompatible
|
||||||
|
github.com/avast/retry-go/v4 v4.5.0
|
||||||
github.com/dcarbone/zadapters/zstdlog v1.0.0
|
github.com/dcarbone/zadapters/zstdlog v1.0.0
|
||||||
github.com/dustin/go-humanize v1.0.1
|
github.com/dustin/go-humanize v1.0.1
|
||||||
github.com/ergochat/irc-go v0.4.0
|
github.com/ergochat/irc-go v0.4.0
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -102,6 +102,8 @@ github.com/autobrr/sse/v2 v2.0.0-20230520125637-530e06346d7d h1:9EGCYgeugAVWLBAt
|
||||||
github.com/autobrr/sse/v2 v2.0.0-20230520125637-530e06346d7d/go.mod h1:zCozZ9lp4DE340T2+wfMPL/eoQwLVIGDOCKCDEFwTQU=
|
github.com/autobrr/sse/v2 v2.0.0-20230520125637-530e06346d7d/go.mod h1:zCozZ9lp4DE340T2+wfMPL/eoQwLVIGDOCKCDEFwTQU=
|
||||||
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
|
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
|
||||||
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
|
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
|
||||||
|
github.com/avast/retry-go/v4 v4.5.0 h1:QoRAZZ90cj5oni2Lsgl2GW8mNTnUCnmpx/iKpwVisHg=
|
||||||
|
github.com/avast/retry-go/v4 v4.5.0/go.mod h1:7hLEXp0oku2Nir2xBAsg0PTphp9z71bN5Aq1fboC3+I=
|
||||||
github.com/benbjohnson/immutable v0.2.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI=
|
github.com/benbjohnson/immutable v0.2.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
|
|
|
@ -247,6 +247,10 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
|
||||||
"fe.webhook_data",
|
"fe.webhook_data",
|
||||||
"fe.webhook_headers",
|
"fe.webhook_headers",
|
||||||
"fe.webhook_expect_status",
|
"fe.webhook_expect_status",
|
||||||
|
"fe.webhook_retry_status",
|
||||||
|
"fe.webhook_retry_attempts",
|
||||||
|
"fe.webhook_retry_delay_seconds",
|
||||||
|
"fe.webhook_retry_max_jitter_seconds",
|
||||||
).
|
).
|
||||||
From("filter f").
|
From("filter f").
|
||||||
LeftJoin("filter_external fe ON f.id = fe.filter_id").
|
LeftJoin("filter_external fe ON f.id = fe.filter_id").
|
||||||
|
@ -276,8 +280,8 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
|
||||||
var delay, maxDownloads, logScore sql.NullInt32
|
var delay, maxDownloads, logScore sql.NullInt32
|
||||||
|
|
||||||
// filter external
|
// filter external
|
||||||
var extName, extType, extExecCmd, extExecArgs, extWebhookHost, extWebhookMethod, extWebhookHeaders, extWebhookData sql.NullString
|
var extName, extType, extExecCmd, extExecArgs, extWebhookHost, extWebhookMethod, extWebhookHeaders, extWebhookData, extWebhookRetryStatus sql.NullString
|
||||||
var extId, extIndex, extWebhookStatus, extExecStatus sql.NullInt32
|
var extId, extIndex, extWebhookStatus, extWebhookRetryAttempts, extWebhookDelaySeconds, extWebhookRetryJitterSeconds, extExecStatus sql.NullInt32
|
||||||
var extEnabled sql.NullBool
|
var extEnabled sql.NullBool
|
||||||
|
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
|
@ -354,6 +358,10 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
|
||||||
&extWebhookData,
|
&extWebhookData,
|
||||||
&extWebhookHeaders,
|
&extWebhookHeaders,
|
||||||
&extWebhookStatus,
|
&extWebhookStatus,
|
||||||
|
&extWebhookRetryStatus,
|
||||||
|
&extWebhookRetryAttempts,
|
||||||
|
&extWebhookDelaySeconds,
|
||||||
|
&extWebhookRetryJitterSeconds,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, errors.Wrap(err, "error scanning row")
|
return nil, errors.Wrap(err, "error scanning row")
|
||||||
}
|
}
|
||||||
|
@ -396,19 +404,23 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
|
||||||
|
|
||||||
if extId.Valid {
|
if extId.Valid {
|
||||||
external := domain.FilterExternal{
|
external := domain.FilterExternal{
|
||||||
ID: int(extId.Int32),
|
ID: int(extId.Int32),
|
||||||
Name: extName.String,
|
Name: extName.String,
|
||||||
Index: int(extIndex.Int32),
|
Index: int(extIndex.Int32),
|
||||||
Type: domain.FilterExternalType(extType.String),
|
Type: domain.FilterExternalType(extType.String),
|
||||||
Enabled: extEnabled.Bool,
|
Enabled: extEnabled.Bool,
|
||||||
ExecCmd: extExecCmd.String,
|
ExecCmd: extExecCmd.String,
|
||||||
ExecArgs: extExecArgs.String,
|
ExecArgs: extExecArgs.String,
|
||||||
ExecExpectStatus: int(extExecStatus.Int32),
|
ExecExpectStatus: int(extExecStatus.Int32),
|
||||||
WebhookHost: extWebhookHost.String,
|
WebhookHost: extWebhookHost.String,
|
||||||
WebhookMethod: extWebhookMethod.String,
|
WebhookMethod: extWebhookMethod.String,
|
||||||
WebhookData: extWebhookData.String,
|
WebhookData: extWebhookData.String,
|
||||||
WebhookHeaders: extWebhookHeaders.String,
|
WebhookHeaders: extWebhookHeaders.String,
|
||||||
WebhookExpectStatus: int(extWebhookStatus.Int32),
|
WebhookExpectStatus: int(extWebhookStatus.Int32),
|
||||||
|
WebhookRetryStatus: extWebhookRetryStatus.String,
|
||||||
|
WebhookRetryAttempts: int(extWebhookRetryAttempts.Int32),
|
||||||
|
WebhookRetryDelaySeconds: int(extWebhookDelaySeconds.Int32),
|
||||||
|
WebhookRetryMaxJitterSeconds: int(extWebhookRetryJitterSeconds.Int32),
|
||||||
}
|
}
|
||||||
externalMap[external.ID] = external
|
externalMap[external.ID] = external
|
||||||
}
|
}
|
||||||
|
@ -502,6 +514,10 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string
|
||||||
"fe.webhook_data",
|
"fe.webhook_data",
|
||||||
"fe.webhook_headers",
|
"fe.webhook_headers",
|
||||||
"fe.webhook_expect_status",
|
"fe.webhook_expect_status",
|
||||||
|
"fe.webhook_retry_status",
|
||||||
|
"fe.webhook_retry_attempts",
|
||||||
|
"fe.webhook_retry_delay_seconds",
|
||||||
|
"fe.webhook_retry_max_jitter_seconds",
|
||||||
"fe.filter_id",
|
"fe.filter_id",
|
||||||
).
|
).
|
||||||
From("filter f").
|
From("filter f").
|
||||||
|
@ -537,8 +553,8 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string
|
||||||
var delay, maxDownloads, logScore sql.NullInt32
|
var delay, maxDownloads, logScore sql.NullInt32
|
||||||
|
|
||||||
// filter external
|
// filter external
|
||||||
var extName, extType, extExecCmd, extExecArgs, extWebhookHost, extWebhookMethod, extWebhookHeaders, extWebhookData sql.NullString
|
var extName, extType, extExecCmd, extExecArgs, extWebhookHost, extWebhookMethod, extWebhookHeaders, extWebhookData, extWebhookRetryStatus sql.NullString
|
||||||
var extId, extIndex, extWebhookStatus, extExecStatus, extFilterId sql.NullInt32
|
var extId, extIndex, extWebhookStatus, extWebhookRetryAttempts, extWebhookDelaySeconds, extWebhookRetryJitterSeconds, extExecStatus, extFilterId sql.NullInt32
|
||||||
var extEnabled sql.NullBool
|
var extEnabled sql.NullBool
|
||||||
|
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
|
@ -615,6 +631,10 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string
|
||||||
&extWebhookData,
|
&extWebhookData,
|
||||||
&extWebhookHeaders,
|
&extWebhookHeaders,
|
||||||
&extWebhookStatus,
|
&extWebhookStatus,
|
||||||
|
&extWebhookRetryStatus,
|
||||||
|
&extWebhookRetryAttempts,
|
||||||
|
&extWebhookDelaySeconds,
|
||||||
|
&extWebhookRetryJitterSeconds,
|
||||||
&extFilterId,
|
&extFilterId,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, errors.Wrap(err, "error scanning row")
|
return nil, errors.Wrap(err, "error scanning row")
|
||||||
|
@ -658,20 +678,24 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string
|
||||||
|
|
||||||
if extId.Valid {
|
if extId.Valid {
|
||||||
external := domain.FilterExternal{
|
external := domain.FilterExternal{
|
||||||
ID: int(extId.Int32),
|
ID: int(extId.Int32),
|
||||||
Name: extName.String,
|
Name: extName.String,
|
||||||
Index: int(extIndex.Int32),
|
Index: int(extIndex.Int32),
|
||||||
Type: domain.FilterExternalType(extType.String),
|
Type: domain.FilterExternalType(extType.String),
|
||||||
Enabled: extEnabled.Bool,
|
Enabled: extEnabled.Bool,
|
||||||
ExecCmd: extExecCmd.String,
|
ExecCmd: extExecCmd.String,
|
||||||
ExecArgs: extExecArgs.String,
|
ExecArgs: extExecArgs.String,
|
||||||
ExecExpectStatus: int(extExecStatus.Int32),
|
ExecExpectStatus: int(extExecStatus.Int32),
|
||||||
WebhookHost: extWebhookHost.String,
|
WebhookHost: extWebhookHost.String,
|
||||||
WebhookMethod: extWebhookMethod.String,
|
WebhookMethod: extWebhookMethod.String,
|
||||||
WebhookData: extWebhookData.String,
|
WebhookData: extWebhookData.String,
|
||||||
WebhookHeaders: extWebhookHeaders.String,
|
WebhookHeaders: extWebhookHeaders.String,
|
||||||
WebhookExpectStatus: int(extWebhookStatus.Int32),
|
WebhookExpectStatus: int(extWebhookStatus.Int32),
|
||||||
FilterId: int(extFilterId.Int32),
|
WebhookRetryStatus: extWebhookRetryStatus.String,
|
||||||
|
WebhookRetryAttempts: int(extWebhookRetryAttempts.Int32),
|
||||||
|
WebhookRetryDelaySeconds: int(extWebhookDelaySeconds.Int32),
|
||||||
|
WebhookRetryMaxJitterSeconds: int(extWebhookRetryJitterSeconds.Int32),
|
||||||
|
FilterId: int(extFilterId.Int32),
|
||||||
}
|
}
|
||||||
externalMap[external.FilterId] = append(externalMap[external.FilterId], external)
|
externalMap[external.FilterId] = append(externalMap[external.FilterId], external)
|
||||||
}
|
}
|
||||||
|
@ -709,6 +733,10 @@ func (r *FilterRepo) FindExternalFiltersByID(ctx context.Context, filterId int)
|
||||||
"fe.webhook_data",
|
"fe.webhook_data",
|
||||||
"fe.webhook_headers",
|
"fe.webhook_headers",
|
||||||
"fe.webhook_expect_status",
|
"fe.webhook_expect_status",
|
||||||
|
"fe.webhook_retry_status",
|
||||||
|
"fe.webhook_retry_attempts",
|
||||||
|
"fe.webhook_retry_delay_seconds",
|
||||||
|
"fe.webhook_retry_max_jitter_seconds",
|
||||||
).
|
).
|
||||||
From("filter_external fe").
|
From("filter_external fe").
|
||||||
Where(sq.Eq{"fe.filter_id": filterId})
|
Where(sq.Eq{"fe.filter_id": filterId})
|
||||||
|
@ -732,8 +760,8 @@ func (r *FilterRepo) FindExternalFiltersByID(ctx context.Context, filterId int)
|
||||||
var external domain.FilterExternal
|
var external domain.FilterExternal
|
||||||
|
|
||||||
// filter external
|
// filter external
|
||||||
var extExecCmd, extExecArgs, extWebhookHost, extWebhookMethod, extWebhookHeaders, extWebhookData sql.NullString
|
var extExecCmd, extExecArgs, extWebhookHost, extWebhookMethod, extWebhookHeaders, extWebhookData, extWebhookRetryStatus sql.NullString
|
||||||
var extWebhookStatus, extExecStatus sql.NullInt32
|
var extWebhookStatus, extWebhookRetryAttempts, extWebhookDelaySeconds, extWebhookRetryJitterSeconds, extExecStatus sql.NullInt32
|
||||||
|
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
&external.ID,
|
&external.ID,
|
||||||
|
@ -749,6 +777,10 @@ func (r *FilterRepo) FindExternalFiltersByID(ctx context.Context, filterId int)
|
||||||
&extWebhookData,
|
&extWebhookData,
|
||||||
&extWebhookHeaders,
|
&extWebhookHeaders,
|
||||||
&extWebhookStatus,
|
&extWebhookStatus,
|
||||||
|
&extWebhookRetryStatus,
|
||||||
|
&extWebhookRetryAttempts,
|
||||||
|
&extWebhookDelaySeconds,
|
||||||
|
&extWebhookRetryJitterSeconds,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, errors.Wrap(err, "error scanning row")
|
return nil, errors.Wrap(err, "error scanning row")
|
||||||
}
|
}
|
||||||
|
@ -762,6 +794,10 @@ func (r *FilterRepo) FindExternalFiltersByID(ctx context.Context, filterId int)
|
||||||
external.WebhookData = extWebhookData.String
|
external.WebhookData = extWebhookData.String
|
||||||
external.WebhookHeaders = extWebhookHeaders.String
|
external.WebhookHeaders = extWebhookHeaders.String
|
||||||
external.WebhookExpectStatus = int(extWebhookStatus.Int32)
|
external.WebhookExpectStatus = int(extWebhookStatus.Int32)
|
||||||
|
external.WebhookRetryStatus = extWebhookRetryStatus.String
|
||||||
|
external.WebhookRetryAttempts = int(extWebhookRetryAttempts.Int32)
|
||||||
|
external.WebhookRetryDelaySeconds = int(extWebhookDelaySeconds.Int32)
|
||||||
|
external.WebhookRetryMaxJitterSeconds = int(extWebhookRetryJitterSeconds.Int32)
|
||||||
|
|
||||||
externalFilters = append(externalFilters, external)
|
externalFilters = append(externalFilters, external)
|
||||||
}
|
}
|
||||||
|
@ -1182,6 +1218,18 @@ func (r *FilterRepo) UpdatePartial(ctx context.Context, filter domain.FilterUpda
|
||||||
if filter.ExternalWebhookExpectStatus != nil {
|
if filter.ExternalWebhookExpectStatus != nil {
|
||||||
q = q.Set("external_webhook_expect_status", filter.ExternalWebhookExpectStatus)
|
q = q.Set("external_webhook_expect_status", filter.ExternalWebhookExpectStatus)
|
||||||
}
|
}
|
||||||
|
if filter.ExternalWebhookRetryStatus != nil {
|
||||||
|
q = q.Set("external_webhook_retry_status", filter.ExternalWebhookRetryStatus)
|
||||||
|
}
|
||||||
|
if filter.ExternalWebhookRetryAttempts != nil {
|
||||||
|
q = q.Set("external_webhook_retry_attempts", filter.ExternalWebhookRetryAttempts)
|
||||||
|
}
|
||||||
|
if filter.ExternalWebhookRetryDelaySeconds != nil {
|
||||||
|
q = q.Set("external_webhook_retry_delay_seconds", filter.ExternalWebhookRetryDelaySeconds)
|
||||||
|
}
|
||||||
|
if filter.ExternalWebhookRetryMaxJitterSeconds != nil {
|
||||||
|
q = q.Set("external_webhook_retry_max_jitter_seconds", filter.ExternalWebhookRetryMaxJitterSeconds)
|
||||||
|
}
|
||||||
|
|
||||||
q = q.Where(sq.Eq{"id": filter.ID})
|
q = q.Where(sq.Eq{"id": filter.ID})
|
||||||
|
|
||||||
|
@ -1462,6 +1510,10 @@ func (r *FilterRepo) StoreFilterExternal(ctx context.Context, filterID int, exte
|
||||||
"webhook_data",
|
"webhook_data",
|
||||||
"webhook_headers",
|
"webhook_headers",
|
||||||
"webhook_expect_status",
|
"webhook_expect_status",
|
||||||
|
"webhook_retry_status",
|
||||||
|
"webhook_retry_attempts",
|
||||||
|
"webhook_retry_delay_seconds",
|
||||||
|
"webhook_retry_max_jitter_seconds",
|
||||||
"filter_id",
|
"filter_id",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1479,6 +1531,10 @@ func (r *FilterRepo) StoreFilterExternal(ctx context.Context, filterID int, exte
|
||||||
toNullString(external.WebhookData),
|
toNullString(external.WebhookData),
|
||||||
toNullString(external.WebhookHeaders),
|
toNullString(external.WebhookHeaders),
|
||||||
toNullInt32(int32(external.WebhookExpectStatus)),
|
toNullInt32(int32(external.WebhookExpectStatus)),
|
||||||
|
toNullString(external.WebhookRetryStatus),
|
||||||
|
toNullInt32(int32(external.WebhookRetryAttempts)),
|
||||||
|
toNullInt32(int32(external.WebhookRetryDelaySeconds)),
|
||||||
|
toNullInt32(int32(external.WebhookRetryMaxJitterSeconds)),
|
||||||
filterID,
|
filterID,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,21 +133,25 @@ CREATE TABLE filter
|
||||||
|
|
||||||
CREATE TABLE filter_external
|
CREATE TABLE filter_external
|
||||||
(
|
(
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
idx INTEGER,
|
idx INTEGER,
|
||||||
type TEXT,
|
type TEXT,
|
||||||
enabled BOOLEAN,
|
enabled BOOLEAN,
|
||||||
exec_cmd TEXT,
|
exec_cmd TEXT,
|
||||||
exec_args TEXT,
|
exec_args TEXT,
|
||||||
exec_expect_status INTEGER,
|
exec_expect_status INTEGER,
|
||||||
webhook_host TEXT,
|
webhook_host TEXT,
|
||||||
webhook_method TEXT,
|
webhook_method TEXT,
|
||||||
webhook_data TEXT,
|
webhook_data TEXT,
|
||||||
webhook_headers TEXT,
|
webhook_headers TEXT,
|
||||||
webhook_expect_status INTEGER,
|
webhook_expect_status INTEGER,
|
||||||
filter_id INTEGER NOT NULL,
|
webhook_retry_status TEXT,
|
||||||
FOREIGN KEY (filter_id) REFERENCES filter(id) ON DELETE CASCADE
|
webhook_retry_attempts INTEGER,
|
||||||
|
webhook_retry_delay_seconds INTEGER,
|
||||||
|
webhook_retry_max_jitter_seconds INTEGER,
|
||||||
|
filter_id INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY (filter_id) REFERENCES filter(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE filter_indexer
|
CREATE TABLE filter_indexer
|
||||||
|
@ -797,5 +801,17 @@ CREATE INDEX feed_cache_feed_id_key_index
|
||||||
`,
|
`,
|
||||||
`ALTER TABLE action
|
`ALTER TABLE action
|
||||||
ADD COLUMN external_client_id INTEGER;
|
ADD COLUMN external_client_id INTEGER;
|
||||||
|
`,
|
||||||
|
`ALTER TABLE filter_external
|
||||||
|
ADD COLUMN external_webhook_retry_status TEXT;
|
||||||
|
|
||||||
|
ALTER TABLE filter_external
|
||||||
|
ADD COLUMN external_webhook_retry_attempts INTEGER;
|
||||||
|
|
||||||
|
ALTER TABLE filter_external
|
||||||
|
ADD COLUMN external_webhook_retry_delay_seconds INTEGER;
|
||||||
|
|
||||||
|
ALTER TABLE filter_external
|
||||||
|
ADD COLUMN external_webhook_retry_max_jitter_seconds INTEGER;
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,21 +133,25 @@ CREATE TABLE filter
|
||||||
|
|
||||||
CREATE TABLE filter_external
|
CREATE TABLE filter_external
|
||||||
(
|
(
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
idx INTEGER,
|
idx INTEGER,
|
||||||
type TEXT,
|
type TEXT,
|
||||||
enabled BOOLEAN,
|
enabled BOOLEAN,
|
||||||
exec_cmd TEXT,
|
exec_cmd TEXT,
|
||||||
exec_args TEXT,
|
exec_args TEXT,
|
||||||
exec_expect_status INTEGER,
|
exec_expect_status INTEGER,
|
||||||
webhook_host TEXT,
|
webhook_host TEXT,
|
||||||
webhook_method TEXT,
|
webhook_method TEXT,
|
||||||
webhook_data TEXT,
|
webhook_data TEXT,
|
||||||
webhook_headers TEXT,
|
webhook_headers TEXT,
|
||||||
webhook_expect_status INTEGER,
|
webhook_expect_status INTEGER,
|
||||||
filter_id INTEGER NOT NULL,
|
webhook_retry_status TEXT,
|
||||||
FOREIGN KEY (filter_id) REFERENCES filter(id) ON DELETE CASCADE
|
webhook_retry_attempts INTEGER,
|
||||||
|
webhook_retry_delay_seconds INTEGER,
|
||||||
|
webhook_retry_max_jitter_seconds INTEGER,
|
||||||
|
filter_id INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY (filter_id) REFERENCES filter(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE filter_indexer
|
CREATE TABLE filter_indexer
|
||||||
|
@ -1348,5 +1352,17 @@ CREATE INDEX feed_cache_feed_id_key_index
|
||||||
`,
|
`,
|
||||||
`ALTER TABLE action
|
`ALTER TABLE action
|
||||||
ADD COLUMN external_client_id INTEGER;
|
ADD COLUMN external_client_id INTEGER;
|
||||||
|
`,
|
||||||
|
`ALTER TABLE filter_external
|
||||||
|
ADD COLUMN external_webhook_retry_status TEXT;
|
||||||
|
|
||||||
|
ALTER TABLE filter_external
|
||||||
|
ADD COLUMN external_webhook_retry_attempts INTEGER;
|
||||||
|
|
||||||
|
ALTER TABLE filter_external
|
||||||
|
ADD COLUMN external_webhook_retry_delay_seconds INTEGER;
|
||||||
|
|
||||||
|
ALTER TABLE filter_external
|
||||||
|
ADD COLUMN external_webhook_retry_max_jitter_seconds INTEGER;
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,20 +138,24 @@ type Filter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type FilterExternal struct {
|
type FilterExternal struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Index int `json:"index"`
|
Index int `json:"index"`
|
||||||
Type FilterExternalType `json:"type"`
|
Type FilterExternalType `json:"type"`
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
ExecCmd string `json:"exec_cmd,omitempty"`
|
ExecCmd string `json:"exec_cmd,omitempty"`
|
||||||
ExecArgs string `json:"exec_args,omitempty"`
|
ExecArgs string `json:"exec_args,omitempty"`
|
||||||
ExecExpectStatus int `json:"exec_expect_status,omitempty"`
|
ExecExpectStatus int `json:"exec_expect_status,omitempty"`
|
||||||
WebhookHost string `json:"webhook_host,omitempty"`
|
WebhookHost string `json:"webhook_host,omitempty"`
|
||||||
WebhookMethod string `json:"webhook_method,omitempty"`
|
WebhookMethod string `json:"webhook_method,omitempty"`
|
||||||
WebhookData string `json:"webhook_data,omitempty"`
|
WebhookData string `json:"webhook_data,omitempty"`
|
||||||
WebhookHeaders string `json:"webhook_headers,omitempty"`
|
WebhookHeaders string `json:"webhook_headers,omitempty"`
|
||||||
WebhookExpectStatus int `json:"webhook_expect_status,omitempty"`
|
WebhookExpectStatus int `json:"webhook_expect_status,omitempty"`
|
||||||
FilterId int `json:"-"`
|
WebhookRetryStatus string `json:"webhook_retry_status,omitempty"`
|
||||||
|
WebhookRetryAttempts int `json:"webhook_retry_attempts,omitempty"`
|
||||||
|
WebhookRetryDelaySeconds int `json:"webhook_retry_delay_seconds,omitempty"`
|
||||||
|
WebhookRetryMaxJitterSeconds int `json:"webhook_retry_max_jitter_seconds,omitempty"`
|
||||||
|
FilterId int `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FilterExternalType string
|
type FilterExternalType string
|
||||||
|
@ -162,79 +166,83 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type FilterUpdate struct {
|
type FilterUpdate struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Name *string `json:"name,omitempty"`
|
Name *string `json:"name,omitempty"`
|
||||||
Enabled *bool `json:"enabled,omitempty"`
|
Enabled *bool `json:"enabled,omitempty"`
|
||||||
MinSize *string `json:"min_size,omitempty"`
|
MinSize *string `json:"min_size,omitempty"`
|
||||||
MaxSize *string `json:"max_size,omitempty"`
|
MaxSize *string `json:"max_size,omitempty"`
|
||||||
Delay *int `json:"delay,omitempty"`
|
Delay *int `json:"delay,omitempty"`
|
||||||
Priority *int32 `json:"priority,omitempty"`
|
Priority *int32 `json:"priority,omitempty"`
|
||||||
MaxDownloads *int `json:"max_downloads,omitempty"`
|
MaxDownloads *int `json:"max_downloads,omitempty"`
|
||||||
MaxDownloadsUnit *FilterMaxDownloadsUnit `json:"max_downloads_unit,omitempty"`
|
MaxDownloadsUnit *FilterMaxDownloadsUnit `json:"max_downloads_unit,omitempty"`
|
||||||
MatchReleases *string `json:"match_releases,omitempty"`
|
MatchReleases *string `json:"match_releases,omitempty"`
|
||||||
ExceptReleases *string `json:"except_releases,omitempty"`
|
ExceptReleases *string `json:"except_releases,omitempty"`
|
||||||
UseRegex *bool `json:"use_regex,omitempty"`
|
UseRegex *bool `json:"use_regex,omitempty"`
|
||||||
MatchReleaseGroups *string `json:"match_release_groups,omitempty"`
|
MatchReleaseGroups *string `json:"match_release_groups,omitempty"`
|
||||||
ExceptReleaseGroups *string `json:"except_release_groups,omitempty"`
|
ExceptReleaseGroups *string `json:"except_release_groups,omitempty"`
|
||||||
MatchReleaseTags *string `json:"match_release_tags,omitempty"`
|
MatchReleaseTags *string `json:"match_release_tags,omitempty"`
|
||||||
ExceptReleaseTags *string `json:"except_release_tags,omitempty"`
|
ExceptReleaseTags *string `json:"except_release_tags,omitempty"`
|
||||||
UseRegexReleaseTags *bool `json:"use_regex_release_tags,omitempty"`
|
UseRegexReleaseTags *bool `json:"use_regex_release_tags,omitempty"`
|
||||||
MatchDescription *string `json:"match_description,omitempty"`
|
MatchDescription *string `json:"match_description,omitempty"`
|
||||||
ExceptDescription *string `json:"except_description,omitempty"`
|
ExceptDescription *string `json:"except_description,omitempty"`
|
||||||
UseRegexDescription *bool `json:"use_regex_description,omitempty"`
|
UseRegexDescription *bool `json:"use_regex_description,omitempty"`
|
||||||
Scene *bool `json:"scene,omitempty"`
|
Scene *bool `json:"scene,omitempty"`
|
||||||
Origins *[]string `json:"origins,omitempty"`
|
Origins *[]string `json:"origins,omitempty"`
|
||||||
ExceptOrigins *[]string `json:"except_origins,omitempty"`
|
ExceptOrigins *[]string `json:"except_origins,omitempty"`
|
||||||
Bonus *[]string `json:"bonus,omitempty"`
|
Bonus *[]string `json:"bonus,omitempty"`
|
||||||
Freeleech *bool `json:"freeleech,omitempty"`
|
Freeleech *bool `json:"freeleech,omitempty"`
|
||||||
FreeleechPercent *string `json:"freeleech_percent,omitempty"`
|
FreeleechPercent *string `json:"freeleech_percent,omitempty"`
|
||||||
SmartEpisode *bool `json:"smart_episode,omitempty"`
|
SmartEpisode *bool `json:"smart_episode,omitempty"`
|
||||||
Shows *string `json:"shows,omitempty"`
|
Shows *string `json:"shows,omitempty"`
|
||||||
Seasons *string `json:"seasons,omitempty"`
|
Seasons *string `json:"seasons,omitempty"`
|
||||||
Episodes *string `json:"episodes,omitempty"`
|
Episodes *string `json:"episodes,omitempty"`
|
||||||
Resolutions *[]string `json:"resolutions,omitempty"` // SD, 480i, 480p, 576p, 720p, 810p, 1080i, 1080p.
|
Resolutions *[]string `json:"resolutions,omitempty"` // SD, 480i, 480p, 576p, 720p, 810p, 1080i, 1080p.
|
||||||
Codecs *[]string `json:"codecs,omitempty"` // XviD, DivX, x264, h.264 (or h264), mpeg2 (or mpeg-2), VC-1 (or VC1), WMV, Remux, h.264 Remux (or h264 Remux), VC-1 Remux (or VC1 Remux).
|
Codecs *[]string `json:"codecs,omitempty"` // XviD, DivX, x264, h.264 (or h264), mpeg2 (or mpeg-2), VC-1 (or VC1), WMV, Remux, h.264 Remux (or h264 Remux), VC-1 Remux (or VC1 Remux).
|
||||||
Sources *[]string `json:"sources,omitempty"` // DSR, PDTV, HDTV, HR.PDTV, HR.HDTV, DVDRip, DVDScr, BDr, BD5, BD9, BDRip, BRRip, DVDR, MDVDR, HDDVD, HDDVDRip, BluRay, WEB-DL, TVRip, CAM, R5, TELESYNC, TS, TELECINE, TC. TELESYNC and TS are synonyms (you don't need both). Same for TELECINE and TC
|
Sources *[]string `json:"sources,omitempty"` // DSR, PDTV, HDTV, HR.PDTV, HR.HDTV, DVDRip, DVDScr, BDr, BD5, BD9, BDRip, BRRip, DVDR, MDVDR, HDDVD, HDDVDRip, BluRay, WEB-DL, TVRip, CAM, R5, TELESYNC, TS, TELECINE, TC. TELESYNC and TS are synonyms (you don't need both). Same for TELECINE and TC
|
||||||
Containers *[]string `json:"containers,omitempty"`
|
Containers *[]string `json:"containers,omitempty"`
|
||||||
MatchHDR *[]string `json:"match_hdr,omitempty"`
|
MatchHDR *[]string `json:"match_hdr,omitempty"`
|
||||||
ExceptHDR *[]string `json:"except_hdr,omitempty"`
|
ExceptHDR *[]string `json:"except_hdr,omitempty"`
|
||||||
MatchOther *[]string `json:"match_other,omitempty"`
|
MatchOther *[]string `json:"match_other,omitempty"`
|
||||||
ExceptOther *[]string `json:"except_other,omitempty"`
|
ExceptOther *[]string `json:"except_other,omitempty"`
|
||||||
Years *string `json:"years,omitempty"`
|
Years *string `json:"years,omitempty"`
|
||||||
Artists *string `json:"artists,omitempty"`
|
Artists *string `json:"artists,omitempty"`
|
||||||
Albums *string `json:"albums,omitempty"`
|
Albums *string `json:"albums,omitempty"`
|
||||||
MatchReleaseTypes *[]string `json:"match_release_types,omitempty"` // Album,Single,EP
|
MatchReleaseTypes *[]string `json:"match_release_types,omitempty"` // Album,Single,EP
|
||||||
ExceptReleaseTypes *string `json:"except_release_types,omitempty"`
|
ExceptReleaseTypes *string `json:"except_release_types,omitempty"`
|
||||||
Formats *[]string `json:"formats,omitempty"` // MP3, FLAC, Ogg, AAC, AC3, DTS
|
Formats *[]string `json:"formats,omitempty"` // MP3, FLAC, Ogg, AAC, AC3, DTS
|
||||||
Quality *[]string `json:"quality,omitempty"` // 192, 320, APS (VBR), V2 (VBR), V1 (VBR), APX (VBR), V0 (VBR), q8.x (VBR), Lossless, 24bit Lossless, Other
|
Quality *[]string `json:"quality,omitempty"` // 192, 320, APS (VBR), V2 (VBR), V1 (VBR), APX (VBR), V0 (VBR), q8.x (VBR), Lossless, 24bit Lossless, Other
|
||||||
Media *[]string `json:"media,omitempty"` // CD, DVD, Vinyl, Soundboard, SACD, DAT, Cassette, WEB, Other
|
Media *[]string `json:"media,omitempty"` // CD, DVD, Vinyl, Soundboard, SACD, DAT, Cassette, WEB, Other
|
||||||
PerfectFlac *bool `json:"perfect_flac,omitempty"`
|
PerfectFlac *bool `json:"perfect_flac,omitempty"`
|
||||||
Cue *bool `json:"cue,omitempty"`
|
Cue *bool `json:"cue,omitempty"`
|
||||||
Log *bool `json:"log,omitempty"`
|
Log *bool `json:"log,omitempty"`
|
||||||
LogScore *int `json:"log_score,omitempty"`
|
LogScore *int `json:"log_score,omitempty"`
|
||||||
MatchCategories *string `json:"match_categories,omitempty"`
|
MatchCategories *string `json:"match_categories,omitempty"`
|
||||||
ExceptCategories *string `json:"except_categories,omitempty"`
|
ExceptCategories *string `json:"except_categories,omitempty"`
|
||||||
MatchUploaders *string `json:"match_uploaders,omitempty"`
|
MatchUploaders *string `json:"match_uploaders,omitempty"`
|
||||||
ExceptUploaders *string `json:"except_uploaders,omitempty"`
|
ExceptUploaders *string `json:"except_uploaders,omitempty"`
|
||||||
MatchLanguage *[]string `json:"match_language,omitempty"`
|
MatchLanguage *[]string `json:"match_language,omitempty"`
|
||||||
ExceptLanguage *[]string `json:"except_language,omitempty"`
|
ExceptLanguage *[]string `json:"except_language,omitempty"`
|
||||||
Tags *string `json:"tags,omitempty"`
|
Tags *string `json:"tags,omitempty"`
|
||||||
ExceptTags *string `json:"except_tags,omitempty"`
|
ExceptTags *string `json:"except_tags,omitempty"`
|
||||||
TagsAny *string `json:"tags_any,omitempty"`
|
TagsAny *string `json:"tags_any,omitempty"`
|
||||||
ExceptTagsAny *string `json:"except_tags_any,omitempty"`
|
ExceptTagsAny *string `json:"except_tags_any,omitempty"`
|
||||||
TagsMatchLogic *string `json:"tags_match_logic,omitempty"`
|
TagsMatchLogic *string `json:"tags_match_logic,omitempty"`
|
||||||
ExceptTagsMatchLogic *string `json:"except_tags_match_logic,omitempty"`
|
ExceptTagsMatchLogic *string `json:"except_tags_match_logic,omitempty"`
|
||||||
ExternalScriptEnabled *bool `json:"external_script_enabled,omitempty"`
|
ExternalScriptEnabled *bool `json:"external_script_enabled,omitempty"`
|
||||||
ExternalScriptCmd *string `json:"external_script_cmd,omitempty"`
|
ExternalScriptCmd *string `json:"external_script_cmd,omitempty"`
|
||||||
ExternalScriptArgs *string `json:"external_script_args,omitempty"`
|
ExternalScriptArgs *string `json:"external_script_args,omitempty"`
|
||||||
ExternalScriptExpectStatus *int `json:"external_script_expect_status,omitempty"`
|
ExternalScriptExpectStatus *int `json:"external_script_expect_status,omitempty"`
|
||||||
ExternalWebhookEnabled *bool `json:"external_webhook_enabled,omitempty"`
|
ExternalWebhookEnabled *bool `json:"external_webhook_enabled,omitempty"`
|
||||||
ExternalWebhookHost *string `json:"external_webhook_host,omitempty"`
|
ExternalWebhookHost *string `json:"external_webhook_host,omitempty"`
|
||||||
ExternalWebhookData *string `json:"external_webhook_data,omitempty"`
|
ExternalWebhookData *string `json:"external_webhook_data,omitempty"`
|
||||||
ExternalWebhookExpectStatus *int `json:"external_webhook_expect_status,omitempty"`
|
ExternalWebhookExpectStatus *int `json:"external_webhook_expect_status,omitempty"`
|
||||||
Actions []*Action `json:"actions,omitempty"`
|
ExternalWebhookRetryStatus *string `json:"external_webhook_retry_status,omitempty"`
|
||||||
External []FilterExternal `json:"external,omitempty"`
|
ExternalWebhookRetryAttempts *int `json:"external_webhook_retry_attempts,omitempty"`
|
||||||
Indexers []Indexer `json:"indexers,omitempty"`
|
ExternalWebhookRetryDelaySeconds *int `json:"external_webhook_retry_delay_seconds,omitempty"`
|
||||||
|
ExternalWebhookRetryMaxJitterSeconds *int `json:"external_webhook_retry_max_jitter_seconds,omitempty"`
|
||||||
|
Actions []*Action `json:"actions,omitempty"`
|
||||||
|
External []FilterExternal `json:"external,omitempty"`
|
||||||
|
Indexers []Indexer `json:"indexers,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Filter) CheckFilter(r *Release) ([]string, bool) {
|
func (f Filter) CheckFilter(r *Release) ([]string, bool) {
|
||||||
|
|
|
@ -13,14 +13,17 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/autobrr/autobrr/internal/domain"
|
"github.com/autobrr/autobrr/internal/domain"
|
||||||
"github.com/autobrr/autobrr/internal/indexer"
|
"github.com/autobrr/autobrr/internal/indexer"
|
||||||
"github.com/autobrr/autobrr/internal/logger"
|
"github.com/autobrr/autobrr/internal/logger"
|
||||||
|
"github.com/autobrr/autobrr/internal/utils"
|
||||||
"github.com/autobrr/autobrr/pkg/errors"
|
"github.com/autobrr/autobrr/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/avast/retry-go/v4"
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/mattn/go-shellwords"
|
"github.com/mattn/go-shellwords"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
|
@ -730,25 +733,53 @@ func (s *service) webhook(ctx context.Context, external domain.FilterExternal, r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var opts []retry.Option
|
||||||
|
|
||||||
|
if external.WebhookRetryAttempts > 0 {
|
||||||
|
option := retry.Attempts(uint(external.WebhookRetryAttempts))
|
||||||
|
opts = append(opts, option)
|
||||||
|
}
|
||||||
|
if external.WebhookRetryDelaySeconds > 0 {
|
||||||
|
option := retry.Delay(time.Duration(external.WebhookRetryDelaySeconds) * time.Second)
|
||||||
|
opts = append(opts, option)
|
||||||
|
}
|
||||||
|
if external.WebhookRetryMaxJitterSeconds > 0 {
|
||||||
|
option := retry.MaxJitter(time.Duration(external.WebhookRetryMaxJitterSeconds) * time.Second)
|
||||||
|
opts = append(opts, option)
|
||||||
|
}
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
res, err := client.Do(req)
|
statusCode, err := retry.DoWithData(
|
||||||
if err != nil {
|
func() (int, error) {
|
||||||
return 0, errors.Wrap(err, "could not make request for webhook")
|
res, err := client.Do(req)
|
||||||
}
|
if err != nil {
|
||||||
|
return 0, errors.Wrap(err, "could not make request for webhook")
|
||||||
|
}
|
||||||
|
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
|
|
||||||
body, err := io.ReadAll(res.Body)
|
body, err := io.ReadAll(res.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, errors.Wrap(err, "could not read request body")
|
return 0, errors.Wrap(err, "could not read request body")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(body) > 0 {
|
if len(body) > 0 {
|
||||||
s.log.Debug().Msgf("filter external webhook response status: %d body: %s", res.StatusCode, body)
|
s.log.Debug().Msgf("filter external webhook response status: %d body: %s", res.StatusCode, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if external.WebhookRetryStatus != "" {
|
||||||
|
retryStatusCodes := strings.Split(strings.ReplaceAll(external.WebhookRetryStatus, " ", ""), ",")
|
||||||
|
if utils.StrSliceContains(retryStatusCodes, strconv.Itoa(res.StatusCode)) {
|
||||||
|
return 0, errors.New("retrying webhook request, got status code: %d", res.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.StatusCode, nil
|
||||||
|
},
|
||||||
|
opts...)
|
||||||
|
|
||||||
s.log.Debug().Msgf("successfully ran external webhook filter to: (%s) payload: (%s) finished in %s", external.WebhookHost, dataArgs, time.Since(start))
|
s.log.Debug().Msgf("successfully ran external webhook filter to: (%s) payload: (%s) finished in %s", external.WebhookHost, dataArgs, time.Since(start))
|
||||||
|
|
||||||
return res.StatusCode, nil
|
return statusCode, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,6 +218,10 @@ const externalFilterSchema = z.object({
|
||||||
webhook_method: z.string().optional(),
|
webhook_method: z.string().optional(),
|
||||||
webhook_data: z.string().optional(),
|
webhook_data: z.string().optional(),
|
||||||
webhook_expect_status: z.number().optional(),
|
webhook_expect_status: z.number().optional(),
|
||||||
|
webhook_retry_status: z.string().optional(),
|
||||||
|
webhook_retry_attempts: z.number().optional(),
|
||||||
|
webhook_retry_delay_seconds: z.number().optional(),
|
||||||
|
webhook_retry_max_jitter_seconds: z.number().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const indexerSchema = z.object({
|
const indexerSchema = z.object({
|
||||||
|
|
|
@ -312,12 +312,31 @@ const TypeForm = ({ external, idx }: TypeFormProps) => {
|
||||||
rows={5}
|
rows={5}
|
||||||
placeholder={"Request data: { \"key\": \"value\" }"}
|
placeholder={"Request data: { \"key\": \"value\" }"}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NumberField
|
<NumberField
|
||||||
name={`external.${idx}.webhook_expect_status`}
|
name={`external.${idx}.webhook_expect_status`}
|
||||||
label="Expected http status"
|
label="Expected http status code"
|
||||||
placeholder="200"
|
placeholder="200"
|
||||||
/>
|
/>
|
||||||
|
<TextField
|
||||||
|
name={`external.${idx}.webhook_retry_status`}
|
||||||
|
label="Retry http status code(s)"
|
||||||
|
placeholder="Retry on status eg. 202, 204"
|
||||||
|
/>
|
||||||
|
<NumberField
|
||||||
|
name={`external.${idx}.webhook_retry_attempts`}
|
||||||
|
label="Maximum retry attempts"
|
||||||
|
placeholder="10"
|
||||||
|
/>
|
||||||
|
<NumberField
|
||||||
|
name={`external.${idx}.webhook_retry_delay_seconds`}
|
||||||
|
label="Retry delay in seconds"
|
||||||
|
placeholder="1"
|
||||||
|
/>
|
||||||
|
<NumberField
|
||||||
|
name={`external.${idx}.webhook_retry_max_jitter_seconds`}
|
||||||
|
label="Max jitter in seconds"
|
||||||
|
placeholder="1"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -292,6 +292,10 @@ const FilterItemDropdown = ({ filter, onToggle }: FilterItemDropdownProps) => {
|
||||||
external_webhook_host: any;
|
external_webhook_host: any;
|
||||||
external_webhook_data: any;
|
external_webhook_data: any;
|
||||||
external_webhook_expect_status: any;
|
external_webhook_expect_status: any;
|
||||||
|
external_webhook_retry_status: any;
|
||||||
|
external_webhook_retry_attempts: any;
|
||||||
|
external_webhook_retry_delay_seconds: any;
|
||||||
|
external_webhook_retry_max_jitter_seconds: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
const completeFilter = await APIClient.filters.getByID(filter.id) as Partial<CompleteFilterType>;
|
const completeFilter = await APIClient.filters.getByID(filter.id) as Partial<CompleteFilterType>;
|
||||||
|
@ -313,6 +317,10 @@ const FilterItemDropdown = ({ filter, onToggle }: FilterItemDropdownProps) => {
|
||||||
delete completeFilter.external_webhook_host;
|
delete completeFilter.external_webhook_host;
|
||||||
delete completeFilter.external_webhook_data;
|
delete completeFilter.external_webhook_data;
|
||||||
delete completeFilter.external_webhook_expect_status;
|
delete completeFilter.external_webhook_expect_status;
|
||||||
|
delete completeFilter.external_webhook_retry_status;
|
||||||
|
delete completeFilter.external_webhook_retry_attempts;
|
||||||
|
delete completeFilter.external_webhook_retry_delay_seconds;
|
||||||
|
delete completeFilter.external_webhook_retry_max_jitter_seconds;
|
||||||
|
|
||||||
// Remove properties with default values from the exported filter to minimize the size of the JSON string
|
// Remove properties with default values from the exported filter to minimize the size of the JSON string
|
||||||
["enabled", "priority", "smart_episode", "resolutions", "sources", "codecs", "containers", "tags_match_logic", "except_tags_match_logic"].forEach((key) => {
|
["enabled", "priority", "smart_episode", "resolutions", "sources", "codecs", "containers", "tags_match_logic", "except_tags_match_logic"].forEach((key) => {
|
||||||
|
|
4
web/src/types/Filter.d.ts
vendored
4
web/src/types/Filter.d.ts
vendored
|
@ -130,5 +130,9 @@ interface ExternalFilter {
|
||||||
webhook_data?: string,
|
webhook_data?: string,
|
||||||
webhook_headers?: string;
|
webhook_headers?: string;
|
||||||
webhook_expect_status?: number;
|
webhook_expect_status?: number;
|
||||||
|
webhook_retry_status?: string,
|
||||||
|
webhook_retry_attempts?: number;
|
||||||
|
webhook_retry_delay_seconds?: number;
|
||||||
|
webhook_retry_max_jitter_seconds?: number;
|
||||||
filter_id?: number;
|
filter_id?: number;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue