mirror of
https://github.com/idanoo/autobrr
synced 2025-07-22 16:29:12 +00:00
feat: delete all releases from settings (#170)
This commit is contained in:
parent
c28c6186d9
commit
3b43ccba8a
8 changed files with 148 additions and 14 deletions
|
@ -283,3 +283,33 @@ FROM "release";`
|
|||
|
||||
return &rls, nil
|
||||
}
|
||||
|
||||
func (repo *ReleaseRepo) Delete(ctx context.Context) error {
|
||||
tx, err := repo.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer tx.Rollback()
|
||||
|
||||
_, err = tx.ExecContext(ctx, `DELETE FROM "release"`)
|
||||
if err != nil {
|
||||
log.Error().Stack().Err(err).Msg("error deleting all releases")
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tx.ExecContext(ctx, `DELETE FROM release_action_status`)
|
||||
if err != nil {
|
||||
log.Error().Stack().Err(err).Msg("error deleting all release_action_status")
|
||||
return err
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
log.Error().Stack().Err(err).Msg("error deleting all releases")
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ type ReleaseRepo interface {
|
|||
GetActionStatusByReleaseID(ctx context.Context, releaseID int64) ([]ReleaseActionStatus, error)
|
||||
Stats(ctx context.Context) (*ReleaseStats, error)
|
||||
StoreReleaseActionStatus(ctx context.Context, actionStatus *ReleaseActionStatus) error
|
||||
Delete(ctx context.Context) error
|
||||
}
|
||||
|
||||
type Release struct {
|
||||
|
|
|
@ -21,6 +21,14 @@ func (e encoder) StatusResponse(ctx context.Context, w http.ResponseWriter, resp
|
|||
}
|
||||
}
|
||||
|
||||
func (e encoder) StatusNoContent(w http.ResponseWriter) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (e encoder) StatusNotFound(ctx context.Context, w http.ResponseWriter) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
|
||||
func (e encoder) StatusInternalError(w http.ResponseWriter) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ type releaseService interface {
|
|||
Find(ctx context.Context, query domain.ReleaseQueryParams) (res []domain.Release, nextCursor int64, count int64, err error)
|
||||
GetIndexerOptions(ctx context.Context) ([]string, error)
|
||||
Stats(ctx context.Context) (*domain.ReleaseStats, error)
|
||||
Delete(ctx context.Context) error
|
||||
}
|
||||
|
||||
type releaseHandler struct {
|
||||
|
@ -32,6 +33,7 @@ func (h releaseHandler) Routes(r chi.Router) {
|
|||
r.Get("/", h.findReleases)
|
||||
r.Get("/stats", h.getStats)
|
||||
r.Get("/indexers", h.getIndexerOptions)
|
||||
r.Delete("/all", h.deleteReleases)
|
||||
}
|
||||
|
||||
func (h releaseHandler) findReleases(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -135,3 +137,13 @@ func (h releaseHandler) getStats(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
h.encoder.StatusResponse(r.Context(), w, stats, http.StatusOK)
|
||||
}
|
||||
|
||||
func (h releaseHandler) deleteReleases(w http.ResponseWriter, r *http.Request) {
|
||||
err := h.service.Delete(r.Context())
|
||||
if err != nil {
|
||||
h.encoder.StatusInternalError(w)
|
||||
return
|
||||
}
|
||||
|
||||
h.encoder.StatusNoContent(w)
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ type Service interface {
|
|||
Store(ctx context.Context, release *domain.Release) error
|
||||
StoreReleaseActionStatus(ctx context.Context, actionStatus *domain.ReleaseActionStatus) error
|
||||
Process(release domain.Release) error
|
||||
Delete(ctx context.Context) error
|
||||
}
|
||||
|
||||
type service struct {
|
||||
|
@ -44,12 +45,7 @@ func (s *service) GetIndexerOptions(ctx context.Context) ([]string, error) {
|
|||
}
|
||||
|
||||
func (s *service) Stats(ctx context.Context) (*domain.ReleaseStats, error) {
|
||||
stats, err := s.repo.Stats(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
return s.repo.Stats(ctx)
|
||||
}
|
||||
|
||||
func (s *service) Store(ctx context.Context, release *domain.Release) error {
|
||||
|
@ -62,12 +58,7 @@ func (s *service) Store(ctx context.Context, release *domain.Release) error {
|
|||
}
|
||||
|
||||
func (s *service) StoreReleaseActionStatus(ctx context.Context, actionStatus *domain.ReleaseActionStatus) error {
|
||||
err := s.repo.StoreReleaseActionStatus(ctx, actionStatus)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return s.repo.StoreReleaseActionStatus(ctx, actionStatus)
|
||||
}
|
||||
|
||||
func (s *service) Process(release domain.Release) error {
|
||||
|
@ -88,3 +79,7 @@ func (s *service) Process(release domain.Release) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *service) Delete(ctx context.Context) error {
|
||||
return s.repo.Delete(ctx)
|
||||
}
|
||||
|
|
|
@ -119,6 +119,7 @@ export const APIClient = {
|
|||
return appClient.Get<ReleaseFindResponse>(`api/release?${params.toString()}`)
|
||||
},
|
||||
indexerOptions: () => appClient.Get<string[]>(`api/release/indexers`),
|
||||
stats: () => appClient.Get<ReleaseStats>("api/release/stats")
|
||||
stats: () => appClient.Get<ReleaseStats>("api/release/stats"),
|
||||
delete: () => appClient.Delete(`api/release/all`),
|
||||
}
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
import {CogIcon, DownloadIcon, KeyIcon} from '@heroicons/react/outline'
|
||||
import {CogIcon, CollectionIcon, DownloadIcon, KeyIcon} from '@heroicons/react/outline'
|
||||
import {NavLink, Route, Switch as RouteSwitch, useLocation, useRouteMatch} from "react-router-dom";
|
||||
|
||||
import { classNames } from "../utils";
|
||||
|
@ -7,12 +7,14 @@ import { IrcSettings } from "./settings/Irc";
|
|||
import ApplicationSettings from "./settings/Application";
|
||||
import DownloadClientSettings from "./settings/DownloadClient";
|
||||
import { RegexPlayground } from './settings/RegexPlayground';
|
||||
import ReleaseSettings from "./settings/Releases";
|
||||
|
||||
const subNavigation = [
|
||||
{name: 'Application', href: '', icon: CogIcon, current: true},
|
||||
{name: 'Indexers', href: 'indexers', icon: KeyIcon, current: false},
|
||||
{name: 'IRC', href: 'irc', icon: KeyIcon, current: false},
|
||||
{name: 'Clients', href: 'clients', icon: DownloadIcon, current: false},
|
||||
{name: 'Releases', href: 'releases', icon: CollectionIcon, current: false},
|
||||
// {name: 'Regex Playground', href: 'regex-playground', icon: CogIcon, current: false}
|
||||
// {name: 'Actions', href: 'actions', icon: PlayIcon, current: false},
|
||||
// {name: 'Rules', href: 'rules', icon: ClipboardCheckIcon, current: false},
|
||||
|
@ -91,6 +93,10 @@ export default function Settings() {
|
|||
<DownloadClientSettings/>
|
||||
</Route>
|
||||
|
||||
<Route path={`${url}/releases`}>
|
||||
<ReleaseSettings/>
|
||||
</Route>
|
||||
|
||||
{/*<Route path={`${url}/actions`}>
|
||||
<ActionSettings/>
|
||||
</Route>*/}
|
||||
|
|
81
web/src/screens/settings/Releases.tsx
Normal file
81
web/src/screens/settings/Releases.tsx
Normal file
|
@ -0,0 +1,81 @@
|
|||
import { useRef } from "react";
|
||||
import { useMutation } from "react-query";
|
||||
import { toast } from "react-hot-toast";
|
||||
|
||||
import { APIClient } from "../../api/APIClient";
|
||||
import Toast from "../../components/notifications/Toast";
|
||||
import { queryClient } from "../../App";
|
||||
import { useToggle } from "../../hooks/hooks";
|
||||
import { DeleteModal } from "../../components/modals";
|
||||
|
||||
function ReleaseSettings() {
|
||||
const [deleteModalIsOpen, toggleDeleteModal] = useToggle(false);
|
||||
const deleteMutation = useMutation(() => APIClient.release.delete(), {
|
||||
onSuccess: () => {
|
||||
toast.custom((t) => (
|
||||
<Toast type="success" body={`All releases was deleted`} t={t}/>
|
||||
));
|
||||
|
||||
// Invalidate filters just in case, most likely not necessary but can't hurt.
|
||||
queryClient.invalidateQueries("releases");
|
||||
|
||||
toggleDeleteModal()
|
||||
}
|
||||
})
|
||||
|
||||
const deleteAction = () => {
|
||||
deleteMutation.mutate()
|
||||
}
|
||||
|
||||
const cancelModalButtonRef = useRef(null);
|
||||
|
||||
return (
|
||||
<form className="divide-y divide-gray-200 dark:divide-gray-700 lg:col-span-9" action="#" method="POST">
|
||||
<DeleteModal
|
||||
isOpen={deleteModalIsOpen}
|
||||
toggle={toggleDeleteModal}
|
||||
buttonRef={cancelModalButtonRef}
|
||||
deleteAction={deleteAction}
|
||||
title={`Delete all releases`}
|
||||
text="Are you sure you want to delete all releases? This action cannot be undone."
|
||||
/>
|
||||
|
||||
<div className="py-6 px-4 sm:p-6 lg:pb-8">
|
||||
<div>
|
||||
<h2 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">Releases</h2>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
Release settings. Reset state.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pb-6 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="px-4 py-5 sm:p-0">
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
|
||||
<div>
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">Danger Zone</h3>
|
||||
</div>
|
||||
|
||||
<ul className="p-4 mt-6 divide-y divide-gray-200 dark:divide-gray-700 border-red-500 border rounded-lg">
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Delete all releases
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleDeleteModal}
|
||||
className="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-red-700 dark:text-red-100 bg-red-100 dark:bg-red-500 hover:bg-red-200 dark:hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:text-sm"
|
||||
>
|
||||
Delete all releases
|
||||
</button>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
export default ReleaseSettings;
|
Loading…
Add table
Add a link
Reference in a new issue