mirror of
https://github.com/idanoo/autobrr
synced 2025-07-22 08:19:12 +00:00

* chore: update copyright year in license headers * Revert "chore: update copyright year in license headers" This reverts commit 3e58129c431b9a491089ce36b908f9bb6ba38ed3. * chore: update copyright year in license headers * fix: sort go imports * fix: add missing license headers
1026 lines
30 KiB
Go
1026 lines
30 KiB
Go
// Copyright (c) 2021 - 2025, Ludvig Lundgren and the autobrr contributors.
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
package database
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
|
|
"github.com/autobrr/autobrr/internal/domain"
|
|
"github.com/autobrr/autobrr/internal/logger"
|
|
"github.com/autobrr/autobrr/pkg/errors"
|
|
|
|
sq "github.com/Masterminds/squirrel"
|
|
"github.com/rs/zerolog"
|
|
)
|
|
|
|
type ActionRepo struct {
|
|
log zerolog.Logger
|
|
db *DB
|
|
clientRepo domain.DownloadClientRepo
|
|
}
|
|
|
|
func NewActionRepo(log logger.Logger, db *DB, clientRepo domain.DownloadClientRepo) domain.ActionRepo {
|
|
return &ActionRepo{
|
|
log: log.With().Str("repo", "action").Logger(),
|
|
db: db,
|
|
clientRepo: clientRepo,
|
|
}
|
|
}
|
|
|
|
func (r *ActionRepo) FindByFilterID(ctx context.Context, filterID int, active *bool, withClient bool) ([]*domain.Action, error) {
|
|
if withClient {
|
|
return r.findByFilterIDWithClient(ctx, filterID, active)
|
|
}
|
|
|
|
return r.findByFilterID(ctx, filterID, active)
|
|
}
|
|
|
|
func (r *ActionRepo) findByFilterID(ctx context.Context, filterID int, active *bool) ([]*domain.Action, error) {
|
|
queryBuilder := r.db.squirrel.
|
|
Select(
|
|
"a.id",
|
|
"a.name",
|
|
"a.type",
|
|
"a.enabled",
|
|
"a.exec_cmd",
|
|
"a.exec_args",
|
|
"a.watch_folder",
|
|
"a.category",
|
|
"a.tags",
|
|
"a.label",
|
|
"a.save_path",
|
|
"a.paused",
|
|
"a.ignore_rules",
|
|
"a.first_last_piece_prio",
|
|
"a.skip_hash_check",
|
|
"a.content_layout",
|
|
"a.priority",
|
|
"a.limit_download_speed",
|
|
"a.limit_upload_speed",
|
|
"a.limit_ratio",
|
|
"a.limit_seed_time",
|
|
"a.reannounce_skip",
|
|
"a.reannounce_delete",
|
|
"a.reannounce_interval",
|
|
"a.reannounce_max_attempts",
|
|
"a.webhook_host",
|
|
"a.webhook_type",
|
|
"a.webhook_method",
|
|
"a.webhook_data",
|
|
"a.external_client_id",
|
|
"a.external_client",
|
|
"a.client_id",
|
|
).
|
|
From("action a").
|
|
Where(sq.Eq{"a.filter_id": filterID})
|
|
|
|
if active != nil {
|
|
queryBuilder = queryBuilder.Where(sq.Eq{"enabled": *active})
|
|
}
|
|
|
|
query, args, err := queryBuilder.ToSql()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error building query")
|
|
}
|
|
|
|
rows, err := r.db.handler.QueryContext(ctx, query, args...)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error executing query")
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
actions := make([]*domain.Action, 0)
|
|
for rows.Next() {
|
|
var a domain.Action
|
|
|
|
var execCmd, execArgs, watchFolder, category, tags, label, savePath, contentLayout, priorityLayout, webhookHost, webhookType, webhookMethod, webhookData, externalClient sql.NullString
|
|
var limitUl, limitDl, limitSeedTime sql.NullInt64
|
|
var limitRatio sql.NullFloat64
|
|
|
|
var externalClientID, clientID sql.NullInt32
|
|
var paused, ignoreRules sql.NullBool
|
|
|
|
if err := rows.Scan(&a.ID, &a.Name, &a.Type, &a.Enabled, &execCmd, &execArgs, &watchFolder, &category, &tags, &label, &savePath, &paused, &ignoreRules, &a.FirstLastPiecePrio, &a.SkipHashCheck, &contentLayout, &priorityLayout, &limitDl, &limitUl, &limitRatio, &limitSeedTime, &a.ReAnnounceSkip, &a.ReAnnounceDelete, &a.ReAnnounceInterval, &a.ReAnnounceMaxAttempts, &webhookHost, &webhookType, &webhookMethod, &webhookData, &externalClientID, &externalClient, &clientID); err != nil {
|
|
return nil, errors.Wrap(err, "error scanning row")
|
|
}
|
|
|
|
a.ExecCmd = execCmd.String
|
|
a.ExecArgs = execArgs.String
|
|
a.WatchFolder = watchFolder.String
|
|
a.Category = category.String
|
|
a.Tags = tags.String
|
|
a.Label = label.String
|
|
a.SavePath = savePath.String
|
|
a.Paused = paused.Bool
|
|
a.IgnoreRules = ignoreRules.Bool
|
|
a.ContentLayout = domain.ActionContentLayout(contentLayout.String)
|
|
a.PriorityLayout = domain.PriorityLayout(priorityLayout.String)
|
|
|
|
a.LimitDownloadSpeed = limitDl.Int64
|
|
a.LimitUploadSpeed = limitUl.Int64
|
|
a.LimitRatio = limitRatio.Float64
|
|
a.LimitSeedTime = limitSeedTime.Int64
|
|
|
|
a.WebhookHost = webhookHost.String
|
|
a.WebhookType = webhookType.String
|
|
a.WebhookMethod = webhookMethod.String
|
|
a.WebhookData = webhookData.String
|
|
|
|
a.ExternalDownloadClientID = externalClientID.Int32
|
|
a.ExternalDownloadClient = externalClient.String
|
|
a.ClientID = clientID.Int32
|
|
|
|
actions = append(actions, &a)
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
return nil, errors.Wrap(err, "row error")
|
|
}
|
|
|
|
return actions, nil
|
|
}
|
|
|
|
func (r *ActionRepo) findByFilterIDWithClient(ctx context.Context, filterID int, active *bool) ([]*domain.Action, error) {
|
|
queryBuilder := r.db.squirrel.
|
|
Select(
|
|
"a.id",
|
|
"a.name",
|
|
"a.type",
|
|
"a.enabled",
|
|
"a.exec_cmd",
|
|
"a.exec_args",
|
|
"a.watch_folder",
|
|
"a.category",
|
|
"a.tags",
|
|
"a.label",
|
|
"a.save_path",
|
|
"a.paused",
|
|
"a.ignore_rules",
|
|
"a.first_last_piece_prio",
|
|
"a.skip_hash_check",
|
|
"a.content_layout",
|
|
"a.priority",
|
|
"a.limit_download_speed",
|
|
"a.limit_upload_speed",
|
|
"a.limit_ratio",
|
|
"a.limit_seed_time",
|
|
"a.reannounce_skip",
|
|
"a.reannounce_delete",
|
|
"a.reannounce_interval",
|
|
"a.reannounce_max_attempts",
|
|
"a.webhook_host",
|
|
"a.webhook_type",
|
|
"a.webhook_method",
|
|
"a.webhook_data",
|
|
"a.external_client_id",
|
|
"a.external_client",
|
|
"a.client_id",
|
|
"c.id",
|
|
"c.name",
|
|
"c.type",
|
|
"c.enabled",
|
|
"c.host",
|
|
"c.port",
|
|
"c.tls",
|
|
"c.tls_skip_verify",
|
|
"c.username",
|
|
"c.password",
|
|
"c.settings",
|
|
).
|
|
From("action a").
|
|
Join("client c ON a.client_id = c.id").
|
|
Where(sq.Eq{"a.filter_id": filterID})
|
|
|
|
if active != nil {
|
|
queryBuilder = queryBuilder.Where(sq.Eq{"enabled": *active})
|
|
}
|
|
|
|
query, args, err := queryBuilder.ToSql()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error building query")
|
|
}
|
|
|
|
rows, err := r.db.handler.QueryContext(ctx, query, args...)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error executing query")
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
actions := make([]*domain.Action, 0)
|
|
for rows.Next() {
|
|
var a domain.Action
|
|
var c domain.DownloadClient
|
|
|
|
var execCmd, execArgs, watchFolder, category, tags, label, savePath, contentLayout, priorityLayout, webhookHost, webhookType, webhookMethod, webhookData, externalClient sql.NullString
|
|
var limitUl, limitDl, limitSeedTime sql.NullInt64
|
|
var limitRatio sql.NullFloat64
|
|
|
|
var externalClientID, clientID sql.NullInt32
|
|
var paused, ignoreRules sql.NullBool
|
|
|
|
var clientClientId, clientPort sql.Null[int32]
|
|
var clientName, clientType, clientHost, clientUsername, clientPassword, clientSettings sql.Null[string]
|
|
var clientEnabled, clientTLS, clientTLSSkip sql.Null[bool]
|
|
|
|
if err := rows.Scan(&a.ID, &a.Name, &a.Type, &a.Enabled, &execCmd, &execArgs, &watchFolder, &category, &tags, &label, &savePath, &paused, &ignoreRules, &a.FirstLastPiecePrio, &a.SkipHashCheck, &contentLayout, &priorityLayout, &limitDl, &limitUl, &limitRatio, &limitSeedTime, &a.ReAnnounceSkip, &a.ReAnnounceDelete, &a.ReAnnounceInterval, &a.ReAnnounceMaxAttempts, &webhookHost, &webhookType, &webhookMethod, &webhookData, &externalClientID, &externalClient, &clientID, &clientClientId, &clientName, &clientType, &clientEnabled, &clientHost, &clientPort, &clientTLS, &clientTLSSkip, &clientUsername, &clientPassword, &clientSettings); err != nil {
|
|
return nil, errors.Wrap(err, "error scanning row")
|
|
}
|
|
|
|
a.ExecCmd = execCmd.String
|
|
a.ExecArgs = execArgs.String
|
|
a.WatchFolder = watchFolder.String
|
|
a.Category = category.String
|
|
a.Tags = tags.String
|
|
a.Label = label.String
|
|
a.SavePath = savePath.String
|
|
a.Paused = paused.Bool
|
|
a.IgnoreRules = ignoreRules.Bool
|
|
a.ContentLayout = domain.ActionContentLayout(contentLayout.String)
|
|
a.PriorityLayout = domain.PriorityLayout(priorityLayout.String)
|
|
|
|
a.LimitDownloadSpeed = limitDl.Int64
|
|
a.LimitUploadSpeed = limitUl.Int64
|
|
a.LimitRatio = limitRatio.Float64
|
|
a.LimitSeedTime = limitSeedTime.Int64
|
|
|
|
a.WebhookHost = webhookHost.String
|
|
a.WebhookType = webhookType.String
|
|
a.WebhookMethod = webhookMethod.String
|
|
a.WebhookData = webhookData.String
|
|
|
|
a.ExternalDownloadClientID = externalClientID.Int32
|
|
a.ExternalDownloadClient = externalClient.String
|
|
a.ClientID = clientID.Int32
|
|
|
|
c.ID = clientClientId.V
|
|
c.Name = clientName.V
|
|
c.Type = domain.DownloadClientType(clientType.V)
|
|
c.Enabled = clientEnabled.V
|
|
c.Host = clientHost.V
|
|
c.Port = int(clientPort.V)
|
|
c.TLS = clientTLS.V
|
|
c.TLSSkipVerify = clientTLSSkip.V
|
|
c.Username = clientUsername.V
|
|
c.Password = clientPassword.V
|
|
//c.Settings = clientSettings.String
|
|
|
|
if a.ClientID > 0 {
|
|
if clientSettings.Valid {
|
|
if err := json.Unmarshal([]byte(clientSettings.V), &c.Settings); err != nil {
|
|
return nil, errors.Wrap(err, "could not unmarshal download client settings: %v", clientSettings.V)
|
|
}
|
|
}
|
|
|
|
a.Client = &c
|
|
}
|
|
|
|
actions = append(actions, &a)
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
return nil, errors.Wrap(err, "row error")
|
|
}
|
|
|
|
return actions, nil
|
|
}
|
|
|
|
func (r *ActionRepo) FindByFilterIDTx(ctx context.Context, filterID int, active *bool) ([]*domain.Action, error) {
|
|
tx, err := r.db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadCommitted})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer tx.Rollback()
|
|
|
|
actions, err := r.findByFilterIDTx(ctx, tx, filterID, active)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, action := range actions {
|
|
if action.ClientID > 0 {
|
|
client, err := r.attachDownloadClient(ctx, tx, action.ClientID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if client != nil {
|
|
action.Client = client
|
|
}
|
|
}
|
|
}
|
|
|
|
return actions, nil
|
|
}
|
|
|
|
func (r *ActionRepo) findByFilterIDTx(ctx context.Context, tx *Tx, filterID int, active *bool) ([]*domain.Action, error) {
|
|
queryBuilder := r.db.squirrel.
|
|
Select(
|
|
"id",
|
|
"name",
|
|
"type",
|
|
"enabled",
|
|
"exec_cmd",
|
|
"exec_args",
|
|
"watch_folder",
|
|
"category",
|
|
"tags",
|
|
"label",
|
|
"save_path",
|
|
"paused",
|
|
"ignore_rules",
|
|
"first_last_piece_prio",
|
|
"skip_hash_check",
|
|
"content_layout",
|
|
"priority",
|
|
"limit_download_speed",
|
|
"limit_upload_speed",
|
|
"limit_ratio",
|
|
"limit_seed_time",
|
|
"reannounce_skip",
|
|
"reannounce_delete",
|
|
"reannounce_interval",
|
|
"reannounce_max_attempts",
|
|
"webhook_host",
|
|
"webhook_type",
|
|
"webhook_method",
|
|
"webhook_data",
|
|
"external_client_id",
|
|
"external_client",
|
|
"client_id",
|
|
).
|
|
From("action").
|
|
Where(sq.Eq{"filter_id": filterID})
|
|
|
|
if active != nil {
|
|
queryBuilder = queryBuilder.Where(sq.Eq{"enabled": *active})
|
|
}
|
|
|
|
query, args, err := queryBuilder.ToSql()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error building query")
|
|
}
|
|
|
|
rows, err := tx.QueryContext(ctx, query, args...)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error executing query")
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
actions := make([]*domain.Action, 0)
|
|
for rows.Next() {
|
|
var a domain.Action
|
|
|
|
var execCmd, execArgs, watchFolder, category, tags, label, savePath, contentLayout, priorityLayout, webhookHost, webhookType, webhookMethod, webhookData, externalClient sql.NullString
|
|
var limitUl, limitDl, limitSeedTime sql.NullInt64
|
|
var limitRatio sql.NullFloat64
|
|
|
|
var externalClientID, clientID sql.NullInt32
|
|
var paused, ignoreRules sql.NullBool
|
|
|
|
if err := rows.Scan(&a.ID, &a.Name, &a.Type, &a.Enabled, &execCmd, &execArgs, &watchFolder, &category, &tags, &label, &savePath, &paused, &ignoreRules, &a.FirstLastPiecePrio, &a.SkipHashCheck, &contentLayout, &priorityLayout, &limitDl, &limitUl, &limitRatio, &limitSeedTime, &a.ReAnnounceSkip, &a.ReAnnounceDelete, &a.ReAnnounceInterval, &a.ReAnnounceMaxAttempts, &webhookHost, &webhookType, &webhookMethod, &webhookData, &externalClientID, &externalClient, &clientID); err != nil {
|
|
return nil, errors.Wrap(err, "error scanning row")
|
|
}
|
|
|
|
a.ExecCmd = execCmd.String
|
|
a.ExecArgs = execArgs.String
|
|
a.WatchFolder = watchFolder.String
|
|
a.Category = category.String
|
|
a.Tags = tags.String
|
|
a.Label = label.String
|
|
a.SavePath = savePath.String
|
|
a.Paused = paused.Bool
|
|
a.IgnoreRules = ignoreRules.Bool
|
|
a.ContentLayout = domain.ActionContentLayout(contentLayout.String)
|
|
a.PriorityLayout = domain.PriorityLayout(priorityLayout.String)
|
|
|
|
a.LimitDownloadSpeed = limitDl.Int64
|
|
a.LimitUploadSpeed = limitUl.Int64
|
|
a.LimitRatio = limitRatio.Float64
|
|
a.LimitSeedTime = limitSeedTime.Int64
|
|
|
|
a.WebhookHost = webhookHost.String
|
|
a.WebhookType = webhookType.String
|
|
a.WebhookMethod = webhookMethod.String
|
|
a.WebhookData = webhookData.String
|
|
|
|
a.ExternalDownloadClientID = externalClientID.Int32
|
|
a.ExternalDownloadClient = externalClient.String
|
|
a.ClientID = clientID.Int32
|
|
|
|
actions = append(actions, &a)
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
return nil, errors.Wrap(err, "row error")
|
|
}
|
|
|
|
return actions, nil
|
|
}
|
|
|
|
func (r *ActionRepo) attachDownloadClient(ctx context.Context, tx *Tx, clientID int32) (*domain.DownloadClient, error) {
|
|
queryBuilder := r.db.squirrel.
|
|
Select(
|
|
"id",
|
|
"name",
|
|
"type",
|
|
"enabled",
|
|
"host",
|
|
"port",
|
|
"tls",
|
|
"tls_skip_verify",
|
|
"username",
|
|
"password",
|
|
"settings",
|
|
).
|
|
From("client").
|
|
Where(sq.Eq{"id": clientID})
|
|
|
|
query, args, err := queryBuilder.ToSql()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error building query")
|
|
}
|
|
|
|
row := tx.QueryRowContext(ctx, query, args...)
|
|
if err := row.Err(); err != nil {
|
|
return nil, errors.Wrap(err, "error executing query")
|
|
}
|
|
|
|
var client domain.DownloadClient
|
|
var settingsJsonStr string
|
|
|
|
if err := row.Scan(&client.ID, &client.Name, &client.Type, &client.Enabled, &client.Host, &client.Port, &client.TLS, &client.TLSSkipVerify, &client.Username, &client.Password, &settingsJsonStr); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
r.log.Warn().Msgf("no download client with id %d", clientID)
|
|
return nil, domain.ErrRecordNotFound
|
|
}
|
|
|
|
return nil, errors.Wrap(err, "error scanning row")
|
|
}
|
|
|
|
if settingsJsonStr != "" {
|
|
if err := json.Unmarshal([]byte(settingsJsonStr), &client.Settings); err != nil {
|
|
return nil, errors.Wrap(err, "could not unmarshal download client settings: %v", settingsJsonStr)
|
|
}
|
|
}
|
|
|
|
return &client, nil
|
|
}
|
|
|
|
func (r *ActionRepo) List(ctx context.Context) ([]domain.Action, error) {
|
|
queryBuilder := r.db.squirrel.
|
|
Select(
|
|
"id",
|
|
"name",
|
|
"type",
|
|
"enabled",
|
|
"exec_cmd",
|
|
"exec_args",
|
|
"watch_folder",
|
|
"category",
|
|
"tags",
|
|
"label",
|
|
"save_path",
|
|
"paused",
|
|
"ignore_rules",
|
|
"first_last_piece_prio",
|
|
"skip_hash_check",
|
|
"content_layout",
|
|
"priority",
|
|
"limit_download_speed",
|
|
"limit_upload_speed",
|
|
"limit_ratio",
|
|
"limit_seed_time",
|
|
"reannounce_skip",
|
|
"reannounce_delete",
|
|
"reannounce_interval",
|
|
"reannounce_max_attempts",
|
|
"webhook_host",
|
|
"webhook_type",
|
|
"webhook_method",
|
|
"webhook_data",
|
|
"external_client_id",
|
|
"external_client",
|
|
"client_id",
|
|
).
|
|
From("action")
|
|
|
|
query, args, err := queryBuilder.ToSql()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error building query")
|
|
}
|
|
|
|
rows, err := r.db.handler.QueryContext(ctx, query, args...)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error executing query")
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
actions := make([]domain.Action, 0)
|
|
for rows.Next() {
|
|
var a domain.Action
|
|
|
|
var execCmd, execArgs, watchFolder, category, tags, label, savePath, contentLayout, priorityLayout, webhookHost, webhookType, webhookMethod, webhookData, externalClient sql.NullString
|
|
var limitUl, limitDl, limitSeedTime sql.NullInt64
|
|
var limitRatio sql.NullFloat64
|
|
var externalClientID, clientID sql.NullInt32
|
|
var paused, ignoreRules sql.NullBool
|
|
|
|
if err := rows.Scan(&a.ID, &a.Name, &a.Type, &a.Enabled, &execCmd, &execArgs, &watchFolder, &category, &tags, &label, &savePath, &paused, &ignoreRules, &a.FirstLastPiecePrio, &a.SkipHashCheck, &contentLayout, &priorityLayout, &limitDl, &limitUl, &limitRatio, &limitSeedTime, &a.ReAnnounceSkip, &a.ReAnnounceDelete, &a.ReAnnounceInterval, &a.ReAnnounceMaxAttempts, &webhookHost, &webhookType, &webhookMethod, &webhookData, &externalClientID, &externalClient, &clientID); err != nil {
|
|
return nil, errors.Wrap(err, "error scanning row")
|
|
}
|
|
|
|
a.Category = category.String
|
|
a.Tags = tags.String
|
|
a.Label = label.String
|
|
a.SavePath = savePath.String
|
|
a.Paused = paused.Bool
|
|
a.IgnoreRules = ignoreRules.Bool
|
|
a.ContentLayout = domain.ActionContentLayout(contentLayout.String)
|
|
a.PriorityLayout = domain.PriorityLayout(priorityLayout.String)
|
|
|
|
a.LimitDownloadSpeed = limitDl.Int64
|
|
a.LimitUploadSpeed = limitUl.Int64
|
|
a.LimitRatio = limitRatio.Float64
|
|
a.LimitSeedTime = limitSeedTime.Int64
|
|
|
|
a.WebhookHost = webhookHost.String
|
|
a.WebhookType = webhookType.String
|
|
a.WebhookMethod = webhookMethod.String
|
|
a.WebhookData = webhookData.String
|
|
|
|
a.ExternalDownloadClientID = externalClientID.Int32
|
|
a.ExternalDownloadClient = externalClient.String
|
|
a.ClientID = clientID.Int32
|
|
|
|
actions = append(actions, a)
|
|
|
|
if err := rows.Err(); err != nil {
|
|
return nil, errors.Wrap(err, "rows error")
|
|
}
|
|
}
|
|
|
|
return actions, nil
|
|
}
|
|
|
|
func (r *ActionRepo) Get(ctx context.Context, req *domain.GetActionRequest) (*domain.Action, error) {
|
|
queryBuilder := r.db.squirrel.
|
|
Select(
|
|
"id",
|
|
"name",
|
|
"type",
|
|
"enabled",
|
|
"exec_cmd",
|
|
"exec_args",
|
|
"watch_folder",
|
|
"category",
|
|
"tags",
|
|
"label",
|
|
"save_path",
|
|
"paused",
|
|
"ignore_rules",
|
|
"first_last_piece_prio",
|
|
"skip_hash_check",
|
|
"content_layout",
|
|
"priority",
|
|
"limit_download_speed",
|
|
"limit_upload_speed",
|
|
"limit_ratio",
|
|
"limit_seed_time",
|
|
"reannounce_skip",
|
|
"reannounce_delete",
|
|
"reannounce_interval",
|
|
"reannounce_max_attempts",
|
|
"webhook_host",
|
|
"webhook_type",
|
|
"webhook_method",
|
|
"webhook_data",
|
|
"external_client_id",
|
|
"external_client",
|
|
"client_id",
|
|
"filter_id",
|
|
).
|
|
From("action").
|
|
Where(sq.Eq{"id": req.Id})
|
|
|
|
query, args, err := queryBuilder.ToSql()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error building query")
|
|
}
|
|
|
|
row := r.db.handler.QueryRowContext(ctx, query, args...)
|
|
if err := row.Err(); err != nil {
|
|
return nil, errors.Wrap(err, "error executing query")
|
|
}
|
|
|
|
var a domain.Action
|
|
|
|
var execCmd, execArgs, watchFolder, category, tags, label, savePath, contentLayout, priorityLayout, webhookHost, webhookType, webhookMethod, webhookData, externalClient sql.NullString
|
|
var limitUl, limitDl, limitSeedTime sql.NullInt64
|
|
var limitRatio sql.NullFloat64
|
|
var externalClientID, clientID, filterID sql.NullInt32
|
|
var paused, ignoreRules sql.NullBool
|
|
|
|
if err := row.Scan(&a.ID, &a.Name, &a.Type, &a.Enabled, &execCmd, &execArgs, &watchFolder, &category, &tags, &label, &savePath, &paused, &ignoreRules, &a.FirstLastPiecePrio, &a.SkipHashCheck, &contentLayout, &priorityLayout, &limitDl, &limitUl, &limitRatio, &limitSeedTime, &a.ReAnnounceSkip, &a.ReAnnounceDelete, &a.ReAnnounceInterval, &a.ReAnnounceMaxAttempts, &webhookHost, &webhookType, &webhookMethod, &webhookData, &externalClientID, &externalClient, &clientID, &filterID); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, domain.ErrRecordNotFound
|
|
}
|
|
|
|
return nil, errors.Wrap(err, "error scanning row")
|
|
}
|
|
|
|
a.ExecCmd = execCmd.String
|
|
a.ExecArgs = execArgs.String
|
|
a.WatchFolder = watchFolder.String
|
|
a.Category = category.String
|
|
a.Tags = tags.String
|
|
a.Label = label.String
|
|
a.SavePath = savePath.String
|
|
a.Paused = paused.Bool
|
|
a.IgnoreRules = ignoreRules.Bool
|
|
a.ContentLayout = domain.ActionContentLayout(contentLayout.String)
|
|
a.PriorityLayout = domain.PriorityLayout(priorityLayout.String)
|
|
|
|
a.LimitDownloadSpeed = limitDl.Int64
|
|
a.LimitUploadSpeed = limitUl.Int64
|
|
a.LimitRatio = limitRatio.Float64
|
|
a.LimitSeedTime = limitSeedTime.Int64
|
|
|
|
a.WebhookHost = webhookHost.String
|
|
a.WebhookType = webhookType.String
|
|
a.WebhookMethod = webhookMethod.String
|
|
a.WebhookData = webhookData.String
|
|
|
|
a.ExternalDownloadClientID = externalClientID.Int32
|
|
a.ExternalDownloadClient = externalClient.String
|
|
a.ClientID = clientID.Int32
|
|
a.FilterID = int(filterID.Int32)
|
|
|
|
return &a, nil
|
|
}
|
|
|
|
func (r *ActionRepo) Delete(ctx context.Context, req *domain.DeleteActionRequest) error {
|
|
queryBuilder := r.db.squirrel.
|
|
Delete("action").
|
|
Where(sq.Eq{"id": req.ActionId})
|
|
|
|
query, args, err := queryBuilder.ToSql()
|
|
if err != nil {
|
|
return errors.Wrap(err, "error building query")
|
|
}
|
|
|
|
_, err = r.db.handler.ExecContext(ctx, query, args...)
|
|
if err != nil {
|
|
return errors.Wrap(err, "error executing query")
|
|
}
|
|
|
|
r.log.Debug().Msgf("action.delete: %v", req.ActionId)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *ActionRepo) DeleteByFilterID(ctx context.Context, filterID int) error {
|
|
queryBuilder := r.db.squirrel.
|
|
Delete("action").
|
|
Where(sq.Eq{"filter_id": filterID})
|
|
|
|
query, args, err := queryBuilder.ToSql()
|
|
if err != nil {
|
|
return errors.Wrap(err, "error building query")
|
|
}
|
|
|
|
_, err = r.db.handler.ExecContext(ctx, query, args...)
|
|
if err != nil {
|
|
return errors.Wrap(err, "error executing query")
|
|
}
|
|
|
|
r.log.Debug().Msgf("action.deleteByFilterID: %v", filterID)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *ActionRepo) Store(ctx context.Context, action *domain.Action) error {
|
|
queryBuilder := r.db.squirrel.
|
|
Insert("action").
|
|
Columns(
|
|
"name",
|
|
"type",
|
|
"enabled",
|
|
"exec_cmd",
|
|
"exec_args",
|
|
"watch_folder",
|
|
"category",
|
|
"tags",
|
|
"label",
|
|
"save_path",
|
|
"paused",
|
|
"ignore_rules",
|
|
"first_last_piece_prio",
|
|
"skip_hash_check",
|
|
"content_layout",
|
|
"priority",
|
|
"limit_upload_speed",
|
|
"limit_download_speed",
|
|
"limit_ratio",
|
|
"limit_seed_time",
|
|
"reannounce_skip",
|
|
"reannounce_delete",
|
|
"reannounce_interval",
|
|
"reannounce_max_attempts",
|
|
"webhook_host",
|
|
"webhook_type",
|
|
"webhook_method",
|
|
"webhook_data",
|
|
"external_client_id",
|
|
"external_client",
|
|
"client_id",
|
|
"filter_id",
|
|
).
|
|
Values(
|
|
action.Name,
|
|
action.Type,
|
|
action.Enabled,
|
|
toNullString(action.ExecCmd),
|
|
toNullString(action.ExecArgs),
|
|
toNullString(action.WatchFolder),
|
|
toNullString(action.Category),
|
|
toNullString(action.Tags),
|
|
toNullString(action.Label),
|
|
toNullString(action.SavePath),
|
|
action.Paused,
|
|
action.IgnoreRules,
|
|
action.FirstLastPiecePrio,
|
|
action.SkipHashCheck,
|
|
toNullString(string(action.ContentLayout)),
|
|
toNullString(string(action.PriorityLayout)),
|
|
toNullInt64(action.LimitUploadSpeed),
|
|
toNullInt64(action.LimitDownloadSpeed),
|
|
toNullFloat64(action.LimitRatio),
|
|
toNullInt64(action.LimitSeedTime),
|
|
action.ReAnnounceSkip,
|
|
action.ReAnnounceDelete,
|
|
action.ReAnnounceInterval,
|
|
action.ReAnnounceMaxAttempts,
|
|
toNullString(action.WebhookHost),
|
|
toNullString(action.WebhookType),
|
|
toNullString(action.WebhookMethod),
|
|
toNullString(action.WebhookData),
|
|
toNullInt32(action.ExternalDownloadClientID),
|
|
toNullString(action.ExternalDownloadClient),
|
|
toNullInt32(action.ClientID),
|
|
toNullInt32(int32(action.FilterID)),
|
|
).
|
|
Suffix("RETURNING id").RunWith(r.db.handler)
|
|
|
|
// return values
|
|
var retID int64
|
|
|
|
if err := queryBuilder.QueryRowContext(ctx).Scan(&retID); err != nil {
|
|
return errors.Wrap(err, "error executing query")
|
|
}
|
|
|
|
action.ID = int(retID)
|
|
|
|
r.log.Debug().Msgf("action.store: added new %d", retID)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *ActionRepo) Update(ctx context.Context, action domain.Action) (*domain.Action, error) {
|
|
queryBuilder := r.db.squirrel.
|
|
Update("action").
|
|
Set("name", action.Name).
|
|
Set("type", action.Type).
|
|
Set("enabled", action.Enabled).
|
|
Set("exec_cmd", toNullString(action.ExecCmd)).
|
|
Set("exec_args", toNullString(action.ExecArgs)).
|
|
Set("watch_folder", toNullString(action.WatchFolder)).
|
|
Set("category", toNullString(action.Category)).
|
|
Set("tags", toNullString(action.Tags)).
|
|
Set("label", toNullString(action.Label)).
|
|
Set("save_path", toNullString(action.SavePath)).
|
|
Set("paused", action.Paused).
|
|
Set("ignore_rules", action.IgnoreRules).
|
|
Set("first_last_piece_prio", action.FirstLastPiecePrio).
|
|
Set("skip_hash_check", action.SkipHashCheck).
|
|
Set("content_layout", toNullString(string(action.ContentLayout))).
|
|
Set("priority", toNullString(string(action.PriorityLayout))).
|
|
Set("limit_upload_speed", toNullInt64(action.LimitUploadSpeed)).
|
|
Set("limit_download_speed", toNullInt64(action.LimitDownloadSpeed)).
|
|
Set("limit_ratio", toNullFloat64(action.LimitRatio)).
|
|
Set("limit_seed_time", toNullInt64(action.LimitSeedTime)).
|
|
Set("reannounce_skip", action.ReAnnounceSkip).
|
|
Set("reannounce_delete", action.ReAnnounceDelete).
|
|
Set("reannounce_interval", action.ReAnnounceInterval).
|
|
Set("reannounce_max_attempts", action.ReAnnounceMaxAttempts).
|
|
Set("webhook_host", toNullString(action.WebhookHost)).
|
|
Set("webhook_type", toNullString(action.WebhookType)).
|
|
Set("webhook_method", toNullString(action.WebhookMethod)).
|
|
Set("webhook_data", toNullString(action.WebhookData)).
|
|
Set("external_client_id", toNullInt32(action.ExternalDownloadClientID)).
|
|
Set("external_client", toNullString(action.ExternalDownloadClient)).
|
|
Set("client_id", toNullInt32(action.ClientID)).
|
|
Set("filter_id", toNullInt32(int32(action.FilterID))).
|
|
Where(sq.Eq{"id": action.ID})
|
|
|
|
query, args, err := queryBuilder.ToSql()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error building query")
|
|
}
|
|
|
|
if _, err := r.db.handler.ExecContext(ctx, query, args...); err != nil {
|
|
return nil, errors.Wrap(err, "error executing query")
|
|
}
|
|
|
|
r.log.Debug().Msgf("action.update: %v", action.ID)
|
|
|
|
return &action, nil
|
|
}
|
|
|
|
func (r *ActionRepo) StoreFilterActions(ctx context.Context, filterID int64, actions []*domain.Action) ([]*domain.Action, error) {
|
|
tx, err := r.db.handler.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error begin transaction")
|
|
}
|
|
|
|
defer tx.Rollback()
|
|
|
|
for _, action := range actions {
|
|
action := action
|
|
|
|
if action.ID > 0 {
|
|
queryBuilder := r.db.squirrel.
|
|
Update("action").
|
|
Set("name", action.Name).
|
|
Set("type", action.Type).
|
|
Set("enabled", action.Enabled).
|
|
Set("exec_cmd", toNullString(action.ExecCmd)).
|
|
Set("exec_args", toNullString(action.ExecArgs)).
|
|
Set("watch_folder", toNullString(action.WatchFolder)).
|
|
Set("category", toNullString(action.Category)).
|
|
Set("tags", toNullString(action.Tags)).
|
|
Set("label", toNullString(action.Label)).
|
|
Set("save_path", toNullString(action.SavePath)).
|
|
Set("paused", action.Paused).
|
|
Set("ignore_rules", action.IgnoreRules).
|
|
Set("first_last_piece_prio", action.FirstLastPiecePrio).
|
|
Set("skip_hash_check", action.SkipHashCheck).
|
|
Set("content_layout", toNullString(string(action.ContentLayout))).
|
|
Set("priority", toNullString(string(action.PriorityLayout))).
|
|
Set("limit_upload_speed", toNullInt64(action.LimitUploadSpeed)).
|
|
Set("limit_download_speed", toNullInt64(action.LimitDownloadSpeed)).
|
|
Set("limit_ratio", toNullFloat64(action.LimitRatio)).
|
|
Set("limit_seed_time", toNullInt64(action.LimitSeedTime)).
|
|
Set("reannounce_skip", action.ReAnnounceSkip).
|
|
Set("reannounce_delete", action.ReAnnounceDelete).
|
|
Set("reannounce_interval", action.ReAnnounceInterval).
|
|
Set("reannounce_max_attempts", action.ReAnnounceMaxAttempts).
|
|
Set("webhook_host", toNullString(action.WebhookHost)).
|
|
Set("webhook_type", toNullString(action.WebhookType)).
|
|
Set("webhook_method", toNullString(action.WebhookMethod)).
|
|
Set("webhook_data", toNullString(action.WebhookData)).
|
|
Set("external_client_id", toNullInt32(action.ExternalDownloadClientID)).
|
|
Set("external_client", toNullString(action.ExternalDownloadClient)).
|
|
Set("client_id", toNullInt32(action.ClientID)).
|
|
Set("filter_id", toNullInt64(filterID)).
|
|
Where(sq.Eq{"id": action.ID})
|
|
|
|
query, args, err := queryBuilder.ToSql()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error building query")
|
|
}
|
|
|
|
if _, err := tx.ExecContext(ctx, query, args...); err != nil {
|
|
return nil, errors.Wrap(err, "error executing query")
|
|
}
|
|
|
|
r.log.Trace().Msgf("action.StoreFilterActions: update %d", action.ID)
|
|
|
|
} else {
|
|
queryBuilder := r.db.squirrel.
|
|
Insert("action").
|
|
Columns(
|
|
"name",
|
|
"type",
|
|
"enabled",
|
|
"exec_cmd",
|
|
"exec_args",
|
|
"watch_folder",
|
|
"category",
|
|
"tags",
|
|
"label",
|
|
"save_path",
|
|
"paused",
|
|
"ignore_rules",
|
|
"first_last_piece_prio",
|
|
"skip_hash_check",
|
|
"content_layout",
|
|
"priority",
|
|
"limit_upload_speed",
|
|
"limit_download_speed",
|
|
"limit_ratio",
|
|
"limit_seed_time",
|
|
"reannounce_skip",
|
|
"reannounce_delete",
|
|
"reannounce_interval",
|
|
"reannounce_max_attempts",
|
|
"webhook_host",
|
|
"webhook_type",
|
|
"webhook_method",
|
|
"webhook_data",
|
|
"external_client_id",
|
|
"external_client",
|
|
"client_id",
|
|
"filter_id",
|
|
).
|
|
Values(
|
|
action.Name,
|
|
action.Type,
|
|
action.Enabled,
|
|
toNullString(action.ExecCmd),
|
|
toNullString(action.ExecArgs),
|
|
toNullString(action.WatchFolder),
|
|
toNullString(action.Category),
|
|
toNullString(action.Tags),
|
|
toNullString(action.Label),
|
|
toNullString(action.SavePath),
|
|
action.Paused,
|
|
action.IgnoreRules,
|
|
action.FirstLastPiecePrio,
|
|
action.SkipHashCheck,
|
|
toNullString(string(action.ContentLayout)),
|
|
toNullString(string(action.PriorityLayout)),
|
|
toNullInt64(action.LimitUploadSpeed),
|
|
toNullInt64(action.LimitDownloadSpeed),
|
|
toNullFloat64(action.LimitRatio),
|
|
toNullInt64(action.LimitSeedTime),
|
|
action.ReAnnounceSkip,
|
|
action.ReAnnounceDelete,
|
|
action.ReAnnounceInterval,
|
|
action.ReAnnounceMaxAttempts,
|
|
toNullString(action.WebhookHost),
|
|
toNullString(action.WebhookType),
|
|
toNullString(action.WebhookMethod),
|
|
toNullString(action.WebhookData),
|
|
toNullInt32(action.ExternalDownloadClientID),
|
|
toNullString(action.ExternalDownloadClient),
|
|
toNullInt32(action.ClientID),
|
|
toNullInt64(filterID),
|
|
).
|
|
Suffix("RETURNING id").RunWith(tx)
|
|
|
|
// return values
|
|
var retID int
|
|
|
|
if err := queryBuilder.QueryRowContext(ctx).Scan(&retID); err != nil {
|
|
return nil, errors.Wrap(err, "error executing query")
|
|
}
|
|
|
|
action.ID = retID
|
|
|
|
r.log.Trace().Msgf("action.StoreFilterActions: store %d", action.ID)
|
|
}
|
|
|
|
r.log.Debug().Msgf("action.StoreFilterActions: store '%s' type: '%v' on filter: %d", action.Name, action.Type, filterID)
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
return nil, errors.Wrap(err, "error updating filter actions")
|
|
}
|
|
|
|
return actions, nil
|
|
}
|
|
|
|
func (r *ActionRepo) ToggleEnabled(actionID int) error {
|
|
queryBuilder := r.db.squirrel.
|
|
Update("action").
|
|
Set("enabled", sq.Expr("NOT enabled")).
|
|
Where(sq.Eq{"id": actionID})
|
|
|
|
query, args, err := queryBuilder.ToSql()
|
|
if err != nil {
|
|
return errors.Wrap(err, "error building query")
|
|
}
|
|
|
|
result, err := r.db.handler.Exec(query, args...)
|
|
if err != nil {
|
|
return errors.Wrap(err, "error executing query")
|
|
}
|
|
|
|
if rowsAffected, err := result.RowsAffected(); err != nil {
|
|
return errors.Wrap(err, "error getting rows affected")
|
|
} else if rowsAffected == 0 {
|
|
return domain.ErrRecordNotFound
|
|
}
|
|
|
|
r.log.Debug().Msgf("action.toggleEnabled: %v", actionID)
|
|
|
|
return nil
|
|
}
|