feat(feeds): clear feed cache (#1071)

This commit is contained in:
ze0s 2023-09-01 21:39:39 +02:00 committed by GitHub
parent 3755881c40
commit b6de7144e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 84 additions and 1 deletions

View file

@ -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

View file

@ -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)
} }

View file

@ -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()

View file

@ -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: {

View file

@ -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 }) => (