Move bulk of webhook logic to plugin (#4866)
* Move bulk of webhook logic to plugin * Re-enable cleanup task * cron tasks
This commit is contained in:
298
plugins/webhooks/client/components/WebhookSubscriptionForm.tsx
Normal file
298
plugins/webhooks/client/components/WebhookSubscriptionForm.tsx
Normal file
@@ -0,0 +1,298 @@
|
||||
import { isEqual, filter, includes } from "lodash";
|
||||
import randomstring from "randomstring";
|
||||
import * as React from "react";
|
||||
import { useEffect } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import WebhookSubscription from "~/models/WebhookSubscription";
|
||||
import Button from "~/components/Button";
|
||||
import Input from "~/components/Input";
|
||||
import Text from "~/components/Text";
|
||||
import useMobile from "~/hooks/useMobile";
|
||||
|
||||
const WEBHOOK_EVENTS = {
|
||||
user: [
|
||||
"users.create",
|
||||
"users.signin",
|
||||
"users.update",
|
||||
"users.suspend",
|
||||
"users.activate",
|
||||
"users.delete",
|
||||
"users.invite",
|
||||
"users.promote",
|
||||
"users.demote",
|
||||
],
|
||||
document: [
|
||||
"documents.create",
|
||||
"documents.publish",
|
||||
"documents.unpublish",
|
||||
"documents.delete",
|
||||
"documents.permanent_delete",
|
||||
"documents.archive",
|
||||
"documents.unarchive",
|
||||
"documents.restore",
|
||||
"documents.move",
|
||||
"documents.update",
|
||||
"documents.title_change",
|
||||
],
|
||||
revision: ["revisions.create"],
|
||||
fileOperation: [
|
||||
"fileOperations.create",
|
||||
"fileOperations.update",
|
||||
"fileOperations.delete",
|
||||
],
|
||||
collection: [
|
||||
"collections.create",
|
||||
"collections.update",
|
||||
"collections.delete",
|
||||
"collections.add_user",
|
||||
"collections.remove_user",
|
||||
"collections.add_group",
|
||||
"collections.remove_group",
|
||||
"collections.move",
|
||||
"collections.permission_changed",
|
||||
],
|
||||
group: [
|
||||
"groups.create",
|
||||
"groups.update",
|
||||
"groups.delete",
|
||||
"groups.add_user",
|
||||
"groups.remove_user",
|
||||
],
|
||||
integration: ["integrations.create", "integrations.update"],
|
||||
share: ["shares.create", "shares.update", "shares.revoke"],
|
||||
team: ["teams.update"],
|
||||
pin: ["pins.create", "pins.update", "pins.delete"],
|
||||
webhookSubscription: [
|
||||
"webhookSubscriptions.create",
|
||||
"webhookSubscriptions.delete",
|
||||
"webhookSubscriptions.update",
|
||||
],
|
||||
view: ["views.create"],
|
||||
};
|
||||
|
||||
const EventCheckboxLabel = styled.label`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 15px;
|
||||
padding: 0.2em 0;
|
||||
`;
|
||||
|
||||
const GroupEventCheckboxLabel = styled(EventCheckboxLabel)`
|
||||
font-weight: 500;
|
||||
font-size: 1.2em;
|
||||
`;
|
||||
|
||||
const AllEventCheckboxLabel = styled(GroupEventCheckboxLabel)`
|
||||
font-size: 1.4em;
|
||||
`;
|
||||
|
||||
const EventCheckboxText = styled.span`
|
||||
margin-left: 0.5rem;
|
||||
`;
|
||||
|
||||
interface FieldProps {
|
||||
disabled?: boolean;
|
||||
}
|
||||
const FieldSet = styled.fieldset<FieldProps>`
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
|
||||
${({ disabled }) =>
|
||||
disabled &&
|
||||
`
|
||||
opacity: 0.5;
|
||||
`}
|
||||
`;
|
||||
|
||||
interface MobileProps {
|
||||
isMobile?: boolean;
|
||||
}
|
||||
const GroupGrid = styled.div<MobileProps>`
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
||||
${({ isMobile }) =>
|
||||
isMobile &&
|
||||
`
|
||||
grid-template-columns: 1fr;
|
||||
`}
|
||||
`;
|
||||
|
||||
const GroupWrapper = styled.div<MobileProps>`
|
||||
padding-bottom: 2rem;
|
||||
|
||||
${({ isMobile }) =>
|
||||
isMobile &&
|
||||
`
|
||||
padding-bottom: 1rem;
|
||||
`}
|
||||
`;
|
||||
|
||||
const TextFields = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 1em;
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
handleSubmit: (data: FormData) => void;
|
||||
webhookSubscription?: WebhookSubscription;
|
||||
};
|
||||
|
||||
interface FormData {
|
||||
name: string;
|
||||
url: string;
|
||||
secret: string;
|
||||
events: string[];
|
||||
}
|
||||
|
||||
function generateSigningSecret() {
|
||||
return `ol_whs_${randomstring.generate(32)}`;
|
||||
}
|
||||
|
||||
function WebhookSubscriptionForm({ handleSubmit, webhookSubscription }: Props) {
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
register,
|
||||
handleSubmit: formHandleSubmit,
|
||||
formState,
|
||||
watch,
|
||||
setValue,
|
||||
} = useForm<FormData>({
|
||||
mode: "all",
|
||||
defaultValues: {
|
||||
events: webhookSubscription ? [...webhookSubscription.events] : [],
|
||||
name: webhookSubscription?.name,
|
||||
url: webhookSubscription?.url,
|
||||
secret: webhookSubscription?.secret ?? generateSigningSecret(),
|
||||
},
|
||||
});
|
||||
|
||||
const events = watch("events");
|
||||
const selectedGroups = filter(events, (e) => !e.includes("."));
|
||||
const isAllEventSelected = includes(events, "*");
|
||||
const filteredEvents = filter(events, (e) => {
|
||||
const [beforePeriod] = e.split(".");
|
||||
|
||||
return (
|
||||
selectedGroups.length === 0 ||
|
||||
e === beforePeriod ||
|
||||
!selectedGroups.includes(beforePeriod)
|
||||
);
|
||||
});
|
||||
|
||||
const isMobile = useMobile();
|
||||
|
||||
useEffect(() => {
|
||||
if (isAllEventSelected) {
|
||||
setValue("events", ["*"]);
|
||||
}
|
||||
}, [isAllEventSelected, setValue]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEqual(events, filteredEvents)) {
|
||||
setValue("events", filteredEvents);
|
||||
}
|
||||
}, [events, filteredEvents, setValue]);
|
||||
|
||||
const verb = webhookSubscription ? t("Update") : t("Create");
|
||||
const inProgressVerb = webhookSubscription ? t("Updating") : t("Creating");
|
||||
|
||||
function EventCheckbox({ label, value }: { label: string; value: string }) {
|
||||
const LabelComponent =
|
||||
value === "*"
|
||||
? AllEventCheckboxLabel
|
||||
: Object.keys(WEBHOOK_EVENTS).includes(value)
|
||||
? GroupEventCheckboxLabel
|
||||
: EventCheckboxLabel;
|
||||
|
||||
return (
|
||||
<LabelComponent>
|
||||
<input
|
||||
type="checkbox"
|
||||
defaultValue={value}
|
||||
{...register("events", {})}
|
||||
/>
|
||||
<EventCheckboxText>{label}</EventCheckboxText>
|
||||
</LabelComponent>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={formHandleSubmit(handleSubmit)}>
|
||||
<Text type="secondary">
|
||||
<Trans>
|
||||
Provide a descriptive name for this webhook and the URL we should send
|
||||
a POST request to when matching events are created.
|
||||
</Trans>
|
||||
</Text>
|
||||
<TextFields>
|
||||
<Input
|
||||
required
|
||||
autoFocus
|
||||
flex
|
||||
label={t("Name")}
|
||||
placeholder={t("A memorable identifer")}
|
||||
{...register("name", {
|
||||
required: true,
|
||||
})}
|
||||
/>
|
||||
<Input
|
||||
required
|
||||
autoFocus
|
||||
flex
|
||||
pattern="https://.*"
|
||||
placeholder="https://…"
|
||||
label={t("URL")}
|
||||
{...register("url", { required: true })}
|
||||
/>
|
||||
<Input
|
||||
flex
|
||||
spellCheck={false}
|
||||
label={t("Signing secret")}
|
||||
{...register("secret", {
|
||||
required: false,
|
||||
})}
|
||||
/>
|
||||
</TextFields>
|
||||
<Text type="secondary">
|
||||
<Trans>
|
||||
Subscribe to all events, groups, or individual events. We recommend
|
||||
only subscribing to the minimum amount of events that your application
|
||||
needs to function.
|
||||
</Trans>
|
||||
</Text>
|
||||
|
||||
<EventCheckbox label={t("All events")} value="*" />
|
||||
|
||||
<FieldSet disabled={isAllEventSelected}>
|
||||
<GroupGrid isMobile={isMobile}>
|
||||
{Object.entries(WEBHOOK_EVENTS).map(([group, events], i) => (
|
||||
<GroupWrapper key={i} isMobile={isMobile}>
|
||||
<EventCheckbox
|
||||
label={t(`All {{ groupName }} events`, { groupName: group })}
|
||||
value={group}
|
||||
/>
|
||||
<FieldSet disabled={selectedGroups.includes(group)}>
|
||||
{events.map((event) => (
|
||||
<EventCheckbox label={event} value={event} key={event} />
|
||||
))}
|
||||
</FieldSet>
|
||||
</GroupWrapper>
|
||||
))}
|
||||
</GroupGrid>
|
||||
</FieldSet>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={formState.isSubmitting || !formState.isValid}
|
||||
>
|
||||
{formState.isSubmitting ? `${inProgressVerb}…` : verb}
|
||||
</Button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export default WebhookSubscriptionForm;
|
||||
Reference in New Issue
Block a user