mirror of
https://github.com/outline/outline.git
synced 2026-06-13 03:14:59 +03:00
feat: Allow http webhook urls when self-hosting (#12499)
This commit is contained in:
@@ -13,6 +13,7 @@ import Input from "~/components/Input";
|
||||
import Text from "~/components/Text";
|
||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
||||
import useMobile from "~/hooks/useMobile";
|
||||
import isCloudHosted from "~/utils/isCloudHosted";
|
||||
import Flex from "@shared/components/Flex";
|
||||
|
||||
const WEBHOOK_EVENTS = {
|
||||
@@ -153,6 +154,9 @@ function WebhookSubscriptionForm({ handleSubmit, webhookSubscription }: Props) {
|
||||
});
|
||||
|
||||
const events = watch("events");
|
||||
const url = watch("url");
|
||||
const showInsecureUrlWarning =
|
||||
!isCloudHosted && typeof url === "string" && url.startsWith("http://");
|
||||
const selectedGroups = filter(events, (e) => !e.includes("."));
|
||||
const isAllEventSelected = includes(events, "*");
|
||||
const filteredEvents = filter(events, (e) => {
|
||||
@@ -224,9 +228,16 @@ function WebhookSubscriptionForm({ handleSubmit, webhookSubscription }: Props) {
|
||||
<Input
|
||||
required
|
||||
flex
|
||||
pattern="https://.*"
|
||||
pattern={isCloudHosted ? "https://.*" : "https?://.*"}
|
||||
placeholder="https://…"
|
||||
label={t("URL")}
|
||||
error={
|
||||
showInsecureUrlWarning
|
||||
? t(
|
||||
"Webhook delivery over http is insecure, use https if possible"
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
{...register("url", { required: true })}
|
||||
/>
|
||||
<Input
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { z } from "zod";
|
||||
import env from "@server/env";
|
||||
import { WebhookSubscription } from "@server/models";
|
||||
import { BaseSchema } from "@server/routes/api/schema";
|
||||
|
||||
const webhookUrl = z
|
||||
.url()
|
||||
.refine((val) => !env.isCloudHosted || val.startsWith("https://"), {
|
||||
error: "Webhook url must use https",
|
||||
});
|
||||
|
||||
export const WebhookSubscriptionsListSchema = BaseSchema.extend({
|
||||
body: z.object({
|
||||
/** Webhook subscriptions sorting direction */
|
||||
@@ -33,7 +40,7 @@ export type WebhookSubscriptionsListReq = z.infer<
|
||||
export const WebhookSubscriptionsCreateSchema = z.object({
|
||||
body: z.object({
|
||||
name: z.string(),
|
||||
url: z.url(),
|
||||
url: webhookUrl,
|
||||
secret: z.string().optional(),
|
||||
events: z.array(z.string()),
|
||||
}),
|
||||
@@ -47,7 +54,7 @@ export const WebhookSubscriptionsUpdateSchema = z.object({
|
||||
body: z.object({
|
||||
id: z.uuid(),
|
||||
name: z.string(),
|
||||
url: z.url(),
|
||||
url: webhookUrl,
|
||||
secret: z.string().optional(),
|
||||
events: z.array(z.string()),
|
||||
}),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import env from "@server/env";
|
||||
import {
|
||||
buildAdmin,
|
||||
buildUser,
|
||||
@@ -167,6 +168,39 @@ describe("#webhookSubscriptions.create", () => {
|
||||
expect(webhook.secret).toEqual(secret);
|
||||
expect(webhook.enabled).toEqual(true);
|
||||
});
|
||||
|
||||
it("should reject http urls when cloud hosted", async () => {
|
||||
vi.spyOn(env, "isCloudHosted", "get").mockReturnValue(true);
|
||||
|
||||
const user = await buildAdmin();
|
||||
const res = await server.post("/api/webhookSubscriptions.create", user, {
|
||||
body: {
|
||||
name: "Test webhook",
|
||||
url: "http://www.example.com",
|
||||
events: ["comments"],
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(400);
|
||||
});
|
||||
|
||||
it("should allow http urls when not cloud hosted", async () => {
|
||||
vi.spyOn(env, "isCloudHosted", "get").mockReturnValue(false);
|
||||
|
||||
const user = await buildAdmin();
|
||||
const url = "http://www.example.com";
|
||||
const res = await server.post("/api/webhookSubscriptions.create", user, {
|
||||
body: {
|
||||
name: "Test webhook",
|
||||
url,
|
||||
events: ["comments"],
|
||||
},
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.url).toEqual(url);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#webhookSubscriptions.update", () => {
|
||||
|
||||
@@ -1610,6 +1610,7 @@
|
||||
"Provide a descriptive name for this webhook and the URL we should send a POST request to when matching events are created.": "Provide a descriptive name for this webhook and the URL we should send a POST request to when matching events are created.",
|
||||
"A memorable identifer": "A memorable identifer",
|
||||
"URL": "URL",
|
||||
"Webhook delivery over http is insecure, use https if possible": "Webhook delivery over http is insecure, use https if possible",
|
||||
"Signing secret": "Signing secret",
|
||||
"Subscribe to all events, groups, or individual events. We recommend only subscribing to the minimum amount of events that your application needs to function.": "Subscribe to all events, groups, or individual events. We recommend only subscribing to the minimum amount of events that your application needs to function.",
|
||||
"All events": "All events",
|
||||
|
||||
Reference in New Issue
Block a user