mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 16:59:12 +00:00
feat(filters): add external script and webhook checks
This commit is contained in:
parent
16dd8c5419
commit
d56693cd33
17 changed files with 635 additions and 200 deletions
|
@ -13,6 +13,7 @@ interface TextFieldProps {
|
|||
columns?: COL_WIDTHS;
|
||||
autoComplete?: string;
|
||||
hidden?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const TextField = ({
|
||||
|
@ -22,7 +23,8 @@ export const TextField = ({
|
|||
placeholder,
|
||||
columns,
|
||||
autoComplete,
|
||||
hidden
|
||||
hidden,
|
||||
disabled,
|
||||
}: TextFieldProps) => (
|
||||
<div
|
||||
className={classNames(
|
||||
|
@ -47,7 +49,12 @@ export const TextField = ({
|
|||
type="text"
|
||||
defaultValue={defaultValue}
|
||||
autoComplete={autoComplete}
|
||||
className={classNames(meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700", "mt-2 block w-full dark:bg-gray-800 dark:text-gray-100 rounded-md")}
|
||||
className={classNames(
|
||||
meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700",
|
||||
disabled ? "bg-gray-100 dark:bg-gray-700 cursor-not-allowed" : "dark:bg-gray-800",
|
||||
"mt-2 block w-full dark:text-gray-100 rounded-md",
|
||||
)}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
|
||||
|
@ -60,6 +67,70 @@ export const TextField = ({
|
|||
</div>
|
||||
);
|
||||
|
||||
interface TextAreaProps {
|
||||
name: string;
|
||||
defaultValue?: string;
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
columns?: COL_WIDTHS;
|
||||
rows?: number;
|
||||
autoComplete?: string;
|
||||
hidden?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const TextArea = ({
|
||||
name,
|
||||
defaultValue,
|
||||
label,
|
||||
placeholder,
|
||||
columns,
|
||||
rows,
|
||||
autoComplete,
|
||||
hidden,
|
||||
disabled,
|
||||
}: TextAreaProps) => (
|
||||
<div
|
||||
className={classNames(
|
||||
hidden ? "hidden" : "",
|
||||
columns ? `col-span-${columns}` : "col-span-12"
|
||||
)}
|
||||
>
|
||||
{label && (
|
||||
<label htmlFor={name} className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<Field name={name}>
|
||||
{({
|
||||
field,
|
||||
meta
|
||||
}: FieldProps) => (
|
||||
<div>
|
||||
<textarea
|
||||
{...field}
|
||||
id={name}
|
||||
rows={rows}
|
||||
defaultValue={defaultValue}
|
||||
autoComplete={autoComplete}
|
||||
className={classNames(
|
||||
meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700",
|
||||
disabled ? "bg-gray-100 dark:bg-gray-700 cursor-not-allowed" : "dark:bg-gray-800",
|
||||
"mt-2 block w-full dark:text-gray-100 rounded-md",
|
||||
)}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
||||
{meta.touched && meta.error && (
|
||||
<p className="error text-sm text-red-600 mt-1">* {meta.error}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Field>
|
||||
</div>
|
||||
);
|
||||
|
||||
interface PasswordFieldProps {
|
||||
name: string;
|
||||
label?: string;
|
||||
|
@ -132,13 +203,15 @@ interface NumberFieldProps {
|
|||
label?: string;
|
||||
placeholder?: string;
|
||||
step?: number;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const NumberField = ({
|
||||
name,
|
||||
label,
|
||||
placeholder,
|
||||
step
|
||||
step,
|
||||
disabled,
|
||||
}: NumberFieldProps) => (
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<label htmlFor={name} className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
|
||||
|
@ -159,9 +232,11 @@ export const NumberField = ({
|
|||
meta.touched && meta.error
|
||||
? "focus:ring-red-500 focus:border-red-500 border-red-500"
|
||||
: "focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300",
|
||||
"mt-2 block w-full dark:bg-gray-800 border border-gray-300 dark:border-gray-700 dark:text-gray-100 rounded-md"
|
||||
"mt-2 block w-full border border-gray-300 dark:border-gray-700 dark:text-gray-100 rounded-md",
|
||||
disabled ? "bg-gray-100 dark:bg-gray-700 cursor-not-allowed" : "dark:bg-gray-800",
|
||||
)}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
/>
|
||||
{meta.touched && meta.error && (
|
||||
<div className="error">{meta.error}</div>
|
||||
|
|
|
@ -74,16 +74,18 @@ interface SwitchGroupProps {
|
|||
label?: string;
|
||||
description?: string;
|
||||
className?: string;
|
||||
heading?: boolean;
|
||||
}
|
||||
|
||||
const SwitchGroup = ({
|
||||
name,
|
||||
label,
|
||||
description
|
||||
description,
|
||||
heading,
|
||||
}: SwitchGroupProps) => (
|
||||
<HeadlessSwitch.Group as="ol" className="py-4 flex items-center justify-between">
|
||||
{label && <div className="flex flex-col">
|
||||
<HeadlessSwitch.Label as="p" className="text-sm font-medium text-gray-900 dark:text-gray-100"
|
||||
<HeadlessSwitch.Label as={heading ? "h2" : "p"} className={classNames("font-medium text-gray-900 dark:text-gray-100", heading ? "text-lg" : "text-sm")}
|
||||
passive>
|
||||
{label}
|
||||
</HeadlessSwitch.Label>
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
useParams
|
||||
} from "react-router-dom";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { Field, FieldArray, FieldProps, Form, Formik, FormikValues } from "formik";
|
||||
import { Field, FieldArray, FieldProps, Form, Formik, FormikValues, useFormikContext } from "formik";
|
||||
import { Dialog, Transition, Switch as SwitchBasic } from "@headlessui/react";
|
||||
import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/solid";
|
||||
|
||||
|
@ -50,6 +50,7 @@ import { AlertWarning } from "../../components/alerts";
|
|||
import { DeleteModal } from "../../components/modals";
|
||||
import { TitleSubtitle } from "../../components/headings";
|
||||
import { EmptyListState } from "../../components/emptystates";
|
||||
import { TextArea } from "../../components/inputs/input";
|
||||
|
||||
interface tabType {
|
||||
name: string;
|
||||
|
@ -61,6 +62,7 @@ const tabs: tabType[] = [
|
|||
{ name: "Movies and TV", href: "movies-tv" },
|
||||
{ name: "Music", href: "music" },
|
||||
{ name: "Advanced", href: "advanced" },
|
||||
{ name: "External", href: "external" },
|
||||
{ name: "Actions", href: "actions" }
|
||||
];
|
||||
|
||||
|
@ -280,7 +282,15 @@ export default function FilterDetails() {
|
|||
albums: filter.albums,
|
||||
origins: filter.origins || [],
|
||||
indexers: filter.indexers || [],
|
||||
actions: filter.actions || []
|
||||
actions: filter.actions || [],
|
||||
external_script_enabled: filter.external_script_enabled || false,
|
||||
external_script_cmd: filter.external_script_cmd || "",
|
||||
external_script_args: filter.external_script_args || "",
|
||||
external_script_expect_status: filter.external_script_expect_status || 0,
|
||||
external_webhook_enabled: filter.external_webhook_enabled || false,
|
||||
external_webhook_host: filter.external_webhook_host || "",
|
||||
external_webhook_data: filter.external_webhook_data ||"",
|
||||
external_webhook_expect_status: filter.external_webhook_expect_status || 0,
|
||||
} as Filter}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
|
@ -291,6 +301,7 @@ export default function FilterDetails() {
|
|||
<Route path="movies-tv" element={<MoviesTv />} />
|
||||
<Route path="music" element={<Music />} />
|
||||
<Route path="advanced" element={<Advanced />} />
|
||||
<Route path="external" element={<External />} />
|
||||
<Route path="actions" element={<FilterActions filter={filter} values={values} />}
|
||||
/>
|
||||
</Routes>
|
||||
|
@ -527,6 +538,75 @@ function CollapsableSection({ title, subtitle, children }: CollapsableSectionPro
|
|||
);
|
||||
}
|
||||
|
||||
export function External() {
|
||||
const { values } = useFormikContext<Filter>();
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
<div className="mt-6">
|
||||
<SwitchGroup name="external_script_enabled" heading={true} label="Script" description="Run external script and check status as part of filtering" />
|
||||
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<TextField
|
||||
name="external_script_cmd"
|
||||
label="Command"
|
||||
columns={6}
|
||||
placeholder="Path to program eg. /bin/test"
|
||||
disabled={!values.external_script_enabled}
|
||||
/>
|
||||
<TextField
|
||||
name="external_script_args"
|
||||
label="Arguments"
|
||||
columns={6}
|
||||
placeholder="Arguments eg. --test"
|
||||
disabled={!values.external_script_enabled}
|
||||
/>
|
||||
<NumberField
|
||||
name="external_script_expect_status"
|
||||
label="Expected exit status"
|
||||
placeholder="0"
|
||||
disabled={!values.external_script_enabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6">
|
||||
<div className="border-t dark:border-gray-700">
|
||||
<SwitchGroup name="external_webhook_enabled" heading={true} label="Webhook" description="Run external webhook and check status as part of filtering" />
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<div className="grid col-span-6 gap-6">
|
||||
<TextField
|
||||
name="external_webhook_host"
|
||||
label="Host"
|
||||
columns={6}
|
||||
placeholder="Host eg. http://localhost/webhook"
|
||||
disabled={!values.external_webhook_enabled}
|
||||
/>
|
||||
<NumberField
|
||||
name="external_webhook_expect_status"
|
||||
label="Expected http status"
|
||||
placeholder="200"
|
||||
disabled={!values.external_webhook_enabled}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<TextArea
|
||||
name="external_webhook_data"
|
||||
label="Data (json)"
|
||||
columns={6}
|
||||
rows={5}
|
||||
placeholder={"{ \"key\": \"value\" }"}
|
||||
disabled={!values.external_webhook_enabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface FilterActionsProps {
|
||||
filter: Filter;
|
||||
values: FormikValues;
|
||||
|
|
8
web/src/types/Filter.d.ts
vendored
8
web/src/types/Filter.d.ts
vendored
|
@ -52,6 +52,14 @@ interface Filter {
|
|||
except_tags_any: string;
|
||||
actions: Action[];
|
||||
indexers: Indexer[];
|
||||
external_script_enabled: boolean;
|
||||
external_script_cmd: string;
|
||||
external_script_args: string;
|
||||
external_script_expect_status: number;
|
||||
external_webhook_enabled: boolean;
|
||||
external_webhook_host: string;
|
||||
external_webhook_data: string;
|
||||
external_webhook_expect_status: number;
|
||||
}
|
||||
|
||||
interface Action {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue