mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 16:59:12 +00:00
feat(filters): improve list view with filtering (#465)
This commit is contained in:
parent
63d4c21e54
commit
f5faf066a9
7 changed files with 576 additions and 94 deletions
|
@ -3,6 +3,8 @@ package database
|
|||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
|
@ -26,6 +28,97 @@ func NewFilterRepo(log logger.Logger, db *DB) domain.FilterRepo {
|
|||
}
|
||||
}
|
||||
|
||||
func (r *FilterRepo) Find(ctx context.Context, params domain.FilterQueryParams) ([]domain.Filter, error) {
|
||||
tx, err := r.db.BeginTx(ctx, &sql.TxOptions{})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error begin transaction")
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
filters, err := r.find(ctx, tx, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = tx.Commit(); err != nil {
|
||||
return nil, errors.Wrap(err, "error commit transaction find releases")
|
||||
}
|
||||
|
||||
return filters, nil
|
||||
}
|
||||
|
||||
func (r *FilterRepo) find(ctx context.Context, tx *Tx, params domain.FilterQueryParams) ([]domain.Filter, error) {
|
||||
|
||||
actionCountQuery := r.db.squirrel.
|
||||
Select("COUNT(*)").
|
||||
From("action a").
|
||||
Where("a.filter_id = f.id")
|
||||
|
||||
queryBuilder := r.db.squirrel.
|
||||
Select(
|
||||
"f.id",
|
||||
"f.enabled",
|
||||
"f.name",
|
||||
"f.priority",
|
||||
"f.created_at",
|
||||
"f.updated_at",
|
||||
).
|
||||
Distinct().
|
||||
Column(sq.Alias(actionCountQuery, "action_count")).
|
||||
LeftJoin("filter_indexer fi ON f.id = fi.filter_id").
|
||||
LeftJoin("indexer i ON i.id = fi.indexer_id").
|
||||
From("filter f")
|
||||
|
||||
if params.Search != "" {
|
||||
queryBuilder = queryBuilder.Where("f.name LIKE ?", fmt.Sprint("%", params.Search, "%"))
|
||||
}
|
||||
|
||||
if len(params.Sort) > 0 {
|
||||
for field, order := range params.Sort {
|
||||
queryBuilder = queryBuilder.OrderBy(fmt.Sprintf("f.%v %v", field, strings.ToUpper(order)))
|
||||
}
|
||||
} else {
|
||||
queryBuilder = queryBuilder.OrderBy("f.name ASC")
|
||||
}
|
||||
|
||||
if params.Filters.Indexers != nil {
|
||||
filter := sq.And{}
|
||||
for _, v := range params.Filters.Indexers {
|
||||
filter = append(filter, sq.Eq{"i.identifier": v})
|
||||
}
|
||||
|
||||
queryBuilder = queryBuilder.Where(filter)
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
var filters []domain.Filter
|
||||
for rows.Next() {
|
||||
var f domain.Filter
|
||||
|
||||
if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &f.Priority, &f.CreatedAt, &f.UpdatedAt, &f.ActionsCount); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning row")
|
||||
}
|
||||
|
||||
filters = append(filters, f)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, errors.Wrap(err, "row error")
|
||||
}
|
||||
|
||||
return filters, nil
|
||||
}
|
||||
|
||||
func (r *FilterRepo) ListFilters(ctx context.Context) ([]domain.Filter, error) {
|
||||
actionCountQuery := r.db.squirrel.
|
||||
Select("COUNT(*)").
|
||||
|
@ -37,6 +130,7 @@ func (r *FilterRepo) ListFilters(ctx context.Context) ([]domain.Filter, error) {
|
|||
"f.id",
|
||||
"f.enabled",
|
||||
"f.name",
|
||||
"f.priority",
|
||||
"f.created_at",
|
||||
"f.updated_at",
|
||||
).
|
||||
|
@ -60,7 +154,7 @@ func (r *FilterRepo) ListFilters(ctx context.Context) ([]domain.Filter, error) {
|
|||
for rows.Next() {
|
||||
var f domain.Filter
|
||||
|
||||
if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &f.CreatedAt, &f.UpdatedAt, &f.ActionsCount); err != nil {
|
||||
if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &f.Priority, &f.CreatedAt, &f.UpdatedAt, &f.ActionsCount); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning row")
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ https://autodl-community.github.io/autodl-irssi/configuration/filter/
|
|||
type FilterRepo interface {
|
||||
FindByID(ctx context.Context, filterID int) (*Filter, error)
|
||||
FindByIndexerIdentifier(indexer string) ([]Filter, error)
|
||||
Find(ctx context.Context, params FilterQueryParams) ([]Filter, error)
|
||||
ListFilters(ctx context.Context) ([]Filter, error)
|
||||
Store(ctx context.Context, filter Filter) (*Filter, error)
|
||||
Update(ctx context.Context, filter Filter) (*Filter, error)
|
||||
|
@ -49,6 +50,14 @@ const (
|
|||
FilterMaxDownloadsEver FilterMaxDownloadsUnit = "EVER"
|
||||
)
|
||||
|
||||
type FilterQueryParams struct {
|
||||
Sort map[string]string
|
||||
Filters struct {
|
||||
Indexers []string
|
||||
}
|
||||
Search string
|
||||
}
|
||||
|
||||
type Filter struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
@ -58,7 +67,7 @@ type Filter struct {
|
|||
MinSize string `json:"min_size,omitempty"`
|
||||
MaxSize string `json:"max_size,omitempty"`
|
||||
Delay int `json:"delay,omitempty"`
|
||||
Priority int32 `json:"priority,omitempty"`
|
||||
Priority int32 `json:"priority"`
|
||||
MaxDownloads int `json:"max_downloads,omitempty"`
|
||||
MaxDownloadsUnit FilterMaxDownloadsUnit `json:"max_downloads_unit,omitempty"`
|
||||
MatchReleases string `json:"match_releases,omitempty"`
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
type Service interface {
|
||||
FindByID(ctx context.Context, filterID int) (*domain.Filter, error)
|
||||
FindByIndexerIdentifier(indexer string) ([]domain.Filter, error)
|
||||
Find(ctx context.Context, params domain.FilterQueryParams) ([]domain.Filter, error)
|
||||
CheckFilter(f domain.Filter, release *domain.Release) (bool, error)
|
||||
ListFilters(ctx context.Context) ([]domain.Filter, error)
|
||||
Store(ctx context.Context, filter domain.Filter) (*domain.Filter, error)
|
||||
|
@ -52,6 +53,29 @@ func NewService(log logger.Logger, repo domain.FilterRepo, actionRepo domain.Act
|
|||
}
|
||||
}
|
||||
|
||||
func (s *service) Find(ctx context.Context, params domain.FilterQueryParams) ([]domain.Filter, error) {
|
||||
// get filters
|
||||
filters, err := s.repo.Find(ctx, params)
|
||||
if err != nil {
|
||||
s.log.Error().Err(err).Msgf("could not find list filters")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]domain.Filter, 0)
|
||||
|
||||
for _, filter := range filters {
|
||||
indexers, err := s.indexerSvc.FindByFilterID(ctx, filter.ID)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
filter.Indexers = indexers
|
||||
|
||||
ret = append(ret, filter)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (s *service) ListFilters(ctx context.Context) ([]domain.Filter, error) {
|
||||
// get filters
|
||||
filters, err := s.repo.ListFilters(ctx)
|
||||
|
|
|
@ -4,7 +4,9 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
|
@ -14,6 +16,7 @@ import (
|
|||
type filterService interface {
|
||||
ListFilters(ctx context.Context) ([]domain.Filter, error)
|
||||
FindByID(ctx context.Context, filterID int) (*domain.Filter, error)
|
||||
Find(ctx context.Context, params domain.FilterQueryParams) ([]domain.Filter, error)
|
||||
Store(ctx context.Context, filter domain.Filter) (*domain.Filter, error)
|
||||
Delete(ctx context.Context, filterID int) error
|
||||
Update(ctx context.Context, filter domain.Filter) (*domain.Filter, error)
|
||||
|
@ -48,7 +51,43 @@ func (h filterHandler) Routes(r chi.Router) {
|
|||
func (h filterHandler) getFilters(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
trackers, err := h.service.ListFilters(ctx)
|
||||
params := domain.FilterQueryParams{
|
||||
Sort: map[string]string{},
|
||||
Filters: struct {
|
||||
Indexers []string
|
||||
}{},
|
||||
Search: "",
|
||||
}
|
||||
|
||||
sort := r.URL.Query().Get("sort")
|
||||
if sort != "" && strings.Contains(sort, "-") {
|
||||
field := ""
|
||||
order := ""
|
||||
|
||||
s := strings.Split(sort, "-")
|
||||
if s[0] == "name" || s[0] == "priority" {
|
||||
field = s[0]
|
||||
}
|
||||
|
||||
if s[1] == "asc" || s[1] == "desc" {
|
||||
order = s[1]
|
||||
}
|
||||
|
||||
params.Sort[field] = order
|
||||
}
|
||||
|
||||
u, err := url.Parse(r.URL.String())
|
||||
if err != nil {
|
||||
h.encoder.StatusResponse(r.Context(), w, map[string]interface{}{
|
||||
"code": "BAD_REQUEST_PARAMS",
|
||||
"message": "indexer parameter is invalid",
|
||||
}, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
vals := u.Query()
|
||||
params.Filters.Indexers = vals["indexer"]
|
||||
|
||||
trackers, err := h.service.Find(ctx, params)
|
||||
if err != nil {
|
||||
//
|
||||
h.encoder.Error(w, err)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue