diff --git a/internal/database/release.go b/internal/database/release.go index 6e34be9..fd77d39 100644 --- a/internal/database/release.go +++ b/internal/database/release.go @@ -76,9 +76,12 @@ func (repo *ReleaseRepo) UpdatePushStatusRejected(ctx context.Context, id int64, return nil } -func (repo *ReleaseRepo) Find(ctx context.Context, params domain.QueryParams) ([]domain.Release, int64, error) { +func (repo *ReleaseRepo) Find(ctx context.Context, params domain.QueryParams) ([]domain.Release, int64, int64, error) { - queryBuilder := sq.Select("id", "filter_status", "push_status", "rejections", "indexer", "filter", "protocol", "title", "torrent_name", "size", "timestamp").From("release").OrderBy("timestamp DESC") + queryBuilder := sq. + Select("id", "filter_status", "push_status", "rejections", "indexer", "filter", "protocol", "title", "torrent_name", "size", "timestamp", "COUNT() OVER() AS total_count"). + From("release"). + OrderBy("timestamp DESC") if params.Limit > 0 { queryBuilder = queryBuilder.Limit(params.Limit) @@ -86,8 +89,11 @@ func (repo *ReleaseRepo) Find(ctx context.Context, params domain.QueryParams) ([ queryBuilder = queryBuilder.Limit(20) } + if params.Offset > 0 { + queryBuilder = queryBuilder.Offset(params.Offset) + } + if params.Cursor > 0 { - //queryBuilder = queryBuilder.Where(sq.Gt{"id": params.Cursor}) queryBuilder = queryBuilder.Where(sq.Lt{"id": params.Cursor}) } @@ -108,26 +114,27 @@ func (repo *ReleaseRepo) Find(ctx context.Context, params domain.QueryParams) ([ rows, err := repo.db.QueryContext(ctx, query, args...) if err != nil { log.Error().Stack().Err(err).Msg("error fetching releases") - //return - return res, 0, nil + return res, 0, 0, nil } defer rows.Close() if err := rows.Err(); err != nil { log.Error().Stack().Err(err) - return res, 0, err + return res, 0, 0, err } + var countItems int64 = 0 + for rows.Next() { var rls domain.Release var indexer, filter sql.NullString var timestamp string - if err := rows.Scan(&rls.ID, &rls.FilterStatus, &rls.PushStatus, pq.Array(&rls.Rejections), &indexer, &filter, &rls.Protocol, &rls.Title, &rls.TorrentName, &rls.Size, ×tamp); err != nil { + if err := rows.Scan(&rls.ID, &rls.FilterStatus, &rls.PushStatus, pq.Array(&rls.Rejections), &indexer, &filter, &rls.Protocol, &rls.Title, &rls.TorrentName, &rls.Size, ×tamp, &countItems); err != nil { log.Error().Stack().Err(err).Msg("release.find: error scanning data to struct") - return res, 0, err + return res, 0, 0, err } rls.Indexer = indexer.String @@ -146,7 +153,7 @@ func (repo *ReleaseRepo) Find(ctx context.Context, params domain.QueryParams) ([ //nextCursor, _ = strconv.ParseInt(lastID, 10, 64) } - return res, nextCursor, nil + return res, nextCursor, countItems, nil } func (repo *ReleaseRepo) Stats(ctx context.Context) (*domain.ReleaseStats, error) { diff --git a/internal/domain/release.go b/internal/domain/release.go index 8a5ebbd..c791ec8 100644 --- a/internal/domain/release.go +++ b/internal/domain/release.go @@ -18,7 +18,7 @@ import ( type ReleaseRepo interface { Store(ctx context.Context, release *Release) (*Release, error) - Find(ctx context.Context, params QueryParams) (res []Release, nextCursor int64, err error) + Find(ctx context.Context, params QueryParams) (res []Release, nextCursor int64, count int64, err error) Stats(ctx context.Context) (*ReleaseStats, error) UpdatePushStatus(ctx context.Context, id int64, status ReleasePushStatus) error UpdatePushStatusRejected(ctx context.Context, id int64, rejections string) error @@ -913,6 +913,7 @@ const ( type QueryParams struct { Limit uint64 + Offset uint64 Cursor uint64 Sort map[string]string Filter map[string]string diff --git a/internal/http/release.go b/internal/http/release.go index 24c0b8e..f4d9f9d 100644 --- a/internal/http/release.go +++ b/internal/http/release.go @@ -10,7 +10,7 @@ import ( ) type releaseService interface { - Find(ctx context.Context, query domain.QueryParams) (res []domain.Release, nextCursor int64, err error) + Find(ctx context.Context, query domain.QueryParams) (res []domain.Release, nextCursor int64, count int64, err error) Stats(ctx context.Context) (*domain.ReleaseStats, error) } @@ -45,6 +45,15 @@ func (h releaseHandler) findReleases(w http.ResponseWriter, r *http.Request) { limit = 20 } + offsetP := r.URL.Query().Get("offset") + offset, err := strconv.Atoi(offsetP) + if err != nil && offsetP != "" { + h.encoder.StatusResponse(r.Context(), w, map[string]interface{}{ + "code": "BAD_REQUEST_PARAMS", + "message": "offset parameter is invalid", + }, http.StatusBadRequest) + } + cursorP := r.URL.Query().Get("cursor") cursor, err := strconv.Atoi(cursorP) if err != nil && cursorP != "" { @@ -56,12 +65,13 @@ func (h releaseHandler) findReleases(w http.ResponseWriter, r *http.Request) { query := domain.QueryParams{ Limit: uint64(limit), + Offset: uint64(offset), Cursor: uint64(cursor), Sort: nil, //Filter: "", } - releases, nextCursor, err := h.service.Find(r.Context(), query) + releases, nextCursor, count, err := h.service.Find(r.Context(), query) if err != nil { h.encoder.StatusNotFound(r.Context(), w) return @@ -70,9 +80,11 @@ func (h releaseHandler) findReleases(w http.ResponseWriter, r *http.Request) { ret := struct { Data []domain.Release `json:"data"` NextCursor int64 `json:"next_cursor"` + Count int64 `json:"count"` }{ Data: releases, NextCursor: nextCursor, + Count: count, } h.encoder.StatusResponse(r.Context(), w, ret, http.StatusOK) diff --git a/internal/release/service.go b/internal/release/service.go index c453798..eb7f2e4 100644 --- a/internal/release/service.go +++ b/internal/release/service.go @@ -11,7 +11,7 @@ import ( ) type Service interface { - Find(ctx context.Context, query domain.QueryParams) (res []domain.Release, nextCursor int64, err error) + Find(ctx context.Context, query domain.QueryParams) (res []domain.Release, nextCursor int64, count int64, err error) Stats(ctx context.Context) (*domain.ReleaseStats, error) Store(ctx context.Context, release *domain.Release) error UpdatePushStatus(ctx context.Context, id int64, status domain.ReleasePushStatus) error @@ -31,16 +31,12 @@ func NewService(repo domain.ReleaseRepo, actionService action.Service) Service { } } -func (s *service) Find(ctx context.Context, query domain.QueryParams) (res []domain.Release, nextCursor int64, err error) { - //releases, err := s.repo.Find(ctx, query) - res, nextCursor, err = s.repo.Find(ctx, query) +func (s *service) Find(ctx context.Context, query domain.QueryParams) (res []domain.Release, nextCursor int64, count int64, err error) { + res, nextCursor, count, err = s.repo.Find(ctx, query) if err != nil { - //return nil, err return } return - - //return releases, nil } func (s *service) Stats(ctx context.Context) (*domain.ReleaseStats, error) { diff --git a/web/src/domain/interfaces.ts b/web/src/domain/interfaces.ts index 8fabcaf..c86409a 100644 --- a/web/src/domain/interfaces.ts +++ b/web/src/domain/interfaces.ts @@ -188,6 +188,7 @@ export interface Release { export interface ReleaseFindResponse { data: Release[]; next_cursor: number; + count: number; } export interface ReleaseStats { diff --git a/web/src/screens/Base.tsx b/web/src/screens/Base.tsx index a49cd87..bb184ee 100644 --- a/web/src/screens/Base.tsx +++ b/web/src/screens/Base.tsx @@ -6,6 +6,7 @@ import Settings from "./Settings"; import { Dashboard } from "./Dashboard"; import { FilterDetails, Filters } from "./filters"; import Logs from './Logs'; +import { Releases } from "./Releases"; import logo from '../logo.png'; function classNames(...classes: string[]) { @@ -13,7 +14,7 @@ function classNames(...classes: string[]) { } export default function Base() { - const nav = [{ name: 'Dashboard', path: "/" }, { name: 'Filters', path: "/filters" }, { name: "Settings", path: "/settings" }, { name: "Logs", path: "/logs" }] + const nav = [{ name: 'Dashboard', path: "/" }, { name: 'Filters', path: "/filters" }, { name: 'Releases', path: "/releases" }, { name: "Settings", path: "/settings" }, { name: "Logs", path: "/logs" }] return (
Error
; + } + + if (isLoading) { + returnLoading...
; + } + + // Render the UI for your table + return ( + <> +
+
+ {column.render('Header')}
+ {/* Add a sort direction indicator */}
+
+ {column.isSorted
+ ? column.isSortedDesc
+ ?
+ |
+ ))}
+
---|
+ {cell.column.Cell.name === "defaultRenderer"
+ ? {cell.render('Cell')}
+ : cell.render('Cell')
+ }
+ |
+ )
+ })}
+