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
346 lines
9.6 KiB
Go
346 lines
9.6 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"
|
|
"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 IndexerRepo struct {
|
|
log zerolog.Logger
|
|
db *DB
|
|
}
|
|
|
|
func NewIndexerRepo(log logger.Logger, db *DB) domain.IndexerRepo {
|
|
return &IndexerRepo{
|
|
log: log.With().Str("module", "database").Str("repo", "indexer").Logger(),
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
func (r *IndexerRepo) Store(ctx context.Context, indexer domain.Indexer) (*domain.Indexer, error) {
|
|
settings, err := json.Marshal(indexer.Settings)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error marshaling json data")
|
|
}
|
|
|
|
queryBuilder := r.db.squirrel.
|
|
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
|
|
err = queryBuilder.QueryRowContext(ctx).Scan(&indexer.ID)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error executing query")
|
|
}
|
|
|
|
return &indexer, nil
|
|
}
|
|
|
|
func (r *IndexerRepo) Update(ctx context.Context, indexer domain.Indexer) (*domain.Indexer, error) {
|
|
settings, err := json.Marshal(indexer.Settings)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error marshaling json data")
|
|
}
|
|
|
|
queryBuilder := r.db.squirrel.
|
|
Update("indexer").
|
|
Set("enabled", indexer.Enabled).
|
|
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})
|
|
|
|
query, args, err := queryBuilder.ToSql()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error building query")
|
|
}
|
|
|
|
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", "use_proxy", "proxy_id", "settings").
|
|
From("indexer").
|
|
OrderBy("name ASC")
|
|
|
|
query, _, err := queryBuilder.ToSql()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error building query")
|
|
}
|
|
|
|
rows, err := r.db.handler.QueryContext(ctx, query)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error executing query")
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
indexers := make([]domain.Indexer, 0)
|
|
|
|
for rows.Next() {
|
|
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(&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")
|
|
}
|
|
|
|
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")
|
|
}
|
|
|
|
i.Settings = settingsMap
|
|
|
|
indexers = append(indexers, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, errors.Wrap(err, "error rows")
|
|
}
|
|
|
|
return indexers, nil
|
|
}
|
|
|
|
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", "use_proxy", "proxy_id", "settings").
|
|
From("indexer").
|
|
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 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, &i.UseProxy, &proxyID, &settings); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, domain.ErrRecordNotFound
|
|
}
|
|
|
|
return nil, errors.Wrap(err, "error scanning row")
|
|
}
|
|
|
|
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 {
|
|
return nil, errors.Wrap(err, "error unmarshal settings")
|
|
}
|
|
|
|
i.Settings = settingsMap
|
|
|
|
return &i, nil
|
|
}
|
|
|
|
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", "use_proxy", "proxy_id", "settings").
|
|
From("indexer")
|
|
|
|
if req.ID > 0 {
|
|
queryBuilder = queryBuilder.Where(sq.Eq{"id": req.ID})
|
|
} else if req.Name != "" {
|
|
queryBuilder = queryBuilder.Where(sq.Eq{"name": req.Name})
|
|
} else if req.Identifier != "" {
|
|
queryBuilder = queryBuilder.Where(sq.Eq{"identifier": req.Identifier})
|
|
}
|
|
|
|
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 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, &i.UseProxy, &proxyID, &settings); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, domain.ErrRecordNotFound
|
|
}
|
|
|
|
return nil, errors.Wrap(err, "error scanning row")
|
|
}
|
|
|
|
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 {
|
|
return nil, errors.Wrap(err, "error unmarshal settings")
|
|
}
|
|
|
|
i.Settings = settingsMap
|
|
|
|
return &i, nil
|
|
}
|
|
|
|
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", "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})
|
|
|
|
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()
|
|
|
|
indexers := make([]domain.Indexer, 0)
|
|
for rows.Next() {
|
|
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(&i.ID, &i.Enabled, &i.Name, &i.Identifier, &identifierExternal, &baseURL, &i.UseProxy, &proxyID, &settings); err != nil {
|
|
return nil, errors.Wrap(err, "error scanning row")
|
|
}
|
|
|
|
if err = json.Unmarshal([]byte(settings), &settingsMap); err != nil {
|
|
return nil, errors.Wrap(err, "error unmarshal settings")
|
|
}
|
|
|
|
i.IdentifierExternal = identifierExternal.V
|
|
i.BaseURL = baseURL.V
|
|
i.ProxyID = proxyID.V
|
|
i.Settings = settingsMap
|
|
|
|
indexers = append(indexers, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, errors.Wrap(err, "error rows")
|
|
}
|
|
|
|
return indexers, nil
|
|
|
|
}
|
|
|
|
func (r *IndexerRepo) Delete(ctx context.Context, id int) error {
|
|
queryBuilder := r.db.squirrel.
|
|
Delete("indexer").
|
|
Where(sq.Eq{"id": id})
|
|
|
|
query, args, err := queryBuilder.ToSql()
|
|
if err != nil {
|
|
return errors.Wrap(err, "error building query")
|
|
}
|
|
|
|
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.ErrRecordNotFound
|
|
}
|
|
|
|
r.log.Debug().Str("method", "delete").Msgf("successfully deleted indexer with id %v", id)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *IndexerRepo) ToggleEnabled(ctx context.Context, indexerID int, enabled bool) error {
|
|
queryBuilder := r.db.squirrel.
|
|
Update("indexer").
|
|
Set("enabled", enabled).
|
|
Set("updated_at", sq.Expr("CURRENT_TIMESTAMP")).
|
|
Where(sq.Eq{"id": indexerID})
|
|
|
|
query, args, err := queryBuilder.ToSql()
|
|
if err != nil {
|
|
return errors.Wrap(err, "error building query")
|
|
}
|
|
|
|
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
|
|
}
|