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:
@@ -10,7 +10,6 @@ import {
|
|||||||
TeamIcon,
|
TeamIcon,
|
||||||
BeakerIcon,
|
BeakerIcon,
|
||||||
BuildingBlocksIcon,
|
BuildingBlocksIcon,
|
||||||
WebhooksIcon,
|
|
||||||
SettingsIcon,
|
SettingsIcon,
|
||||||
ExportIcon,
|
ExportIcon,
|
||||||
ImportIcon,
|
ImportIcon,
|
||||||
@@ -32,7 +31,6 @@ import Security from "~/scenes/Settings/Security";
|
|||||||
import SelfHosted from "~/scenes/Settings/SelfHosted";
|
import SelfHosted from "~/scenes/Settings/SelfHosted";
|
||||||
import Shares from "~/scenes/Settings/Shares";
|
import Shares from "~/scenes/Settings/Shares";
|
||||||
import Tokens from "~/scenes/Settings/Tokens";
|
import Tokens from "~/scenes/Settings/Tokens";
|
||||||
import Webhooks from "~/scenes/Settings/Webhooks";
|
|
||||||
import Zapier from "~/scenes/Settings/Zapier";
|
import Zapier from "~/scenes/Settings/Zapier";
|
||||||
import GoogleIcon from "~/components/Icons/GoogleIcon";
|
import GoogleIcon from "~/components/Icons/GoogleIcon";
|
||||||
import ZapierIcon from "~/components/Icons/ZapierIcon";
|
import ZapierIcon from "~/components/Icons/ZapierIcon";
|
||||||
@@ -172,14 +170,6 @@ const useSettingsConfig = () => {
|
|||||||
icon: plugin.icon,
|
icon: plugin.icon,
|
||||||
} as ConfigItem;
|
} as ConfigItem;
|
||||||
}),
|
}),
|
||||||
Webhooks: {
|
|
||||||
name: t("Webhooks"),
|
|
||||||
path: "/settings/webhooks",
|
|
||||||
component: Webhooks,
|
|
||||||
enabled: can.createWebhookSubscription,
|
|
||||||
group: t("Integrations"),
|
|
||||||
icon: WebhooksIcon,
|
|
||||||
},
|
|
||||||
SelfHosted: {
|
SelfHosted: {
|
||||||
name: t("Self Hosted"),
|
name: t("Self Hosted"),
|
||||||
path: integrationSettingsPath("self-hosted"),
|
path: integrationSettingsPath("self-hosted"),
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
"start": "node ./build/server/index.js",
|
"start": "node ./build/server/index.js",
|
||||||
"dev": "NODE_ENV=development yarn concurrently -n api,collaboration -c \"blue,magenta\" \"node --inspect=0.0.0.0 build/server/index.js --services=collaboration,websockets,admin,web,worker\"",
|
"dev": "NODE_ENV=development yarn concurrently -n api,collaboration -c \"blue,magenta\" \"node --inspect=0.0.0.0 build/server/index.js --services=collaboration,websockets,admin,web,worker\"",
|
||||||
"dev:watch": "nodemon --exec \"yarn build:server && yarn dev\" -e js,ts,tsx --ignore build/ --ignore app/ --ignore shared/editor",
|
"dev:watch": "nodemon --exec \"yarn build:server && yarn dev\" -e js,ts,tsx --ignore build/ --ignore app/ --ignore shared/editor",
|
||||||
"lint": "eslint app server shared",
|
"lint": "eslint app server shared plugins",
|
||||||
"prepare": "husky install",
|
"prepare": "husky install",
|
||||||
"postinstall": "rimraf node_modules/prosemirror-view/dist/index.d.ts",
|
"postinstall": "rimraf node_modules/prosemirror-view/dist/index.d.ts",
|
||||||
"heroku-postbuild": "yarn build && yarn db:migrate",
|
"heroku-postbuild": "yarn build && yarn db:migrate",
|
||||||
|
|||||||
1
plugins/webhooks/client/Icon.tsx
Normal file
1
plugins/webhooks/client/Icon.tsx
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { WebhooksIcon as default } from "outline-icons";
|
||||||
5
plugins/webhooks/plugin.json
Normal file
5
plugins/webhooks/plugin.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "Webhooks",
|
||||||
|
"description": "Adds HTTP webhooks for various events.",
|
||||||
|
"requiredEnvVars": []
|
||||||
|
}
|
||||||
3
plugins/webhooks/server/.babelrc
Normal file
3
plugins/webhooks/server/.babelrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../../server/.babelrc"
|
||||||
|
}
|
||||||
@@ -4,10 +4,10 @@ import { ValidationError } from "@server/errors";
|
|||||||
import auth from "@server/middlewares/authentication";
|
import auth from "@server/middlewares/authentication";
|
||||||
import { WebhookSubscription, Event } from "@server/models";
|
import { WebhookSubscription, Event } from "@server/models";
|
||||||
import { authorize } from "@server/policies";
|
import { authorize } from "@server/policies";
|
||||||
import { presentWebhookSubscription } from "@server/presenters";
|
import pagination from "@server/routes/api/middlewares/pagination";
|
||||||
import { WebhookSubscriptionEvent, APIContext } from "@server/types";
|
import { WebhookSubscriptionEvent, APIContext } from "@server/types";
|
||||||
import { assertArray, assertPresent, assertUuid } from "@server/validation";
|
import { assertArray, assertPresent, assertUuid } from "@server/validation";
|
||||||
import pagination from "./middlewares/pagination";
|
import presentWebhookSubscription from "../presenters/webhookSubscription";
|
||||||
|
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { WebhookSubscription } from "@server/models";
|
import { WebhookSubscription } from "@server/models";
|
||||||
|
import BaseProcessor from "@server/queues/processors/BaseProcessor";
|
||||||
import { Event } from "@server/types";
|
import { Event } from "@server/types";
|
||||||
import DeliverWebhookTask from "../tasks/DeliverWebhookTask";
|
import DeliverWebhookTask from "../tasks/DeliverWebhookTask";
|
||||||
import BaseProcessor from "./BaseProcessor";
|
|
||||||
|
|
||||||
export default class WebhookProcessor extends BaseProcessor {
|
export default class WebhookProcessor extends BaseProcessor {
|
||||||
static applicableEvents: ["*"] = ["*"];
|
static applicableEvents: ["*"] = ["*"];
|
||||||
@@ -2,11 +2,16 @@ import { subDays } from "date-fns";
|
|||||||
import { Op } from "sequelize";
|
import { Op } from "sequelize";
|
||||||
import Logger from "@server/logging/Logger";
|
import Logger from "@server/logging/Logger";
|
||||||
import { WebhookDelivery } from "@server/models";
|
import { WebhookDelivery } from "@server/models";
|
||||||
import BaseTask, { TaskPriority } from "./BaseTask";
|
import BaseTask, {
|
||||||
|
TaskPriority,
|
||||||
|
TaskSchedule,
|
||||||
|
} from "@server/queues/tasks/BaseTask";
|
||||||
|
|
||||||
type Props = void;
|
type Props = void;
|
||||||
|
|
||||||
export default class CleanupWebhookDeliveriesTask extends BaseTask<Props> {
|
export default class CleanupWebhookDeliveriesTask extends BaseTask<Props> {
|
||||||
|
static cron = TaskSchedule.Daily;
|
||||||
|
|
||||||
public async perform(_: Props) {
|
public async perform(_: Props) {
|
||||||
Logger.info("task", `Deleting WebhookDeliveries older than one week…`);
|
Logger.info("task", `Deleting WebhookDeliveries older than one week…`);
|
||||||
const count = await WebhookDelivery.unscoped().destroy({
|
const count = await WebhookDelivery.unscoped().destroy({
|
||||||
@@ -34,15 +34,13 @@ import {
|
|||||||
presentStar,
|
presentStar,
|
||||||
presentTeam,
|
presentTeam,
|
||||||
presentUser,
|
presentUser,
|
||||||
presentWebhook,
|
|
||||||
presentWebhookSubscription,
|
|
||||||
presentView,
|
presentView,
|
||||||
presentShare,
|
presentShare,
|
||||||
presentMembership,
|
presentMembership,
|
||||||
presentGroupMembership,
|
presentGroupMembership,
|
||||||
presentCollectionGroupMembership,
|
presentCollectionGroupMembership,
|
||||||
} from "@server/presenters";
|
} from "@server/presenters";
|
||||||
import { WebhookPayload } from "@server/presenters/webhook";
|
import BaseTask from "@server/queues/tasks/BaseTask";
|
||||||
import {
|
import {
|
||||||
CollectionEvent,
|
CollectionEvent,
|
||||||
CollectionGroupEvent,
|
CollectionGroupEvent,
|
||||||
@@ -62,7 +60,8 @@ import {
|
|||||||
ViewEvent,
|
ViewEvent,
|
||||||
WebhookSubscriptionEvent,
|
WebhookSubscriptionEvent,
|
||||||
} from "@server/types";
|
} from "@server/types";
|
||||||
import BaseTask from "./BaseTask";
|
import presentWebhook, { WebhookPayload } from "../presenters/webhook";
|
||||||
|
import presentWebhookSubscription from "../presenters/webhookSubscription";
|
||||||
|
|
||||||
function assertUnreachable(event: never) {
|
function assertUnreachable(event: never) {
|
||||||
Logger.warn(`DeliverWebhookTask did not handle ${(event as any).name}`);
|
Logger.warn(`DeliverWebhookTask did not handle ${(event as any).name}`);
|
||||||
@@ -23,8 +23,6 @@ import presentSubscription from "./subscription";
|
|||||||
import presentTeam from "./team";
|
import presentTeam from "./team";
|
||||||
import presentUser from "./user";
|
import presentUser from "./user";
|
||||||
import presentView from "./view";
|
import presentView from "./view";
|
||||||
import presentWebhook from "./webhook";
|
|
||||||
import presentWebhookSubscription from "./webhookSubscription";
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
presentApiKey,
|
presentApiKey,
|
||||||
@@ -52,6 +50,4 @@ export {
|
|||||||
presentTeam,
|
presentTeam,
|
||||||
presentUser,
|
presentUser,
|
||||||
presentView,
|
presentView,
|
||||||
presentWebhook,
|
|
||||||
presentWebhookSubscription,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,17 @@ export enum TaskPriority {
|
|||||||
High = 10,
|
High = 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum TaskSchedule {
|
||||||
|
Daily = "daily",
|
||||||
|
Hourly = "hourly",
|
||||||
|
}
|
||||||
|
|
||||||
export default abstract class BaseTask<T> {
|
export default abstract class BaseTask<T> {
|
||||||
|
/**
|
||||||
|
* An optional schedule for this task to be run automatically.
|
||||||
|
*/
|
||||||
|
static cron: TaskSchedule | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedule this task type to be processed asyncronously by a worker.
|
* Schedule this task type to be processed asyncronously by a worker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -3,13 +3,15 @@ import { Op } from "sequelize";
|
|||||||
import documentPermanentDeleter from "@server/commands/documentPermanentDeleter";
|
import documentPermanentDeleter from "@server/commands/documentPermanentDeleter";
|
||||||
import Logger from "@server/logging/Logger";
|
import Logger from "@server/logging/Logger";
|
||||||
import { Document } from "@server/models";
|
import { Document } from "@server/models";
|
||||||
import BaseTask, { TaskPriority } from "./BaseTask";
|
import BaseTask, { TaskPriority, TaskSchedule } from "./BaseTask";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
limit: number;
|
limit: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class CleanupDeletedDocumentsTask extends BaseTask<Props> {
|
export default class CleanupDeletedDocumentsTask extends BaseTask<Props> {
|
||||||
|
static cron = TaskSchedule.Daily;
|
||||||
|
|
||||||
public async perform({ limit }: Props) {
|
public async perform({ limit }: Props) {
|
||||||
Logger.info(
|
Logger.info(
|
||||||
"task",
|
"task",
|
||||||
|
|||||||
@@ -3,13 +3,15 @@ import { Op } from "sequelize";
|
|||||||
import teamPermanentDeleter from "@server/commands/teamPermanentDeleter";
|
import teamPermanentDeleter from "@server/commands/teamPermanentDeleter";
|
||||||
import Logger from "@server/logging/Logger";
|
import Logger from "@server/logging/Logger";
|
||||||
import { Team } from "@server/models";
|
import { Team } from "@server/models";
|
||||||
import BaseTask, { TaskPriority } from "./BaseTask";
|
import BaseTask, { TaskPriority, TaskSchedule } from "./BaseTask";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
limit: number;
|
limit: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class CleanupDeletedTeamsTask extends BaseTask<Props> {
|
export default class CleanupDeletedTeamsTask extends BaseTask<Props> {
|
||||||
|
static cron = TaskSchedule.Daily;
|
||||||
|
|
||||||
public async perform({ limit }: Props) {
|
public async perform({ limit }: Props) {
|
||||||
Logger.info(
|
Logger.info(
|
||||||
"task",
|
"task",
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { Op } from "sequelize";
|
import { Op } from "sequelize";
|
||||||
import Logger from "@server/logging/Logger";
|
import Logger from "@server/logging/Logger";
|
||||||
import { Attachment } from "@server/models";
|
import { Attachment } from "@server/models";
|
||||||
import BaseTask, { TaskPriority } from "./BaseTask";
|
import BaseTask, { TaskPriority, TaskSchedule } from "./BaseTask";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
limit: number;
|
limit: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class CleanupExpiredAttachmentsTask extends BaseTask<Props> {
|
export default class CleanupExpiredAttachmentsTask extends BaseTask<Props> {
|
||||||
|
static cron = TaskSchedule.Daily;
|
||||||
|
|
||||||
public async perform({ limit }: Props) {
|
public async perform({ limit }: Props) {
|
||||||
Logger.info("task", `Deleting expired attachments…`);
|
Logger.info("task", `Deleting expired attachments…`);
|
||||||
const attachments = await Attachment.unscoped().findAll({
|
const attachments = await Attachment.unscoped().findAll({
|
||||||
|
|||||||
@@ -3,13 +3,15 @@ import { Op } from "sequelize";
|
|||||||
import { FileOperationState } from "@shared/types";
|
import { FileOperationState } from "@shared/types";
|
||||||
import Logger from "@server/logging/Logger";
|
import Logger from "@server/logging/Logger";
|
||||||
import { FileOperation } from "@server/models";
|
import { FileOperation } from "@server/models";
|
||||||
import BaseTask, { TaskPriority } from "./BaseTask";
|
import BaseTask, { TaskPriority, TaskSchedule } from "./BaseTask";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
limit: number;
|
limit: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class CleanupExpiredFileOperationsTask extends BaseTask<Props> {
|
export default class CleanupExpiredFileOperationsTask extends BaseTask<Props> {
|
||||||
|
static cron = TaskSchedule.Daily;
|
||||||
|
|
||||||
public async perform({ limit }: Props) {
|
public async perform({ limit }: Props) {
|
||||||
Logger.info("task", `Expiring file operations older than 15 days…`);
|
Logger.info("task", `Expiring file operations older than 15 days…`);
|
||||||
const fileOperations = await FileOperation.unscoped().findAll({
|
const fileOperations = await FileOperation.unscoped().findAll({
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import { sequelize } from "@server/database/sequelize";
|
|||||||
import InviteReminderEmail from "@server/emails/templates/InviteReminderEmail";
|
import InviteReminderEmail from "@server/emails/templates/InviteReminderEmail";
|
||||||
import { User } from "@server/models";
|
import { User } from "@server/models";
|
||||||
import { UserFlag } from "@server/models/User";
|
import { UserFlag } from "@server/models/User";
|
||||||
import BaseTask, { TaskPriority } from "./BaseTask";
|
import BaseTask, { TaskPriority, TaskSchedule } from "./BaseTask";
|
||||||
|
|
||||||
type Props = undefined;
|
type Props = undefined;
|
||||||
|
|
||||||
export default class InviteReminderTask extends BaseTask<Props> {
|
export default class InviteReminderTask extends BaseTask<Props> {
|
||||||
|
static cron = TaskSchedule.Daily;
|
||||||
|
|
||||||
public async perform() {
|
public async perform() {
|
||||||
const users = await User.scope("invited").findAll({
|
const users = await User.scope("invited").findAll({
|
||||||
attributes: ["id"],
|
attributes: ["id"],
|
||||||
|
|||||||
@@ -3,12 +3,7 @@ import { Context } from "koa";
|
|||||||
import Router from "koa-router";
|
import Router from "koa-router";
|
||||||
import env from "@server/env";
|
import env from "@server/env";
|
||||||
import { AuthenticationError } from "@server/errors";
|
import { AuthenticationError } from "@server/errors";
|
||||||
import CleanupDeletedDocumentsTask from "@server/queues/tasks/CleanupDeletedDocumentsTask";
|
import tasks from "@server/queues/tasks";
|
||||||
import CleanupDeletedTeamsTask from "@server/queues/tasks/CleanupDeletedTeamsTask";
|
|
||||||
import CleanupExpiredAttachmentsTask from "@server/queues/tasks/CleanupExpiredAttachmentsTask";
|
|
||||||
import CleanupExpiredFileOperationsTask from "@server/queues/tasks/CleanupExpiredFileOperationsTask";
|
|
||||||
import CleanupWebhookDeliveriesTask from "@server/queues/tasks/CleanupWebhookDeliveriesTask";
|
|
||||||
import InviteReminderTask from "@server/queues/tasks/InviteReminderTask";
|
|
||||||
|
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
|
|
||||||
@@ -34,17 +29,12 @@ const cronHandler = async (ctx: Context) => {
|
|||||||
throw AuthenticationError("Invalid secret token");
|
throw AuthenticationError("Invalid secret token");
|
||||||
}
|
}
|
||||||
|
|
||||||
await CleanupDeletedDocumentsTask.schedule({ limit });
|
for (const name in tasks) {
|
||||||
|
const TaskClass = tasks[name];
|
||||||
await CleanupExpiredFileOperationsTask.schedule({ limit });
|
if (TaskClass.cron) {
|
||||||
|
await TaskClass.schedule({ limit });
|
||||||
await CleanupExpiredAttachmentsTask.schedule({ limit });
|
}
|
||||||
|
}
|
||||||
await CleanupDeletedTeamsTask.schedule({ limit });
|
|
||||||
|
|
||||||
await CleanupWebhookDeliveriesTask.schedule({ limit });
|
|
||||||
|
|
||||||
await InviteReminderTask.schedule();
|
|
||||||
|
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import subscriptions from "./subscriptions";
|
|||||||
import team from "./team";
|
import team from "./team";
|
||||||
import users from "./users";
|
import users from "./users";
|
||||||
import views from "./views";
|
import views from "./views";
|
||||||
import webhookSubscriptions from "./webhookSubscriptions";
|
|
||||||
|
|
||||||
const api = new Koa<AppState, AppContext>();
|
const api = new Koa<AppState, AppContext>();
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
@@ -82,7 +81,6 @@ router.use("/", attachments.routes());
|
|||||||
router.use("/", utils.routes());
|
router.use("/", utils.routes());
|
||||||
router.use("/", groups.routes());
|
router.use("/", groups.routes());
|
||||||
router.use("/", fileOperationsRoute.routes());
|
router.use("/", fileOperationsRoute.routes());
|
||||||
router.use("/", webhookSubscriptions.routes());
|
|
||||||
|
|
||||||
if (env.ENVIRONMENT === "development") {
|
if (env.ENVIRONMENT === "development") {
|
||||||
router.use("/", developer.routes());
|
router.use("/", developer.routes());
|
||||||
|
|||||||
@@ -313,7 +313,6 @@
|
|||||||
"Shared Links": "Shared Links",
|
"Shared Links": "Shared Links",
|
||||||
"Import": "Import",
|
"Import": "Import",
|
||||||
"Integrations": "Integrations",
|
"Integrations": "Integrations",
|
||||||
"Webhooks": "Webhooks",
|
|
||||||
"Self Hosted": "Self Hosted",
|
"Self Hosted": "Self Hosted",
|
||||||
"Google Analytics": "Google Analytics",
|
"Google Analytics": "Google Analytics",
|
||||||
"Show path to document": "Show path to document",
|
"Show path to document": "Show path to document",
|
||||||
@@ -671,22 +670,6 @@
|
|||||||
"Active": "Active",
|
"Active": "Active",
|
||||||
"Everyone": "Everyone",
|
"Everyone": "Everyone",
|
||||||
"Admins": "Admins",
|
"Admins": "Admins",
|
||||||
"Are you sure you want to delete the {{ name }} webhook?": "Are you sure you want to delete the {{ name }} webhook?",
|
|
||||||
"Webhook updated": "Webhook updated",
|
|
||||||
"Update": "Update",
|
|
||||||
"Updating": "Updating",
|
|
||||||
"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",
|
|
||||||
"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",
|
|
||||||
"All {{ groupName }} events": "All {{ groupName }} events",
|
|
||||||
"Delete webhook": "Delete webhook",
|
|
||||||
"Disabled": "Disabled",
|
|
||||||
"Subscribed events": "Subscribed events",
|
|
||||||
"Edit webhook": "Edit webhook",
|
|
||||||
"Webhook created": "Webhook created",
|
|
||||||
"Settings saved": "Settings saved",
|
"Settings saved": "Settings saved",
|
||||||
"Logo updated": "Logo updated",
|
"Logo updated": "Logo updated",
|
||||||
"Unable to upload new logo": "Unable to upload new logo",
|
"Unable to upload new logo": "Unable to upload new logo",
|
||||||
@@ -770,6 +753,7 @@
|
|||||||
"Settings that impact the access, security, and content of your knowledge base.": "Settings that impact the access, security, and content of your knowledge base.",
|
"Settings that impact the access, security, and content of your knowledge base.": "Settings that impact the access, security, and content of your knowledge base.",
|
||||||
"Allow members to sign-in with {{ authProvider }}": "Allow members to sign-in with {{ authProvider }}",
|
"Allow members to sign-in with {{ authProvider }}": "Allow members to sign-in with {{ authProvider }}",
|
||||||
"Connected": "Connected",
|
"Connected": "Connected",
|
||||||
|
"Disabled": "Disabled",
|
||||||
"Allow members to sign-in using their email address": "Allow members to sign-in using their email address",
|
"Allow members to sign-in using their email address": "Allow members to sign-in using their email address",
|
||||||
"The server must have SMTP configured to enable this setting": "The server must have SMTP configured to enable this setting",
|
"The server must have SMTP configured to enable this setting": "The server must have SMTP configured to enable this setting",
|
||||||
"Access": "Access",
|
"Access": "Access",
|
||||||
@@ -793,9 +777,6 @@
|
|||||||
"You can create an unlimited amount of personal tokens to authenticate\n with the API. Tokens have the same permissions as your user account.\n For more details see the <em>developer documentation</em>.": "You can create an unlimited amount of personal tokens to authenticate\n with the API. Tokens have the same permissions as your user account.\n For more details see the <em>developer documentation</em>.",
|
"You can create an unlimited amount of personal tokens to authenticate\n with the API. Tokens have the same permissions as your user account.\n For more details see the <em>developer documentation</em>.": "You can create an unlimited amount of personal tokens to authenticate\n with the API. Tokens have the same permissions as your user account.\n For more details see the <em>developer documentation</em>.",
|
||||||
"Tokens": "Tokens",
|
"Tokens": "Tokens",
|
||||||
"Create a token": "Create a token",
|
"Create a token": "Create a token",
|
||||||
"New webhook": "New webhook",
|
|
||||||
"Webhooks can be used to notify your application when events happen in {{appName}}. Events are sent as a https request with a JSON payload in near real-time.": "Webhooks can be used to notify your application when events happen in {{appName}}. Events are sent as a https request with a JSON payload in near real-time.",
|
|
||||||
"Create a webhook": "Create a webhook",
|
|
||||||
"Zapier is a platform that allows {{appName}} to easily integrate with thousands of other business tools. Automate your workflows, sync data, and more.": "Zapier is a platform that allows {{appName}} to easily integrate with thousands of other business tools. Automate your workflows, sync data, and more.",
|
"Zapier is a platform that allows {{appName}} to easily integrate with thousands of other business tools. Automate your workflows, sync data, and more.": "Zapier is a platform that allows {{appName}} to easily integrate with thousands of other business tools. Automate your workflows, sync data, and more.",
|
||||||
"Your are creating a new workspace using your current account — <em>{{email}}</em>": "Your are creating a new workspace using your current account — <em>{{email}}</em>",
|
"Your are creating a new workspace using your current account — <em>{{email}}</em>": "Your are creating a new workspace using your current account — <em>{{email}}</em>",
|
||||||
"Workspace name": "Workspace name",
|
"Workspace name": "Workspace name",
|
||||||
@@ -838,5 +819,24 @@
|
|||||||
"Post to Channel": "Post to Channel",
|
"Post to Channel": "Post to Channel",
|
||||||
"This is what we found for \"{{ term }}\"": "This is what we found for \"{{ term }}\"",
|
"This is what we found for \"{{ term }}\"": "This is what we found for \"{{ term }}\"",
|
||||||
"No results for \"{{ term }}\"": "No results for \"{{ term }}\"",
|
"No results for \"{{ term }}\"": "No results for \"{{ term }}\"",
|
||||||
|
"Are you sure you want to delete the {{ name }} webhook?": "Are you sure you want to delete the {{ name }} webhook?",
|
||||||
|
"Webhook updated": "Webhook updated",
|
||||||
|
"Update": "Update",
|
||||||
|
"Updating": "Updating",
|
||||||
|
"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",
|
||||||
|
"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",
|
||||||
|
"All {{ groupName }} events": "All {{ groupName }} events",
|
||||||
|
"Delete webhook": "Delete webhook",
|
||||||
|
"Subscribed events": "Subscribed events",
|
||||||
|
"Edit webhook": "Edit webhook",
|
||||||
|
"Webhook created": "Webhook created",
|
||||||
|
"Webhooks": "Webhooks",
|
||||||
|
"New webhook": "New webhook",
|
||||||
|
"Webhooks can be used to notify your application when events happen in {{appName}}. Events are sent as a https request with a JSON payload in near real-time.": "Webhooks can be used to notify your application when events happen in {{appName}}. Events are sent as a https request with a JSON payload in near real-time.",
|
||||||
|
"Create a webhook": "Create a webhook",
|
||||||
"Uploading": "Uploading"
|
"Uploading": "Uploading"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user