mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 08:49:13 +00:00
feat: add support for proxies to use with IRC and Indexers (#1421)
* feat: add support for proxies * fix(http): release handler * fix(migrations): define proxy early * fix(migrations): pg proxy * fix(proxy): list update delete * fix(proxy): remove log and imports * feat(irc): use proxy * feat(irc): tests * fix(web): update imports for ProxyForms.tsx * fix(database): migration * feat(proxy): test * feat(proxy): validate proxy type * feat(proxy): validate and test * feat(proxy): improve validate and test * feat(proxy): fix db schema * feat(proxy): add db tests * feat(proxy): handle http errors * fix(http): imports * feat(proxy): use proxy for indexer downloads * feat(proxy): indexerforms select proxy * feat(proxy): handle torrent download * feat(proxy): skip if disabled * feat(proxy): imports * feat(proxy): implement in Feeds * feat(proxy): update helper text indexer proxy * feat(proxy): add internal cache
This commit is contained in:
parent
472d327308
commit
bc0f4cc055
59 changed files with 2533 additions and 371 deletions
|
@ -36,6 +36,8 @@ func (r *FeedRepo) FindByID(ctx context.Context, id int) (*domain.Feed, error) {
|
|||
"i.identifier",
|
||||
"i.identifier_external",
|
||||
"i.name",
|
||||
"i.use_proxy",
|
||||
"i.proxy_id",
|
||||
"f.name",
|
||||
"f.type",
|
||||
"f.enabled",
|
||||
|
@ -66,8 +68,9 @@ func (r *FeedRepo) FindByID(ctx context.Context, id int) (*domain.Feed, error) {
|
|||
var f domain.Feed
|
||||
|
||||
var apiKey, cookie, settings sql.NullString
|
||||
var proxyID sql.NullInt64
|
||||
|
||||
if err := row.Scan(&f.ID, &f.Indexer.ID, &f.Indexer.Identifier, &f.Indexer.IdentifierExternal, &f.Indexer.Name, &f.Name, &f.Type, &f.Enabled, &f.URL, &f.Interval, &f.Timeout, &f.MaxAge, &apiKey, &cookie, &settings, &f.CreatedAt, &f.UpdatedAt); err != nil {
|
||||
if err := row.Scan(&f.ID, &f.Indexer.ID, &f.Indexer.Identifier, &f.Indexer.IdentifierExternal, &f.Indexer.Name, &f.UseProxy, &proxyID, &f.Name, &f.Type, &f.Enabled, &f.URL, &f.Interval, &f.Timeout, &f.MaxAge, &apiKey, &cookie, &settings, &f.CreatedAt, &f.UpdatedAt); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, domain.ErrRecordNotFound
|
||||
}
|
||||
|
@ -75,6 +78,7 @@ func (r *FeedRepo) FindByID(ctx context.Context, id int) (*domain.Feed, error) {
|
|||
return nil, errors.Wrap(err, "error scanning row")
|
||||
}
|
||||
|
||||
f.ProxyID = proxyID.Int64
|
||||
f.ApiKey = apiKey.String
|
||||
f.Cookie = cookie.String
|
||||
|
||||
|
@ -98,6 +102,8 @@ func (r *FeedRepo) FindByIndexerIdentifier(ctx context.Context, indexer string)
|
|||
"i.identifier",
|
||||
"i.identifier_external",
|
||||
"i.name",
|
||||
"i.use_proxy",
|
||||
"i.proxy_id",
|
||||
"f.name",
|
||||
"f.type",
|
||||
"f.enabled",
|
||||
|
@ -128,8 +134,9 @@ func (r *FeedRepo) FindByIndexerIdentifier(ctx context.Context, indexer string)
|
|||
var f domain.Feed
|
||||
|
||||
var apiKey, cookie, settings sql.NullString
|
||||
var proxyID sql.NullInt64
|
||||
|
||||
if err := row.Scan(&f.ID, &f.Indexer.ID, &f.Indexer.Identifier, &f.Indexer.IdentifierExternal, &f.Indexer.Name, &f.Name, &f.Type, &f.Enabled, &f.URL, &f.Interval, &f.Timeout, &f.MaxAge, &apiKey, &cookie, &settings, &f.CreatedAt, &f.UpdatedAt); err != nil {
|
||||
if err := row.Scan(&f.ID, &f.Indexer.ID, &f.Indexer.Identifier, &f.Indexer.IdentifierExternal, &f.Indexer.Name, &f.UseProxy, &proxyID, &f.Name, &f.Type, &f.Enabled, &f.URL, &f.Interval, &f.Timeout, &f.MaxAge, &apiKey, &cookie, &settings, &f.CreatedAt, &f.UpdatedAt); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, domain.ErrRecordNotFound
|
||||
}
|
||||
|
@ -137,6 +144,7 @@ func (r *FeedRepo) FindByIndexerIdentifier(ctx context.Context, indexer string)
|
|||
return nil, errors.Wrap(err, "error scanning row")
|
||||
}
|
||||
|
||||
f.ProxyID = proxyID.Int64
|
||||
f.ApiKey = apiKey.String
|
||||
f.Cookie = cookie.String
|
||||
|
||||
|
@ -158,6 +166,8 @@ func (r *FeedRepo) Find(ctx context.Context) ([]domain.Feed, error) {
|
|||
"i.identifier",
|
||||
"i.identifier_external",
|
||||
"i.name",
|
||||
"i.use_proxy",
|
||||
"i.proxy_id",
|
||||
"f.name",
|
||||
"f.type",
|
||||
"f.enabled",
|
||||
|
@ -196,10 +206,13 @@ func (r *FeedRepo) Find(ctx context.Context) ([]domain.Feed, error) {
|
|||
var apiKey, cookie, lastRunData, settings sql.NullString
|
||||
var lastRun sql.NullTime
|
||||
|
||||
if err := rows.Scan(&f.ID, &f.Indexer.ID, &f.Indexer.Identifier, &f.Indexer.IdentifierExternal, &f.Indexer.Name, &f.Name, &f.Type, &f.Enabled, &f.URL, &f.Interval, &f.Timeout, &f.MaxAge, &apiKey, &cookie, &lastRun, &lastRunData, &settings, &f.CreatedAt, &f.UpdatedAt); err != nil {
|
||||
var proxyID sql.NullInt64
|
||||
|
||||
if err := rows.Scan(&f.ID, &f.Indexer.ID, &f.Indexer.Identifier, &f.Indexer.IdentifierExternal, &f.Indexer.Name, &f.UseProxy, &proxyID, &f.Name, &f.Type, &f.Enabled, &f.URL, &f.Interval, &f.Timeout, &f.MaxAge, &apiKey, &cookie, &lastRun, &lastRunData, &settings, &f.CreatedAt, &f.UpdatedAt); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning row")
|
||||
}
|
||||
|
||||
f.ProxyID = proxyID.Int64
|
||||
f.LastRun = lastRun.Time
|
||||
f.LastRunData = lastRunData.String
|
||||
f.ApiKey = apiKey.String
|
||||
|
|
|
@ -36,8 +36,8 @@ func (r *IndexerRepo) Store(ctx context.Context, indexer domain.Indexer) (*domai
|
|||
}
|
||||
|
||||
queryBuilder := r.db.squirrel.
|
||||
Insert("indexer").Columns("enabled", "name", "identifier", "identifier_external", "implementation", "base_url", "settings").
|
||||
Values(indexer.Enabled, indexer.Name, indexer.Identifier, indexer.IdentifierExternal, indexer.Implementation, indexer.BaseURL, settings).
|
||||
Insert("indexer").Columns("enabled", "name", "identifier", "identifier_external", "implementation", "base_url", "use_proxy", "proxy_id", "settings").
|
||||
Values(indexer.Enabled, indexer.Name, indexer.Identifier, indexer.IdentifierExternal, indexer.Implementation, indexer.BaseURL, indexer.UseProxy, toNullInt64(indexer.ProxyID), settings).
|
||||
Suffix("RETURNING id").RunWith(r.db.handler)
|
||||
|
||||
// return values
|
||||
|
@ -61,6 +61,8 @@ func (r *IndexerRepo) Update(ctx context.Context, indexer domain.Indexer) (*doma
|
|||
Set("name", indexer.Name).
|
||||
Set("identifier_external", indexer.IdentifierExternal).
|
||||
Set("base_url", indexer.BaseURL).
|
||||
Set("use_proxy", indexer.UseProxy).
|
||||
Set("proxy_id", toNullInt64(indexer.ProxyID)).
|
||||
Set("settings", settings).
|
||||
Set("updated_at", time.Now().Format(time.RFC3339)).
|
||||
Where(sq.Eq{"id": indexer.ID})
|
||||
|
@ -70,16 +72,26 @@ func (r *IndexerRepo) Update(ctx context.Context, indexer domain.Indexer) (*doma
|
|||
return nil, errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
if _, err = r.db.handler.ExecContext(ctx, query, args...); err != nil {
|
||||
result, err := r.db.handler.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error rows affected")
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return nil, domain.ErrUpdateFailed
|
||||
}
|
||||
|
||||
return &indexer, nil
|
||||
}
|
||||
|
||||
func (r *IndexerRepo) List(ctx context.Context) ([]domain.Indexer, error) {
|
||||
queryBuilder := r.db.squirrel.
|
||||
Select("id", "enabled", "name", "identifier", "identifier_external", "implementation", "base_url", "settings").
|
||||
Select("id", "enabled", "name", "identifier", "identifier_external", "implementation", "base_url", "use_proxy", "proxy_id", "settings").
|
||||
From("indexer").
|
||||
OrderBy("name ASC")
|
||||
|
||||
|
@ -98,27 +110,29 @@ func (r *IndexerRepo) List(ctx context.Context) ([]domain.Indexer, error) {
|
|||
indexers := make([]domain.Indexer, 0)
|
||||
|
||||
for rows.Next() {
|
||||
var f domain.Indexer
|
||||
var i domain.Indexer
|
||||
|
||||
var identifierExternal, implementation, baseURL sql.Null[string]
|
||||
var proxyID sql.Null[int64]
|
||||
var settings string
|
||||
var settingsMap map[string]string
|
||||
|
||||
if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &f.Identifier, &identifierExternal, &implementation, &baseURL, &settings); err != nil {
|
||||
if err := rows.Scan(&i.ID, &i.Enabled, &i.Name, &i.Identifier, &identifierExternal, &implementation, &baseURL, &i.UseProxy, &proxyID, &settings); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning row")
|
||||
}
|
||||
|
||||
f.IdentifierExternal = identifierExternal.V
|
||||
f.Implementation = implementation.V
|
||||
f.BaseURL = baseURL.V
|
||||
i.IdentifierExternal = identifierExternal.V
|
||||
i.Implementation = implementation.V
|
||||
i.BaseURL = baseURL.V
|
||||
i.ProxyID = proxyID.V
|
||||
|
||||
if err = json.Unmarshal([]byte(settings), &settingsMap); err != nil {
|
||||
return nil, errors.Wrap(err, "error unmarshal settings")
|
||||
}
|
||||
|
||||
f.Settings = settingsMap
|
||||
i.Settings = settingsMap
|
||||
|
||||
indexers = append(indexers, f)
|
||||
indexers = append(indexers, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, errors.Wrap(err, "error rows")
|
||||
|
@ -129,7 +143,7 @@ func (r *IndexerRepo) List(ctx context.Context) ([]domain.Indexer, error) {
|
|||
|
||||
func (r *IndexerRepo) FindByID(ctx context.Context, id int) (*domain.Indexer, error) {
|
||||
queryBuilder := r.db.squirrel.
|
||||
Select("id", "enabled", "name", "identifier", "identifier_external", "implementation", "base_url", "settings").
|
||||
Select("id", "enabled", "name", "identifier", "identifier_external", "implementation", "base_url", "use_proxy", "proxy_id", "settings").
|
||||
From("indexer").
|
||||
Where(sq.Eq{"id": id})
|
||||
|
||||
|
@ -146,8 +160,9 @@ func (r *IndexerRepo) FindByID(ctx context.Context, id int) (*domain.Indexer, er
|
|||
var i domain.Indexer
|
||||
|
||||
var identifierExternal, implementation, baseURL, settings sql.Null[string]
|
||||
var proxyID sql.Null[int64]
|
||||
|
||||
if err := row.Scan(&i.ID, &i.Enabled, &i.Name, &i.Identifier, &identifierExternal, &implementation, &baseURL, &settings); err != nil {
|
||||
if err := row.Scan(&i.ID, &i.Enabled, &i.Name, &i.Identifier, &identifierExternal, &implementation, &baseURL, &i.UseProxy, &proxyID, &settings); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, domain.ErrRecordNotFound
|
||||
}
|
||||
|
@ -158,6 +173,7 @@ func (r *IndexerRepo) FindByID(ctx context.Context, id int) (*domain.Indexer, er
|
|||
i.IdentifierExternal = identifierExternal.V
|
||||
i.Implementation = implementation.V
|
||||
i.BaseURL = baseURL.V
|
||||
i.ProxyID = proxyID.V
|
||||
|
||||
var settingsMap map[string]string
|
||||
if err = json.Unmarshal([]byte(settings.V), &settingsMap); err != nil {
|
||||
|
@ -171,7 +187,7 @@ func (r *IndexerRepo) FindByID(ctx context.Context, id int) (*domain.Indexer, er
|
|||
|
||||
func (r *IndexerRepo) GetBy(ctx context.Context, req domain.GetIndexerRequest) (*domain.Indexer, error) {
|
||||
queryBuilder := r.db.squirrel.
|
||||
Select("id", "enabled", "name", "identifier", "identifier_external", "implementation", "base_url", "settings").
|
||||
Select("id", "enabled", "name", "identifier", "identifier_external", "implementation", "base_url", "use_proxy", "proxy_id", "settings").
|
||||
From("indexer")
|
||||
|
||||
if req.ID > 0 {
|
||||
|
@ -195,8 +211,9 @@ func (r *IndexerRepo) GetBy(ctx context.Context, req domain.GetIndexerRequest) (
|
|||
var i domain.Indexer
|
||||
|
||||
var identifierExternal, implementation, baseURL, settings sql.Null[string]
|
||||
var proxyID sql.Null[int64]
|
||||
|
||||
if err := row.Scan(&i.ID, &i.Enabled, &i.Name, &i.Identifier, &identifierExternal, &implementation, &baseURL, &settings); err != nil {
|
||||
if err := row.Scan(&i.ID, &i.Enabled, &i.Name, &i.Identifier, &identifierExternal, &implementation, &baseURL, &i.UseProxy, &proxyID, &settings); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, domain.ErrRecordNotFound
|
||||
}
|
||||
|
@ -207,6 +224,7 @@ func (r *IndexerRepo) GetBy(ctx context.Context, req domain.GetIndexerRequest) (
|
|||
i.IdentifierExternal = identifierExternal.V
|
||||
i.Implementation = implementation.V
|
||||
i.BaseURL = baseURL.V
|
||||
i.ProxyID = proxyID.V
|
||||
|
||||
var settingsMap map[string]string
|
||||
if err = json.Unmarshal([]byte(settings.V), &settingsMap); err != nil {
|
||||
|
@ -220,7 +238,7 @@ func (r *IndexerRepo) GetBy(ctx context.Context, req domain.GetIndexerRequest) (
|
|||
|
||||
func (r *IndexerRepo) FindByFilterID(ctx context.Context, id int) ([]domain.Indexer, error) {
|
||||
queryBuilder := r.db.squirrel.
|
||||
Select("id", "enabled", "name", "identifier", "identifier_external", "base_url", "settings").
|
||||
Select("id", "enabled", "name", "identifier", "identifier_external", "base_url", "use_proxy", "proxy_id", "settings").
|
||||
From("indexer").
|
||||
Join("filter_indexer ON indexer.id = filter_indexer.indexer_id").
|
||||
Where(sq.Eq{"filter_indexer.filter_id": id})
|
||||
|
@ -239,13 +257,14 @@ func (r *IndexerRepo) FindByFilterID(ctx context.Context, id int) ([]domain.Inde
|
|||
|
||||
indexers := make([]domain.Indexer, 0)
|
||||
for rows.Next() {
|
||||
var f domain.Indexer
|
||||
var i domain.Indexer
|
||||
|
||||
var settings string
|
||||
var settingsMap map[string]string
|
||||
var identifierExternal, baseURL sql.Null[string]
|
||||
var proxyID sql.Null[int64]
|
||||
|
||||
if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &f.Identifier, &identifierExternal, &baseURL, &settings); err != nil {
|
||||
if err := rows.Scan(&i.ID, &i.Enabled, &i.Name, &i.Identifier, &identifierExternal, &baseURL, &i.UseProxy, &proxyID, &settings); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning row")
|
||||
}
|
||||
|
||||
|
@ -253,11 +272,12 @@ func (r *IndexerRepo) FindByFilterID(ctx context.Context, id int) ([]domain.Inde
|
|||
return nil, errors.Wrap(err, "error unmarshal settings")
|
||||
}
|
||||
|
||||
f.IdentifierExternal = identifierExternal.V
|
||||
f.BaseURL = baseURL.V
|
||||
f.Settings = settingsMap
|
||||
i.IdentifierExternal = identifierExternal.V
|
||||
i.BaseURL = baseURL.V
|
||||
i.ProxyID = proxyID.V
|
||||
i.Settings = settingsMap
|
||||
|
||||
indexers = append(indexers, f)
|
||||
indexers = append(indexers, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, errors.Wrap(err, "error rows")
|
||||
|
@ -282,13 +302,13 @@ func (r *IndexerRepo) Delete(ctx context.Context, id int) error {
|
|||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
rows, err := result.RowsAffected()
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error rows affected")
|
||||
}
|
||||
|
||||
if rows != 1 {
|
||||
return errors.New("error deleting row")
|
||||
if rowsAffected == 0 {
|
||||
return domain.ErrRecordNotFound
|
||||
}
|
||||
|
||||
r.log.Debug().Str("method", "delete").Msgf("successfully deleted indexer with id %v", id)
|
||||
|
@ -297,8 +317,6 @@ func (r *IndexerRepo) Delete(ctx context.Context, id int) error {
|
|||
}
|
||||
|
||||
func (r *IndexerRepo) ToggleEnabled(ctx context.Context, indexerID int, enabled bool) error {
|
||||
var err error
|
||||
|
||||
queryBuilder := r.db.squirrel.
|
||||
Update("indexer").
|
||||
Set("enabled", enabled).
|
||||
|
@ -310,10 +328,19 @@ func (r *IndexerRepo) ToggleEnabled(ctx context.Context, indexerID int, enabled
|
|||
return errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
_, err = r.db.handler.ExecContext(ctx, query, args...)
|
||||
result, err := r.db.handler.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error rows affected")
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return domain.ErrUpdateFailed
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ func NewIrcRepo(log logger.Logger, db *DB) domain.IrcRepo {
|
|||
|
||||
func (r *IrcRepo) GetNetworkByID(ctx context.Context, id int64) (*domain.IrcNetwork, error) {
|
||||
queryBuilder := r.db.squirrel.
|
||||
Select("id", "enabled", "name", "server", "port", "tls", "pass", "nick", "auth_mechanism", "auth_account", "auth_password", "invite_command", "bouncer_addr", "use_bouncer", "bot_mode").
|
||||
Select("id", "enabled", "name", "server", "port", "tls", "pass", "nick", "auth_mechanism", "auth_account", "auth_password", "invite_command", "bouncer_addr", "use_bouncer", "bot_mode", "use_proxy", "proxy_id").
|
||||
From("irc_network").
|
||||
Where(sq.Eq{"id": id})
|
||||
|
||||
|
@ -42,26 +42,27 @@ func (r *IrcRepo) GetNetworkByID(ctx context.Context, id int64) (*domain.IrcNetw
|
|||
|
||||
var n domain.IrcNetwork
|
||||
|
||||
var pass, nick, inviteCmd, bouncerAddr sql.NullString
|
||||
var account, password sql.NullString
|
||||
var tls sql.NullBool
|
||||
var pass, nick, inviteCmd, bouncerAddr sql.Null[string]
|
||||
var account, password sql.Null[string]
|
||||
var tls sql.Null[bool]
|
||||
var proxyId sql.Null[int64]
|
||||
|
||||
row := r.db.handler.QueryRowContext(ctx, query, args...)
|
||||
if err := row.Scan(&n.ID, &n.Enabled, &n.Name, &n.Server, &n.Port, &tls, &pass, &nick, &n.Auth.Mechanism, &account, &password, &inviteCmd, &bouncerAddr, &n.UseBouncer, &n.BotMode); err != nil {
|
||||
if err := row.Scan(&n.ID, &n.Enabled, &n.Name, &n.Server, &n.Port, &tls, &pass, &nick, &n.Auth.Mechanism, &account, &password, &inviteCmd, &bouncerAddr, &n.UseBouncer, &n.BotMode, &n.UseProxy, &proxyId); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, domain.ErrRecordNotFound
|
||||
}
|
||||
|
||||
return nil, errors.Wrap(err, "error scanning row")
|
||||
}
|
||||
|
||||
n.TLS = tls.Bool
|
||||
n.Pass = pass.String
|
||||
n.Nick = nick.String
|
||||
n.InviteCommand = inviteCmd.String
|
||||
n.Auth.Account = account.String
|
||||
n.Auth.Password = password.String
|
||||
n.BouncerAddr = bouncerAddr.String
|
||||
n.TLS = tls.V
|
||||
n.Pass = pass.V
|
||||
n.Nick = nick.V
|
||||
n.InviteCommand = inviteCmd.V
|
||||
n.BouncerAddr = bouncerAddr.V
|
||||
n.Auth.Account = account.V
|
||||
n.Auth.Password = password.V
|
||||
n.ProxyId = proxyId.V
|
||||
|
||||
return &n, nil
|
||||
}
|
||||
|
@ -111,7 +112,7 @@ func (r *IrcRepo) DeleteNetwork(ctx context.Context, id int64) error {
|
|||
|
||||
func (r *IrcRepo) FindActiveNetworks(ctx context.Context) ([]domain.IrcNetwork, error) {
|
||||
queryBuilder := r.db.squirrel.
|
||||
Select("id", "enabled", "name", "server", "port", "tls", "pass", "nick", "auth_mechanism", "auth_account", "auth_password", "invite_command", "bouncer_addr", "use_bouncer", "bot_mode").
|
||||
Select("id", "enabled", "name", "server", "port", "tls", "pass", "nick", "auth_mechanism", "auth_account", "auth_password", "invite_command", "bouncer_addr", "use_bouncer", "bot_mode", "use_proxy", "proxy_id").
|
||||
From("irc_network").
|
||||
Where(sq.Eq{"enabled": true})
|
||||
|
||||
|
@ -131,22 +132,24 @@ func (r *IrcRepo) FindActiveNetworks(ctx context.Context) ([]domain.IrcNetwork,
|
|||
for rows.Next() {
|
||||
var net domain.IrcNetwork
|
||||
|
||||
var pass, nick, inviteCmd, bouncerAddr sql.NullString
|
||||
var account, password sql.NullString
|
||||
var tls sql.NullBool
|
||||
var pass, nick, inviteCmd, bouncerAddr sql.Null[string]
|
||||
var account, password sql.Null[string]
|
||||
var tls sql.Null[bool]
|
||||
var proxyId sql.Null[int64]
|
||||
|
||||
if err := rows.Scan(&net.ID, &net.Enabled, &net.Name, &net.Server, &net.Port, &tls, &pass, &nick, &net.Auth.Mechanism, &account, &password, &inviteCmd, &bouncerAddr, &net.UseBouncer, &net.BotMode); err != nil {
|
||||
if err := rows.Scan(&net.ID, &net.Enabled, &net.Name, &net.Server, &net.Port, &tls, &pass, &nick, &net.Auth.Mechanism, &account, &password, &inviteCmd, &bouncerAddr, &net.UseBouncer, &net.BotMode, &net.UseProxy, &proxyId); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning row")
|
||||
}
|
||||
|
||||
net.TLS = tls.Bool
|
||||
net.Pass = pass.String
|
||||
net.Nick = nick.String
|
||||
net.InviteCommand = inviteCmd.String
|
||||
net.BouncerAddr = bouncerAddr.String
|
||||
net.TLS = tls.V
|
||||
net.Pass = pass.V
|
||||
net.Nick = nick.V
|
||||
net.InviteCommand = inviteCmd.V
|
||||
net.BouncerAddr = bouncerAddr.V
|
||||
net.Auth.Account = account.V
|
||||
net.Auth.Password = password.V
|
||||
|
||||
net.Auth.Account = account.String
|
||||
net.Auth.Password = password.String
|
||||
net.ProxyId = proxyId.V
|
||||
|
||||
networks = append(networks, net)
|
||||
}
|
||||
|
@ -159,7 +162,7 @@ func (r *IrcRepo) FindActiveNetworks(ctx context.Context) ([]domain.IrcNetwork,
|
|||
|
||||
func (r *IrcRepo) ListNetworks(ctx context.Context) ([]domain.IrcNetwork, error) {
|
||||
queryBuilder := r.db.squirrel.
|
||||
Select("id", "enabled", "name", "server", "port", "tls", "pass", "nick", "auth_mechanism", "auth_account", "auth_password", "invite_command", "bouncer_addr", "use_bouncer", "bot_mode").
|
||||
Select("id", "enabled", "name", "server", "port", "tls", "pass", "nick", "auth_mechanism", "auth_account", "auth_password", "invite_command", "bouncer_addr", "use_bouncer", "bot_mode", "use_proxy", "proxy_id").
|
||||
From("irc_network").
|
||||
OrderBy("name ASC")
|
||||
|
||||
|
@ -179,22 +182,24 @@ func (r *IrcRepo) ListNetworks(ctx context.Context) ([]domain.IrcNetwork, error)
|
|||
for rows.Next() {
|
||||
var net domain.IrcNetwork
|
||||
|
||||
var pass, nick, inviteCmd, bouncerAddr sql.NullString
|
||||
var account, password sql.NullString
|
||||
var tls sql.NullBool
|
||||
var pass, nick, inviteCmd, bouncerAddr sql.Null[string]
|
||||
var account, password sql.Null[string]
|
||||
var tls sql.Null[bool]
|
||||
var proxyId sql.Null[int64]
|
||||
|
||||
if err := rows.Scan(&net.ID, &net.Enabled, &net.Name, &net.Server, &net.Port, &tls, &pass, &nick, &net.Auth.Mechanism, &account, &password, &inviteCmd, &bouncerAddr, &net.UseBouncer, &net.BotMode); err != nil {
|
||||
if err := rows.Scan(&net.ID, &net.Enabled, &net.Name, &net.Server, &net.Port, &tls, &pass, &nick, &net.Auth.Mechanism, &account, &password, &inviteCmd, &bouncerAddr, &net.UseBouncer, &net.BotMode, &net.UseProxy, &proxyId); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning row")
|
||||
}
|
||||
|
||||
net.TLS = tls.Bool
|
||||
net.Pass = pass.String
|
||||
net.Nick = nick.String
|
||||
net.InviteCommand = inviteCmd.String
|
||||
net.BouncerAddr = bouncerAddr.String
|
||||
net.TLS = tls.V
|
||||
net.Pass = pass.V
|
||||
net.Nick = nick.V
|
||||
net.InviteCommand = inviteCmd.V
|
||||
net.BouncerAddr = bouncerAddr.V
|
||||
net.Auth.Account = account.V
|
||||
net.Auth.Password = password.V
|
||||
|
||||
net.Auth.Account = account.String
|
||||
net.Auth.Password = password.String
|
||||
net.ProxyId = proxyId.V
|
||||
|
||||
networks = append(networks, net)
|
||||
}
|
||||
|
@ -225,13 +230,13 @@ func (r *IrcRepo) ListChannels(networkID int64) ([]domain.IrcChannel, error) {
|
|||
var channels []domain.IrcChannel
|
||||
for rows.Next() {
|
||||
var ch domain.IrcChannel
|
||||
var pass sql.NullString
|
||||
var pass sql.Null[string]
|
||||
|
||||
if err := rows.Scan(&ch.ID, &ch.Name, &ch.Enabled, &pass); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning row")
|
||||
}
|
||||
|
||||
ch.Password = pass.String
|
||||
ch.Password = pass.V
|
||||
|
||||
channels = append(channels, ch)
|
||||
}
|
||||
|
@ -244,7 +249,7 @@ func (r *IrcRepo) ListChannels(networkID int64) ([]domain.IrcChannel, error) {
|
|||
|
||||
func (r *IrcRepo) CheckExistingNetwork(ctx context.Context, network *domain.IrcNetwork) (*domain.IrcNetwork, error) {
|
||||
queryBuilder := r.db.squirrel.
|
||||
Select("id", "enabled", "name", "server", "port", "tls", "pass", "nick", "auth_mechanism", "auth_account", "auth_password", "invite_command", "bouncer_addr", "use_bouncer", "bot_mode").
|
||||
Select("id", "enabled", "name", "server", "port", "tls", "pass", "nick", "auth_mechanism", "auth_account", "auth_password", "invite_command", "bouncer_addr", "use_bouncer", "bot_mode", "use_proxy", "proxy_id").
|
||||
From("irc_network").
|
||||
Where(sq.Eq{"server": network.Server}).
|
||||
Where(sq.Eq{"port": network.Port}).
|
||||
|
@ -263,11 +268,12 @@ func (r *IrcRepo) CheckExistingNetwork(ctx context.Context, network *domain.IrcN
|
|||
|
||||
var net domain.IrcNetwork
|
||||
|
||||
var pass, nick, inviteCmd, bouncerAddr sql.NullString
|
||||
var account, password sql.NullString
|
||||
var tls sql.NullBool
|
||||
var pass, nick, inviteCmd, bouncerAddr sql.Null[string]
|
||||
var account, password sql.Null[string]
|
||||
var tls sql.Null[bool]
|
||||
var proxyId sql.Null[int64]
|
||||
|
||||
if err = row.Scan(&net.ID, &net.Enabled, &net.Name, &net.Server, &net.Port, &tls, &pass, &nick, &net.Auth.Mechanism, &account, &password, &inviteCmd, &bouncerAddr, &net.UseBouncer, &net.BotMode); err != nil {
|
||||
if err = row.Scan(&net.ID, &net.Enabled, &net.Name, &net.Server, &net.Port, &tls, &pass, &nick, &net.Auth.Mechanism, &account, &password, &inviteCmd, &bouncerAddr, &net.UseBouncer, &net.BotMode, &net.UseProxy, &proxyId); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
// no result is not an error in our case
|
||||
return nil, nil
|
||||
|
@ -276,29 +282,20 @@ func (r *IrcRepo) CheckExistingNetwork(ctx context.Context, network *domain.IrcN
|
|||
return nil, errors.Wrap(err, "error scanning row")
|
||||
}
|
||||
|
||||
net.TLS = tls.Bool
|
||||
net.Pass = pass.String
|
||||
net.Nick = nick.String
|
||||
net.InviteCommand = inviteCmd.String
|
||||
net.BouncerAddr = bouncerAddr.String
|
||||
net.Auth.Account = account.String
|
||||
net.Auth.Password = password.String
|
||||
net.TLS = tls.V
|
||||
net.Pass = pass.V
|
||||
net.Nick = nick.V
|
||||
net.InviteCommand = inviteCmd.V
|
||||
net.BouncerAddr = bouncerAddr.V
|
||||
net.Auth.Account = account.V
|
||||
net.Auth.Password = password.V
|
||||
|
||||
net.ProxyId = proxyId.V
|
||||
|
||||
return &net, nil
|
||||
}
|
||||
|
||||
func (r *IrcRepo) StoreNetwork(ctx context.Context, network *domain.IrcNetwork) error {
|
||||
netName := toNullString(network.Name)
|
||||
pass := toNullString(network.Pass)
|
||||
nick := toNullString(network.Nick)
|
||||
inviteCmd := toNullString(network.InviteCommand)
|
||||
bouncerAddr := toNullString(network.BouncerAddr)
|
||||
|
||||
account := toNullString(network.Auth.Account)
|
||||
password := toNullString(network.Auth.Password)
|
||||
|
||||
var retID int64
|
||||
|
||||
queryBuilder := r.db.squirrel.
|
||||
Insert("irc_network").
|
||||
Columns(
|
||||
|
@ -319,60 +316,49 @@ func (r *IrcRepo) StoreNetwork(ctx context.Context, network *domain.IrcNetwork)
|
|||
).
|
||||
Values(
|
||||
network.Enabled,
|
||||
netName,
|
||||
toNullString(network.Name),
|
||||
network.Server,
|
||||
network.Port,
|
||||
network.TLS,
|
||||
pass,
|
||||
nick,
|
||||
toNullString(network.Pass),
|
||||
toNullString(network.Nick),
|
||||
network.Auth.Mechanism,
|
||||
account,
|
||||
password,
|
||||
inviteCmd,
|
||||
bouncerAddr,
|
||||
toNullString(network.Auth.Account),
|
||||
toNullString(network.Auth.Password),
|
||||
toNullString(network.InviteCommand),
|
||||
toNullString(network.BouncerAddr),
|
||||
network.UseBouncer,
|
||||
network.BotMode,
|
||||
).
|
||||
Suffix("RETURNING id").
|
||||
RunWith(r.db.handler)
|
||||
|
||||
if err := queryBuilder.QueryRowContext(ctx).Scan(&retID); err != nil {
|
||||
if err := queryBuilder.QueryRowContext(ctx).Scan(&network.ID); err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
network.ID = retID
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *IrcRepo) UpdateNetwork(ctx context.Context, network *domain.IrcNetwork) error {
|
||||
netName := toNullString(network.Name)
|
||||
pass := toNullString(network.Pass)
|
||||
nick := toNullString(network.Nick)
|
||||
inviteCmd := toNullString(network.InviteCommand)
|
||||
bouncerAddr := toNullString(network.BouncerAddr)
|
||||
|
||||
account := toNullString(network.Auth.Account)
|
||||
password := toNullString(network.Auth.Password)
|
||||
|
||||
var err error
|
||||
|
||||
queryBuilder := r.db.squirrel.
|
||||
Update("irc_network").
|
||||
Set("enabled", network.Enabled).
|
||||
Set("name", netName).
|
||||
Set("name", toNullString(network.Name)).
|
||||
Set("server", network.Server).
|
||||
Set("port", network.Port).
|
||||
Set("tls", network.TLS).
|
||||
Set("pass", pass).
|
||||
Set("nick", nick).
|
||||
Set("pass", toNullString(network.Pass)).
|
||||
Set("nick", toNullString(network.Nick)).
|
||||
Set("auth_mechanism", network.Auth.Mechanism).
|
||||
Set("auth_account", account).
|
||||
Set("auth_password", password).
|
||||
Set("invite_command", inviteCmd).
|
||||
Set("bouncer_addr", bouncerAddr).
|
||||
Set("auth_account", toNullString(network.Auth.Account)).
|
||||
Set("auth_password", toNullString(network.Auth.Password)).
|
||||
Set("invite_command", toNullString(network.InviteCommand)).
|
||||
Set("bouncer_addr", toNullString(network.BouncerAddr)).
|
||||
Set("use_bouncer", network.UseBouncer).
|
||||
Set("bot_mode", network.BotMode).
|
||||
Set("use_proxy", network.UseProxy).
|
||||
Set("proxy_id", toNullInt64(network.ProxyId)).
|
||||
Set("updated_at", time.Now().Format(time.RFC3339)).
|
||||
Where(sq.Eq{"id": network.ID})
|
||||
|
||||
|
@ -414,7 +400,6 @@ func (r *IrcRepo) StoreNetworkChannels(ctx context.Context, networkID int64, cha
|
|||
|
||||
for _, channel := range channels {
|
||||
// values
|
||||
pass := toNullString(channel.Password)
|
||||
|
||||
channelQueryBuilder := r.db.squirrel.
|
||||
Insert("irc_channel").
|
||||
|
@ -429,21 +414,17 @@ func (r *IrcRepo) StoreNetworkChannels(ctx context.Context, networkID int64, cha
|
|||
channel.Enabled,
|
||||
true,
|
||||
channel.Name,
|
||||
pass,
|
||||
toNullString(channel.Password),
|
||||
networkID,
|
||||
).
|
||||
Suffix("RETURNING id").
|
||||
RunWith(tx)
|
||||
|
||||
// returning
|
||||
var retID int64
|
||||
|
||||
if err = channelQueryBuilder.QueryRowContext(ctx).Scan(&retID); err != nil {
|
||||
if err = channelQueryBuilder.QueryRowContext(ctx).Scan(&channel.ID); err != nil {
|
||||
return errors.Wrap(err, "error executing query storeNetworkChannels")
|
||||
}
|
||||
|
||||
channel.ID = retID
|
||||
|
||||
//channelQuery, channelArgs, err := channelQueryBuilder.ToSql()
|
||||
//if err != nil {
|
||||
// r.log.Error().Stack().Err(err).Msg("irc.storeNetworkChannels: error building query")
|
||||
|
@ -467,8 +448,6 @@ func (r *IrcRepo) StoreNetworkChannels(ctx context.Context, networkID int64, cha
|
|||
}
|
||||
|
||||
func (r *IrcRepo) StoreChannel(ctx context.Context, networkID int64, channel *domain.IrcChannel) error {
|
||||
pass := toNullString(channel.Password)
|
||||
|
||||
if channel.ID != 0 {
|
||||
// update record
|
||||
channelQueryBuilder := r.db.squirrel.
|
||||
|
@ -476,7 +455,7 @@ func (r *IrcRepo) StoreChannel(ctx context.Context, networkID int64, channel *do
|
|||
Set("enabled", channel.Enabled).
|
||||
Set("detached", channel.Detached).
|
||||
Set("name", channel.Name).
|
||||
Set("password", pass).
|
||||
Set("password", toNullString(channel.Password)).
|
||||
Where(sq.Eq{"id": channel.ID})
|
||||
|
||||
query, args, err := channelQueryBuilder.ToSql()
|
||||
|
@ -501,21 +480,17 @@ func (r *IrcRepo) StoreChannel(ctx context.Context, networkID int64, channel *do
|
|||
channel.Enabled,
|
||||
true,
|
||||
channel.Name,
|
||||
pass,
|
||||
toNullString(channel.Password),
|
||||
networkID,
|
||||
).
|
||||
Suffix("RETURNING id").
|
||||
RunWith(r.db.handler)
|
||||
|
||||
// returning
|
||||
var retID int64
|
||||
|
||||
if err := queryBuilder.QueryRowContext(ctx).Scan(&retID); err != nil {
|
||||
if err := queryBuilder.QueryRowContext(ctx).Scan(&channel.ID); err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
channel.ID = retID
|
||||
|
||||
//channelQuery, channelArgs, err := channelQueryBuilder.ToSql()
|
||||
//if err != nil {
|
||||
// r.log.Error().Stack().Err(err).Msg("irc.storeChannel: error building query")
|
||||
|
@ -536,15 +511,13 @@ func (r *IrcRepo) StoreChannel(ctx context.Context, networkID int64, channel *do
|
|||
}
|
||||
|
||||
func (r *IrcRepo) UpdateChannel(channel *domain.IrcChannel) error {
|
||||
pass := toNullString(channel.Password)
|
||||
|
||||
// update record
|
||||
channelQueryBuilder := r.db.squirrel.
|
||||
Update("irc_channel").
|
||||
Set("enabled", channel.Enabled).
|
||||
Set("detached", channel.Detached).
|
||||
Set("name", channel.Name).
|
||||
Set("password", pass).
|
||||
Set("password", toNullString(channel.Password)).
|
||||
Where(sq.Eq{"id": channel.ID})
|
||||
|
||||
query, args, err := channelQueryBuilder.ToSql()
|
||||
|
@ -561,7 +534,6 @@ func (r *IrcRepo) UpdateChannel(channel *domain.IrcChannel) error {
|
|||
}
|
||||
|
||||
func (r *IrcRepo) UpdateInviteCommand(networkID int64, invite string) error {
|
||||
|
||||
// update record
|
||||
channelQueryBuilder := r.db.squirrel.
|
||||
Update("irc_network").
|
||||
|
|
|
@ -14,6 +14,20 @@ CREATE TABLE users
|
|||
UNIQUE (username)
|
||||
);
|
||||
|
||||
CREATE TABLE proxy
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
enabled BOOLEAN,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
addr TEXT NOT NULL,
|
||||
auth_user TEXT,
|
||||
auth_pass TEXT,
|
||||
timeout INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE indexer
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
|
@ -24,8 +38,11 @@ CREATE TABLE indexer
|
|||
enabled BOOLEAN,
|
||||
name TEXT NOT NULL,
|
||||
settings TEXT,
|
||||
use_proxy BOOLEAN DEFAULT FALSE,
|
||||
proxy_id INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (proxy_id) REFERENCES proxy(id) ON DELETE SET NULL,
|
||||
UNIQUE (identifier)
|
||||
);
|
||||
|
||||
|
@ -51,8 +68,11 @@ CREATE TABLE irc_network
|
|||
bot_mode BOOLEAN DEFAULT FALSE,
|
||||
connected BOOLEAN,
|
||||
connected_since TIMESTAMP,
|
||||
use_proxy BOOLEAN DEFAULT FALSE,
|
||||
proxy_id INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (proxy_id) REFERENCES proxy(id) ON DELETE SET NULL,
|
||||
UNIQUE (server, port, nick)
|
||||
);
|
||||
|
||||
|
@ -900,5 +920,39 @@ ADD COLUMN months TEXT;
|
|||
|
||||
ALTER TABLE filter
|
||||
ADD COLUMN days TEXT;
|
||||
`,
|
||||
`CREATE TABLE proxy
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
enabled BOOLEAN,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
addr TEXT NOT NULL,
|
||||
auth_user TEXT,
|
||||
auth_pass TEXT,
|
||||
timeout INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
ALTER TABLE indexer
|
||||
ADD COLUMN proxy_id INTEGER;
|
||||
|
||||
ALTER TABLE indexer
|
||||
ADD COLUMN use_proxy BOOLEAN DEFAULT FALSE;
|
||||
|
||||
ALTER TABLE indexer
|
||||
ADD FOREIGN KEY (proxy_id) REFERENCES proxy
|
||||
ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE irc_network
|
||||
ADD COLUMN proxy_id INTEGER;
|
||||
|
||||
ALTER TABLE irc_network
|
||||
ADD COLUMN use_proxy BOOLEAN DEFAULT FALSE;
|
||||
|
||||
ALTER TABLE irc_network
|
||||
ADD FOREIGN KEY (proxy_id) REFERENCES proxy
|
||||
ON DELETE SET NULL;
|
||||
`,
|
||||
}
|
||||
|
|
265
internal/database/proxy.go
Normal file
265
internal/database/proxy.go
Normal file
|
@ -0,0 +1,265 @@
|
|||
// Copyright (c) 2021 - 2024, Ludvig Lundgren and the autobrr contributors.
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"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 ProxyRepo struct {
|
||||
log zerolog.Logger
|
||||
db *DB
|
||||
}
|
||||
|
||||
func NewProxyRepo(log logger.Logger, db *DB) domain.ProxyRepo {
|
||||
return &ProxyRepo{
|
||||
log: log.With().Str("repo", "proxy").Logger(),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ProxyRepo) Store(ctx context.Context, p *domain.Proxy) error {
|
||||
queryBuilder := r.db.squirrel.
|
||||
Insert("proxy").
|
||||
Columns(
|
||||
"enabled",
|
||||
"name",
|
||||
"type",
|
||||
"addr",
|
||||
"auth_user",
|
||||
"auth_pass",
|
||||
"timeout",
|
||||
).
|
||||
Values(
|
||||
p.Enabled,
|
||||
p.Name,
|
||||
p.Type,
|
||||
toNullString(p.Addr),
|
||||
toNullString(p.User),
|
||||
toNullString(p.Pass),
|
||||
p.Timeout,
|
||||
).
|
||||
Suffix("RETURNING id").
|
||||
RunWith(r.db.handler)
|
||||
|
||||
var retID int64
|
||||
err := queryBuilder.QueryRowContext(ctx).Scan(&retID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
p.ID = retID
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ProxyRepo) Update(ctx context.Context, p *domain.Proxy) error {
|
||||
queryBuilder := r.db.squirrel.
|
||||
Update("proxy").
|
||||
Set("enabled", p.Enabled).
|
||||
Set("name", p.Name).
|
||||
Set("type", p.Type).
|
||||
Set("addr", p.Addr).
|
||||
Set("auth_user", toNullString(p.User)).
|
||||
Set("auth_pass", toNullString(p.Pass)).
|
||||
Set("timeout", p.Timeout).
|
||||
Set("updated_at", time.Now().Format(time.RFC3339)).
|
||||
Where(sq.Eq{"id": p.ID})
|
||||
|
||||
query, args, err := queryBuilder.ToSql()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
// update record
|
||||
res, err := r.db.handler.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
rowsAffected, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error getting affected rows")
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return domain.ErrUpdateFailed
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *ProxyRepo) List(ctx context.Context) ([]domain.Proxy, error) {
|
||||
queryBuilder := r.db.squirrel.
|
||||
Select(
|
||||
"id",
|
||||
"enabled",
|
||||
"name",
|
||||
"type",
|
||||
"addr",
|
||||
"auth_user",
|
||||
"auth_pass",
|
||||
"timeout",
|
||||
).
|
||||
From("proxy").
|
||||
OrderBy("name ASC")
|
||||
|
||||
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()
|
||||
|
||||
proxies := make([]domain.Proxy, 0)
|
||||
for rows.Next() {
|
||||
var proxy domain.Proxy
|
||||
|
||||
var user, pass sql.NullString
|
||||
|
||||
if err := rows.Scan(&proxy.ID, &proxy.Enabled, &proxy.Name, &proxy.Type, &proxy.Addr, &user, &pass, &proxy.Timeout); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning row")
|
||||
}
|
||||
|
||||
proxy.User = user.String
|
||||
proxy.Pass = pass.String
|
||||
|
||||
proxies = append(proxies, proxy)
|
||||
}
|
||||
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error row")
|
||||
}
|
||||
|
||||
return proxies, nil
|
||||
}
|
||||
|
||||
func (r *ProxyRepo) Delete(ctx context.Context, id int64) error {
|
||||
tx, err := r.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error begin transaction")
|
||||
}
|
||||
|
||||
defer tx.Rollback()
|
||||
|
||||
queryBuilder := r.db.squirrel.
|
||||
Delete("proxy").
|
||||
Where(sq.Eq{"id": id})
|
||||
|
||||
query, args, err := queryBuilder.ToSql()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
res, err := tx.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
rowsAffected, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error getting affected rows")
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return domain.ErrDeleteFailed
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error commit deleting proxy")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ProxyRepo) FindByID(ctx context.Context, id int64) (*domain.Proxy, error) {
|
||||
queryBuilder := r.db.squirrel.
|
||||
Select(
|
||||
"id",
|
||||
"enabled",
|
||||
"name",
|
||||
"type",
|
||||
"addr",
|
||||
"auth_user",
|
||||
"auth_pass",
|
||||
"timeout",
|
||||
).
|
||||
From("proxy").
|
||||
OrderBy("name ASC").
|
||||
Where(sq.Eq{"id": 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 proxy domain.Proxy
|
||||
|
||||
var user, pass sql.NullString
|
||||
|
||||
err = row.Scan(&proxy.ID, &proxy.Enabled, &proxy.Name, &proxy.Type, &proxy.Addr, &user, &pass, &proxy.Timeout)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, domain.ErrRecordNotFound
|
||||
}
|
||||
|
||||
return nil, errors.Wrap(err, "error scanning row")
|
||||
}
|
||||
|
||||
proxy.User = user.String
|
||||
proxy.Pass = pass.String
|
||||
|
||||
return &proxy, nil
|
||||
}
|
||||
|
||||
func (r *ProxyRepo) ToggleEnabled(ctx context.Context, id int64, enabled bool) error {
|
||||
queryBuilder := r.db.squirrel.
|
||||
Update("proxy").
|
||||
Set("enabled", enabled).
|
||||
Set("updated_at", time.Now().Format(time.RFC3339)).
|
||||
Where(sq.Eq{"id": id})
|
||||
|
||||
query, args, err := queryBuilder.ToSql()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
// update record
|
||||
res, err := r.db.handler.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
rowsAffected, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error getting affected rows")
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return domain.ErrUpdateFailed
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
220
internal/database/proxy_test.go
Normal file
220
internal/database/proxy_test.go
Normal file
|
@ -0,0 +1,220 @@
|
|||
// Copyright (c) 2021 - 2024, Ludvig Lundgren and the autobrr contributors.
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
//go:build integration
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getMockProxy() *domain.Proxy {
|
||||
return &domain.Proxy{
|
||||
//ID: 0,
|
||||
Name: "Proxy",
|
||||
Enabled: true,
|
||||
Type: domain.ProxyTypeSocks5,
|
||||
Addr: "socks5://127.0.0.1:1080",
|
||||
User: "",
|
||||
Pass: "",
|
||||
Timeout: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyRepo_Store(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewProxyRepo(log, db)
|
||||
mockData := getMockProxy()
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
proxies, err := repo.List(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, proxies)
|
||||
assert.Equal(t, mockData.Name, proxies[0].Name)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), mockData.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Fails_With_Missing_or_empty_fields [%s]", dbType), func(t *testing.T) {
|
||||
mockData := domain.Proxy{}
|
||||
err := repo.Store(context.Background(), &mockData)
|
||||
assert.Error(t, err)
|
||||
|
||||
proxies, err := repo.List(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, proxies)
|
||||
//assert.Nil(t, proxies)
|
||||
|
||||
// Cleanup
|
||||
// No cleanup needed
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Fails_With_Context_Timeout [%s]", dbType), func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
|
||||
defer cancel()
|
||||
|
||||
err := repo.Store(ctx, mockData)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyRepo_Update(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewProxyRepo(log, db)
|
||||
mockData := getMockProxy()
|
||||
|
||||
t.Run(fmt.Sprintf("Update_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Update mockData
|
||||
updatedProxy := mockData
|
||||
updatedProxy.Name = "Updated Proxy"
|
||||
updatedProxy.Enabled = false
|
||||
|
||||
// Execute
|
||||
err = repo.Update(context.Background(), updatedProxy)
|
||||
assert.NoError(t, err)
|
||||
|
||||
proxies, err := repo.List(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, proxies)
|
||||
assert.Equal(t, "Updated Proxy", proxies[0].Name)
|
||||
assert.Equal(t, false, proxies[0].Enabled)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), proxies[0].ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Update_Fails_Invalid_ID [%s]", dbType), func(t *testing.T) {
|
||||
mockData.ID = -1
|
||||
err := repo.Update(context.Background(), mockData)
|
||||
assert.Error(t, err)
|
||||
assert.ErrorIs(t, err, domain.ErrUpdateFailed)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyRepo_Delete(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewProxyRepo(log, db)
|
||||
mockData := getMockProxy()
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
proxies, err := repo.List(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, proxies)
|
||||
assert.Equal(t, mockData.Name, proxies[0].Name)
|
||||
|
||||
// Execute
|
||||
err = repo.Delete(context.Background(), proxies[0].ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify that the proxy is deleted and return error ErrRecordNotFound
|
||||
proxy, err := repo.FindByID(context.Background(), proxies[0].ID)
|
||||
assert.ErrorIs(t, err, domain.ErrRecordNotFound)
|
||||
assert.Nil(t, proxy)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Fails_No_Record [%s]", dbType), func(t *testing.T) {
|
||||
err := repo.Delete(context.Background(), 9999)
|
||||
assert.Error(t, err)
|
||||
assert.ErrorIs(t, err, domain.ErrDeleteFailed)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyRepo_ToggleEnabled(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewProxyRepo(log, db)
|
||||
mockData := getMockProxy()
|
||||
|
||||
t.Run(fmt.Sprintf("ToggleEnabled_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
proxies, err := repo.List(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, proxies)
|
||||
assert.Equal(t, true, proxies[0].Enabled)
|
||||
|
||||
// Execute
|
||||
err = repo.ToggleEnabled(context.Background(), mockData.ID, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify that the proxy is updated
|
||||
proxy, err := repo.FindByID(context.Background(), proxies[0].ID)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, proxy)
|
||||
assert.Equal(t, false, proxy.Enabled)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), proxies[0].ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("ToggleEnabled_Fails_Invalid_ID [%s]", dbType), func(t *testing.T) {
|
||||
err := repo.ToggleEnabled(context.Background(), -1, false)
|
||||
assert.Error(t, err)
|
||||
assert.ErrorIs(t, err, domain.ErrUpdateFailed)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyRepo_FindByID(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewProxyRepo(log, db)
|
||||
mockData := getMockProxy()
|
||||
|
||||
t.Run(fmt.Sprintf("FindByID_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
proxies, err := repo.List(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, proxies)
|
||||
|
||||
// Execute
|
||||
proxy, err := repo.FindByID(context.Background(), proxies[0].ID)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, proxy)
|
||||
assert.Equal(t, proxies[0].ID, proxy.ID)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), proxies[0].ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("FindByID_Fails_Invalid_ID [%s]", dbType), func(t *testing.T) {
|
||||
// Test using an invalid ID
|
||||
proxy, err := repo.FindByID(context.Background(), -1)
|
||||
assert.ErrorIs(t, err, domain.ErrRecordNotFound) // should return an error
|
||||
assert.Nil(t, proxy) // should be nil
|
||||
})
|
||||
|
||||
}
|
||||
}
|
|
@ -14,6 +14,20 @@ CREATE TABLE users
|
|||
UNIQUE (username)
|
||||
);
|
||||
|
||||
CREATE TABLE proxy
|
||||
(
|
||||
id INTEGER PRIMARY KEY,
|
||||
enabled BOOLEAN,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
addr TEXT NOT NULL,
|
||||
auth_user TEXT,
|
||||
auth_pass TEXT,
|
||||
timeout INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE indexer
|
||||
(
|
||||
id INTEGER PRIMARY KEY,
|
||||
|
@ -24,8 +38,11 @@ CREATE TABLE indexer
|
|||
enabled BOOLEAN,
|
||||
name TEXT NOT NULL,
|
||||
settings TEXT,
|
||||
use_proxy BOOLEAN DEFAULT FALSE,
|
||||
proxy_id INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (proxy_id) REFERENCES proxy(id) ON DELETE SET NULL,
|
||||
UNIQUE (identifier)
|
||||
);
|
||||
|
||||
|
@ -51,8 +68,11 @@ CREATE TABLE irc_network
|
|||
bot_mode BOOLEAN DEFAULT FALSE,
|
||||
connected BOOLEAN,
|
||||
connected_since TIMESTAMP,
|
||||
use_proxy BOOLEAN DEFAULT FALSE,
|
||||
proxy_id INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (proxy_id) REFERENCES proxy(id) ON DELETE SET NULL,
|
||||
UNIQUE (server, port, nick)
|
||||
);
|
||||
|
||||
|
@ -1538,5 +1558,37 @@ ADD COLUMN months TEXT;
|
|||
|
||||
ALTER TABLE filter
|
||||
ADD COLUMN days TEXT;
|
||||
`,
|
||||
`CREATE TABLE proxy
|
||||
(
|
||||
id INTEGER PRIMARY KEY,
|
||||
enabled BOOLEAN,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
addr TEXT NOT NULL,
|
||||
auth_user TEXT,
|
||||
auth_pass TEXT,
|
||||
timeout INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
ALTER TABLE indexer
|
||||
ADD proxy_id INTEGER
|
||||
CONSTRAINT indexer_proxy_id_fk
|
||||
REFERENCES proxy(id)
|
||||
ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE indexer
|
||||
ADD use_proxy BOOLEAN DEFAULT FALSE;
|
||||
|
||||
ALTER TABLE irc_network
|
||||
ADD use_proxy BOOLEAN DEFAULT FALSE;
|
||||
|
||||
ALTER TABLE irc_network
|
||||
ADD proxy_id INTEGER
|
||||
CONSTRAINT irc_network_proxy_id_fk
|
||||
REFERENCES proxy(id)
|
||||
ON DELETE SET NULL;
|
||||
`,
|
||||
}
|
||||
|
|
|
@ -16,10 +16,10 @@ func dataSourceName(configPath string, name string) string {
|
|||
return name
|
||||
}
|
||||
|
||||
func toNullString(s string) sql.NullString {
|
||||
return sql.NullString{
|
||||
String: s,
|
||||
Valid: s != "",
|
||||
func toNullString(s string) sql.Null[string] {
|
||||
return sql.Null[string]{
|
||||
V: s,
|
||||
Valid: s != "",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue