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:
ze0s 2024-09-02 11:10:45 +02:00 committed by GitHub
parent 472d327308
commit bc0f4cc055
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
59 changed files with 2533 additions and 371 deletions

View file

@ -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").