mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 08:49:13 +00:00
feat(feeds): clear feed cache (#1071)
This commit is contained in:
parent
3755881c40
commit
b6de7144e0
5 changed files with 84 additions and 1 deletions
|
@ -216,7 +216,7 @@ func (r *FeedCacheRepo) DeleteBucket(ctx context.Context, bucket string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if rows == 0 {
|
if rows == 0 {
|
||||||
return errors.Wrap(err, "error no rows affected")
|
r.log.Warn().Msgf("no rows affected for delete of bucket: %s", bucket)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -33,6 +33,7 @@ type Service interface {
|
||||||
Test(ctx context.Context, feed *domain.Feed) error
|
Test(ctx context.Context, feed *domain.Feed) error
|
||||||
ToggleEnabled(ctx context.Context, id int, enabled bool) error
|
ToggleEnabled(ctx context.Context, id int, enabled bool) error
|
||||||
Delete(ctx context.Context, id int) error
|
Delete(ctx context.Context, id int) error
|
||||||
|
DeleteFeedCache(ctx context.Context, id int) error
|
||||||
GetLastRunData(ctx context.Context, id int) (string, error)
|
GetLastRunData(ctx context.Context, id int) (string, error)
|
||||||
|
|
||||||
Start() error
|
Start() error
|
||||||
|
@ -122,6 +123,21 @@ func (s *service) Delete(ctx context.Context, id int) error {
|
||||||
return s.delete(ctx, id)
|
return s.delete(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *service) DeleteFeedCache(ctx context.Context, id int) error {
|
||||||
|
feed, err := s.repo.FindByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Error().Err(err).Msgf("could not find feed by id: %d", id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.cacheRepo.DeleteBucket(ctx, feed.Name); err != nil {
|
||||||
|
s.log.Error().Err(err).Msgf("could not clear feed cache: %d", id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *service) ToggleEnabled(ctx context.Context, id int, enabled bool) error {
|
func (s *service) ToggleEnabled(ctx context.Context, id int, enabled bool) error {
|
||||||
return s.toggleEnabled(ctx, id, enabled)
|
return s.toggleEnabled(ctx, id, enabled)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ type feedService interface {
|
||||||
Store(ctx context.Context, feed *domain.Feed) error
|
Store(ctx context.Context, feed *domain.Feed) error
|
||||||
Update(ctx context.Context, feed *domain.Feed) error
|
Update(ctx context.Context, feed *domain.Feed) error
|
||||||
Delete(ctx context.Context, id int) error
|
Delete(ctx context.Context, id int) error
|
||||||
|
DeleteFeedCache(ctx context.Context, id int) error
|
||||||
ToggleEnabled(ctx context.Context, id int, enabled bool) error
|
ToggleEnabled(ctx context.Context, id int, enabled bool) error
|
||||||
Test(ctx context.Context, feed *domain.Feed) error
|
Test(ctx context.Context, feed *domain.Feed) error
|
||||||
GetLastRunData(ctx context.Context, id int) (string, error)
|
GetLastRunData(ctx context.Context, id int) (string, error)
|
||||||
|
@ -44,6 +45,7 @@ func (h feedHandler) Routes(r chi.Router) {
|
||||||
r.Route("/{feedID}", func(r chi.Router) {
|
r.Route("/{feedID}", func(r chi.Router) {
|
||||||
r.Put("/", h.update)
|
r.Put("/", h.update)
|
||||||
r.Delete("/", h.delete)
|
r.Delete("/", h.delete)
|
||||||
|
r.Delete("/cache", h.deleteCache)
|
||||||
r.Patch("/enabled", h.toggleEnabled)
|
r.Patch("/enabled", h.toggleEnabled)
|
||||||
r.Get("/latest", h.latestRun)
|
r.Get("/latest", h.latestRun)
|
||||||
})
|
})
|
||||||
|
@ -168,6 +170,26 @@ func (h feedHandler) delete(w http.ResponseWriter, r *http.Request) {
|
||||||
h.encoder.StatusResponse(w, http.StatusNoContent, nil)
|
h.encoder.StatusResponse(w, http.StatusNoContent, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h feedHandler) deleteCache(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var (
|
||||||
|
ctx = r.Context()
|
||||||
|
filterID = chi.URLParam(r, "feedID")
|
||||||
|
)
|
||||||
|
|
||||||
|
id, err := strconv.Atoi(filterID)
|
||||||
|
if err != nil {
|
||||||
|
h.encoder.Error(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.service.DeleteFeedCache(ctx, id); err != nil {
|
||||||
|
h.encoder.Error(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.encoder.StatusResponse(w, http.StatusNoContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func (h feedHandler) latestRun(w http.ResponseWriter, r *http.Request) {
|
func (h feedHandler) latestRun(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
ctx = r.Context()
|
ctx = r.Context()
|
||||||
|
|
|
@ -131,6 +131,7 @@ export const APIClient = {
|
||||||
toggleEnable: (id: number, enabled: boolean) => appClient.Patch(`api/feeds/${id}/enabled`, { enabled }),
|
toggleEnable: (id: number, enabled: boolean) => appClient.Patch(`api/feeds/${id}/enabled`, { enabled }),
|
||||||
update: (feed: Feed) => appClient.Put(`api/feeds/${feed.id}`, feed),
|
update: (feed: Feed) => appClient.Put(`api/feeds/${feed.id}`, feed),
|
||||||
delete: (id: number) => appClient.Delete(`api/feeds/${id}`),
|
delete: (id: number) => appClient.Delete(`api/feeds/${id}`),
|
||||||
|
deleteCache: (id: number) => appClient.Delete(`api/feeds/${id}/cache`),
|
||||||
test: (feed: Feed) => appClient.Post("api/feeds/test", feed)
|
test: (feed: Feed) => appClient.Post("api/feeds/test", feed)
|
||||||
},
|
},
|
||||||
indexers: {
|
indexers: {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import { DeleteModal } from "@components/modals";
|
||||||
import { FeedUpdateForm } from "@forms/settings/FeedForms";
|
import { FeedUpdateForm } from "@forms/settings/FeedForms";
|
||||||
import { EmptySimple } from "@components/emptystates";
|
import { EmptySimple } from "@components/emptystates";
|
||||||
import { ImplementationBadges } from "./Indexer";
|
import { ImplementationBadges } from "./Indexer";
|
||||||
|
import {ArrowPathIcon} from "@heroicons/react/24/solid";
|
||||||
|
|
||||||
export const feedKeys = {
|
export const feedKeys = {
|
||||||
all: ["feeds"] as const,
|
all: ["feeds"] as const,
|
||||||
|
@ -230,10 +231,13 @@ const FeedItemDropdown = ({
|
||||||
toggleUpdate
|
toggleUpdate
|
||||||
}: FeedItemDropdownProps) => {
|
}: FeedItemDropdownProps) => {
|
||||||
const cancelModalButtonRef = useRef(null);
|
const cancelModalButtonRef = useRef(null);
|
||||||
|
const cancelCacheModalButtonRef = useRef(null);
|
||||||
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const [deleteModalIsOpen, toggleDeleteModal] = useToggle(false);
|
const [deleteModalIsOpen, toggleDeleteModal] = useToggle(false);
|
||||||
|
const [deleteCacheModalIsOpen, toggleDeleteCacheModal] = useToggle(false);
|
||||||
|
|
||||||
const deleteMutation = useMutation({
|
const deleteMutation = useMutation({
|
||||||
mutationFn: (id: number) => APIClient.feeds.delete(id),
|
mutationFn: (id: number) => APIClient.feeds.delete(id),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
|
@ -244,6 +248,13 @@ const FeedItemDropdown = ({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const deleteCacheMutation = useMutation({
|
||||||
|
mutationFn: (id: number) => APIClient.feeds.deleteCache(id),
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.custom((t) => <Toast type="success" body={`Feed ${feed?.name} cache was cleared!`} t={t}/>);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu as="div">
|
<Menu as="div">
|
||||||
<DeleteModal
|
<DeleteModal
|
||||||
|
@ -257,6 +268,16 @@ const FeedItemDropdown = ({
|
||||||
title={`Remove feed: ${feed.name}`}
|
title={`Remove feed: ${feed.name}`}
|
||||||
text="Are you sure you want to remove this feed? This action cannot be undone."
|
text="Are you sure you want to remove this feed? This action cannot be undone."
|
||||||
/>
|
/>
|
||||||
|
<DeleteModal
|
||||||
|
isOpen={deleteCacheModalIsOpen}
|
||||||
|
toggle={toggleDeleteCacheModal}
|
||||||
|
buttonRef={cancelCacheModalButtonRef}
|
||||||
|
deleteAction={() => {
|
||||||
|
deleteCacheMutation.mutate(feed.id);
|
||||||
|
}}
|
||||||
|
title={`Remove feed cache: ${feed.name}`}
|
||||||
|
text="Are you sure you want to remove the feed cache? This action cannot be undone."
|
||||||
|
/>
|
||||||
<Menu.Button className="px-4 py-2">
|
<Menu.Button className="px-4 py-2">
|
||||||
<EllipsisHorizontalIcon
|
<EllipsisHorizontalIcon
|
||||||
className="w-5 h-5 text-gray-700 hover:text-gray-900 dark:text-gray-100 dark:hover:text-gray-400"
|
className="w-5 h-5 text-gray-700 hover:text-gray-900 dark:text-gray-100 dark:hover:text-gray-400"
|
||||||
|
@ -317,6 +338,7 @@ const FeedItemDropdown = ({
|
||||||
)}
|
)}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
<Menu.Item>
|
<Menu.Item>
|
||||||
{({ active }) => (
|
{({ active }) => (
|
||||||
<a
|
<a
|
||||||
|
@ -339,6 +361,28 @@ const FeedItemDropdown = ({
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
<Menu.Item>
|
||||||
|
{({ active }) => (
|
||||||
|
<button
|
||||||
|
className={classNames(
|
||||||
|
active ? "bg-red-600 text-white" : "text-gray-900 dark:text-gray-300",
|
||||||
|
"font-medium group flex rounded-md items-center w-full px-2 py-2 text-sm"
|
||||||
|
)}
|
||||||
|
onClick={() => toggleDeleteCacheModal()}
|
||||||
|
title="Manually clear all feed cache"
|
||||||
|
>
|
||||||
|
<ArrowPathIcon
|
||||||
|
className={classNames(
|
||||||
|
active ? "text-white" : "text-red-500",
|
||||||
|
"w-5 h-5 mr-2"
|
||||||
|
)}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
Clear feed cache
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</Menu.Item>
|
||||||
|
</div>
|
||||||
<div className="px-1 py-1">
|
<div className="px-1 py-1">
|
||||||
<Menu.Item>
|
<Menu.Item>
|
||||||
{({ active }) => (
|
{({ active }) => (
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue