diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/LINGYUN.Abp.MicroService.WorkflowService.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/LINGYUN.Abp.MicroService.WorkflowService.csproj
index cf1c97bab..24fea9115 100644
--- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/LINGYUN.Abp.MicroService.WorkflowService.csproj
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/LINGYUN.Abp.MicroService.WorkflowService.csproj
@@ -82,6 +82,7 @@
-->
+
@@ -98,6 +99,8 @@
+
+
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/WorkflowServiceModule.Configure.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/WorkflowServiceModule.Configure.cs
index d2cd66737..8fae4cdf1 100644
--- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/WorkflowServiceModule.Configure.cs
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/WorkflowServiceModule.Configure.cs
@@ -17,6 +17,7 @@ using LINGYUN.Abp.MicroService.WorkflowService.Extensions;
using LINGYUN.Abp.MicroService.WorkflowService.Navigation;
using LINGYUN.Abp.Notifications;
using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
+using LINGYUN.Abp.WebhooksManagement;
using Localization.Resources.AbpUi;
using Medallion.Threading;
using Medallion.Threading.Redis;
@@ -352,6 +353,15 @@ public partial class WorkflowServiceModule
});
}
+ private void ConfigureWebhooksManagement()
+ {
+ Configure(options =>
+ {
+ options.IsDynamicWebhookStoreEnabled = true;
+ options.SaveStaticWebhooksToDatabase = false;
+ });
+ }
+
private void ConfigureMultiTenancy(IConfiguration configuration)
{
// 多租户
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/WorkflowServiceModule.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/WorkflowServiceModule.cs
index 708f3824e..1faded37d 100644
--- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/WorkflowServiceModule.cs
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/WorkflowServiceModule.cs
@@ -19,6 +19,8 @@ using LINGYUN.Abp.ElsaNext.Studio.Diagnostics.StructuredLogs.Blazor;
using LINGYUN.Abp.ElsaNext.Studio.Identity.Blazor;
using LINGYUN.Abp.ElsaNext.Studio.Notifications.Blazor;
using LINGYUN.Abp.ElsaNext.Studio.Saas.Blazor;
+using LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor;
+using LINGYUN.Abp.ElsaNext.Webhooks;
using LINGYUN.Abp.Emailing.Platform;
using LINGYUN.Abp.EventBus.CAP;
using LINGYUN.Abp.ExceptionHandling.Emailing;
@@ -31,6 +33,7 @@ using LINGYUN.Abp.Saas.EntityFrameworkCore;
using LINGYUN.Abp.Serilog.Enrichers.Application;
using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
using LINGYUN.Abp.Sms.Platform;
+using LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
@@ -63,6 +66,7 @@ namespace LINGYUN.Abp.MicroService.WorkflowService;
typeof(AbpElsaNextBlobStoringModule),
typeof(AbpElsaNextEmailModule),
typeof(AbpElsaNextNotificationsModule),
+ typeof(AbpElsaNextWebhooksModule),
typeof(AbpElsaNextServerModule),
typeof(AbpElsaNextStudioBlazorModule),
typeof(AbpElsaNextAgentsBlazorModule),
@@ -73,6 +77,7 @@ namespace LINGYUN.Abp.MicroService.WorkflowService;
typeof(AbpElsaNextStudioIdentityBlazorModule),
typeof(AbpElsaNextStudioNotificationsBlazorModule),
typeof(AbpElsaNextStudioSaasBlazorModule),
+ typeof(AbpElsaNextStudioWebhooksBlazorModule),
typeof(AbpEmailingExceptionHandlingModule),
typeof(AbpHttpClientIdentityModelWebModule),
typeof(AbpAspNetCoreMultiTenancyModule),
@@ -87,6 +92,7 @@ namespace LINGYUN.Abp.MicroService.WorkflowService;
typeof(AbpIdentityEntityFrameworkCoreModule),
typeof(AbpLocalizationManagementEntityFrameworkCoreModule),
typeof(AbpNotificationsEntityFrameworkCoreModule),
+ typeof(WebhooksManagementEntityFrameworkCoreModule),
typeof(AbpAuthorizationOrganizationUnitsModule),
typeof(AbpAspNetCoreAuthenticationJwtBearerModule),
typeof(AbpAspNetCoreAuthenticationOpenIdConnectModule),
@@ -135,6 +141,7 @@ public partial class WorkflowServiceModule : AbpModule
ConfigureVirtualFileSystem();
ConfigureNotificationsManagement();
ConfigurePermissionManagement();
+ ConfigureWebhooksManagement();
ConfigureTiming(configuration);
ConfigureCaching(configuration);
ConfigureAuditing(configuration);
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/AbpElsaNextStudioWebhooksBlazorModule.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/AbpElsaNextStudioWebhooksBlazorModule.cs
new file mode 100644
index 000000000..afff5a6ef
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/AbpElsaNextStudioWebhooksBlazorModule.cs
@@ -0,0 +1,17 @@
+using LINGYUN.Abp.ElsaNext.Studio.Blazor;
+using LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor.Extensions;
+using LINGYUN.Abp.ElsaNext.Webhooks;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor;
+
+[DependsOn(
+ typeof(AbpElsaNextWebhooksModule),
+ typeof(AbpElsaNextStudioBlazorModule))]
+public class AbpElsaNextStudioWebhooksBlazorModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddWebhooksUIHintHandlers();
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/Components/WebhookPicker.razor b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/Components/WebhookPicker.razor
new file mode 100644
index 000000000..66f5f0b5b
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/Components/WebhookPicker.razor
@@ -0,0 +1,130 @@
+@using Elsa.Api.Client.Shared.UIHints.DropDown
+@using Elsa.Studio.Models
+@using LINGYUN.Abp.ElsaNext.Studio.Blazor.Components
+@using LINGYUN.Abp.Webhooks
+@using Microsoft.Extensions.Localization
+@inject ILocalizer Localizer
+
+@inherits StudioComponentBase
+@{
+ var inputDescriptor = EditorContext.InputDescriptor;
+ var displayName = inputDescriptor.DisplayName;
+ var description = inputDescriptor.Description;
+}
+
+
+
+
+
+
+
+@code {
+ private SelectListItem? _selectedItem;
+
+ [Parameter] public DisplayInputEditorContext EditorContext { get; set; } = null!;
+
+ [Inject] protected IDialogService DialogService { get; set; } = null!;
+ [Inject] private IStringLocalizerFactory StringLocalizerFactory { get; set; } = null!;
+ [Inject] private IWebhookDefinitionManager WebhookDefinitionManager { get; set; } = null!;
+
+ protected async override Task OnInitializedAsync()
+ {
+ var selectedItem = await ResolveSelectedItemAsync();
+
+ if (selectedItem != null)
+ {
+ await InvokeAsync(() => _selectedItem = selectedItem);
+ }
+ }
+
+ private async Task ResolveSelectedItemAsync()
+ {
+ var value = EditorContext.GetLiteralValueOrDefault();
+
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ return null;
+ }
+
+ var webhookDefine = await WebhookDefinitionManager.GetOrNullAsync(value);
+ if (webhookDefine != null)
+ {
+ return new SelectListItem(
+ webhookDefine.DisplayName != null
+ ? webhookDefine.DisplayName.Localize(StringLocalizerFactory)
+ : webhookDefine.Name,
+ webhookDefine.Name);
+ }
+
+ return new SelectListItem(value, value);
+ }
+
+ private async Task OpenSelectDialogAsync()
+ {
+ if (EditorContext.IsReadOnly)
+ {
+ return;
+ }
+
+ var inputDescriptor = EditorContext.InputDescriptor;
+ var parameters = new DialogParameters
+ {
+ { x => x.Title, Localizer[inputDescriptor.DisplayName ?? inputDescriptor.Name] },
+ { x => x.SelectedValue, _selectedItem?.Value }
+ };
+ var options = new DialogOptions
+ {
+ CloseOnEscapeKey = true,
+ FullWidth = true,
+ MaxWidth = MaxWidth.Medium
+ };
+
+ var dialog = await DialogService.ShowAsync(string.Empty, parameters, options);
+ var result = await dialog.Result;
+
+ if (result is { Canceled: false, Data: SelectListItem item })
+ {
+ await InvokeAsync(async () =>
+ {
+ _selectedItem = item;
+ await EditorContext.UpdateValueOrLiteralExpressionAsync(item.Value);
+ });
+ }
+ }
+
+ private MudTextFieldExIcon[] GetAdornmentIcons() =>
+ [
+ new()
+ {
+ Icon = Icons.Material.Filled.Search,
+ Tooltip = Localizer["Search"],
+ Disabled = EditorContext.IsReadOnly,
+ OnClick = OpenSelectDialogAsync
+ },
+ new()
+ {
+ Icon = Icons.Material.Filled.Clear,
+ Tooltip = Localizer["Clear"],
+ Visible = _selectedItem != null,
+ Disabled = EditorContext.IsReadOnly,
+ OnClick = ClearAsync
+ }
+ ];
+
+ private async Task ClearAsync()
+ {
+ await InvokeAsync(async () =>
+ {
+ _selectedItem = null;
+ await EditorContext.UpdateValueOrLiteralExpressionAsync(string.Empty);
+ });
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/Components/WebhookSelectDialog.razor b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/Components/WebhookSelectDialog.razor
new file mode 100644
index 000000000..7d3770a97
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/Components/WebhookSelectDialog.razor
@@ -0,0 +1,156 @@
+@using System.Threading
+@using Elsa.Api.Client.Shared.UIHints.DropDown
+@using LINGYUN.Abp.Webhooks
+@using Microsoft.Extensions.Localization
+@inject ILocalizer Localizer
+
+@implements IDisposable
+
+
+
+ @Title
+
+
+
+
+
+
+
+ @Localizer["Name"]
+
+
+
+ @context.Text
+
+ @if (context.Value == SelectedValue)
+ {
+
+ }
+ else
+ {
+
+ @Localizer["Select"]
+
+ }
+
+
+
+
+
+
+ @Localizer["No results found"]
+
+
+
+
+
+ @Localizer["Cancel"]
+
+
+
+@code {
+ private const int DebounceIntervalMilliseconds = 350;
+
+ private MudTable? _table;
+ private string? _searchText;
+ private CancellationTokenSource? _debounceCancellationTokenSource;
+
+ [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!;
+
+ [Parameter] public string Title { get; set; } = string.Empty;
+
+ [Parameter] public string? SelectedValue { get; set; }
+
+ [Inject] private IStringLocalizerFactory StringLocalizerFactory { get; set; } = null!;
+ [Inject] private IWebhookDefinitionManager WebhookDefinitionManager { get; set; } = null!;
+
+ public void Dispose()
+ {
+ _debounceCancellationTokenSource?.Cancel();
+ _debounceCancellationTokenSource?.Dispose();
+ }
+
+ private async Task OnSearchTextChangedAsync(string? searchValue)
+ {
+ _searchText = searchValue;
+
+ _debounceCancellationTokenSource?.Cancel();
+ _debounceCancellationTokenSource?.Dispose();
+ _debounceCancellationTokenSource = new CancellationTokenSource();
+
+ try
+ {
+ await Task.Delay(DebounceIntervalMilliseconds, _debounceCancellationTokenSource.Token);
+
+ var table = _table;
+
+ if (table != null)
+ {
+ await InvokeAsync(() => table.ReloadServerData());
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ }
+
+ private async Task> LoadTenantsAsync(TableState state, CancellationToken cancellationToken)
+ {
+ var filter = string.IsNullOrWhiteSpace(_searchText) ? null : _searchText.Trim();
+
+ var webhooks = await WebhookDefinitionManager.GetWebhooksAsync();
+
+ var items = webhooks
+ .Select(webhook => new SelectListItem(
+ webhook.DisplayName != null
+ ? webhook.DisplayName.Localize(StringLocalizerFactory)
+ : webhook.Name,
+ webhook.Name))
+ .WhereIf(!string.IsNullOrWhiteSpace(filter), x => x.Text.Contains(filter!) || x.Value.Contains(filter!))
+ .OrderBy(x => x.Text)
+ .Skip(state.Page * state.PageSize)
+ .Take(state.PageSize)
+ .ToList();
+
+ return new TableData
+ {
+ Items = items,
+ TotalItems = webhooks.Count,
+ };
+ }
+
+ private async Task OnRowClickAsync(TableRowClickEventArgs args)
+ {
+ await SelectAsync(args.Item);
+ }
+
+ private Task SelectAsync(SelectListItem? item)
+ {
+ if (item == null)
+ {
+ return Task.CompletedTask;
+ }
+
+ MudDialog.Close(DialogResult.Ok(item));
+ return Task.CompletedTask;
+ }
+
+ private void CancelAsync()
+ {
+ MudDialog.Cancel();
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/Extensions/ServiceCollectionExtensions.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/Extensions/ServiceCollectionExtensions.cs
new file mode 100644
index 000000000..69b9a5395
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/Extensions/ServiceCollectionExtensions.cs
@@ -0,0 +1,13 @@
+using Elsa.Studio.Extensions;
+using LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor.Handlers;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor.Extensions;
+
+public static class ServiceCollectionExtensions
+{
+ public static IServiceCollection AddWebhooksUIHintHandlers(this IServiceCollection services)
+ {
+ return services.AddUIHintHandler();
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/FodyWeavers.xml b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/FodyWeavers.xml
new file mode 100644
index 000000000..5d6962159
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/FodyWeavers.xsd b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/Handlers/WebhookPickerHandler.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/Handlers/WebhookPickerHandler.cs
new file mode 100644
index 000000000..b48cb92ef
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/Handlers/WebhookPickerHandler.cs
@@ -0,0 +1,25 @@
+using Elsa.Studio;
+using Elsa.Studio.Contracts;
+using Elsa.Studio.Models;
+using LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor.Components;
+using LINGYUN.Abp.ElsaNext.Webhooks.UIHints;
+using Microsoft.AspNetCore.Components;
+
+namespace LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor.Handlers;
+
+public class WebhookPickerHandler : IUIHintHandler
+{
+ public bool GetSupportsUIHint(string uiHint) => uiHint is WebhooksUIHints.WebhookPicker;
+
+ public string UISyntax => WellKnownSyntaxNames.Object;
+
+ public RenderFragment DisplayInputEditor(DisplayInputEditorContext context)
+ {
+ return builder =>
+ {
+ builder.OpenComponent(0, typeof(WebhookPicker));
+ builder.AddAttribute(1, nameof(WebhookPicker.EditorContext), context);
+ builder.CloseComponent();
+ };
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor.csproj b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor.csproj
new file mode 100644
index 000000000..8ae04f812
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor.csproj
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ net10.0
+ LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor
+ LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor
+ false
+ false
+ false
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/_Imports.razor b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/_Imports.razor
new file mode 100644
index 000000000..1f6d130e9
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.Webhooks.Blazor/_Imports.razor
@@ -0,0 +1,5 @@
+@using Microsoft.AspNetCore.Components.Web
+@using Elsa.Studio.Components
+@using Elsa.Studio.Localization
+@using MudBlazor
+@using MudExtensions
\ No newline at end of file
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Webhooks/FodyWeavers.xml b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Webhooks/FodyWeavers.xml
new file mode 100644
index 000000000..5d6962159
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Webhooks/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Webhooks/FodyWeavers.xsd b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Webhooks/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Webhooks/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Webhooks/LINGYUN.Abp.ElsaNext.Webhooks.csproj b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Webhooks/LINGYUN.Abp.ElsaNext.Webhooks.csproj
new file mode 100644
index 000000000..5b370bf52
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Webhooks/LINGYUN.Abp.ElsaNext.Webhooks.csproj
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+ net10.0
+ LINGYUN.Abp.ElsaNext.Webhooks
+ LINGYUN.Abp.ElsaNext.Webhooks
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Webhooks/LINGYUN/Abp/ElsaNext/Webhooks/AbpElsaNextWebhooksModule.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Webhooks/LINGYUN/Abp/ElsaNext/Webhooks/AbpElsaNextWebhooksModule.cs
new file mode 100644
index 000000000..2ee5a3bf0
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Webhooks/LINGYUN/Abp/ElsaNext/Webhooks/AbpElsaNextWebhooksModule.cs
@@ -0,0 +1,38 @@
+using Elsa.Features.Services;
+using LINGYUN.Abp.ElsaNext.Localization;
+using LINGYUN.Abp.ElsaNext.Webhooks.Extensions;
+using LINGYUN.Abp.Webhooks;
+using Volo.Abp.Localization;
+using Volo.Abp.Modularity;
+using Volo.Abp.VirtualFileSystem;
+
+namespace LINGYUN.Abp.ElsaNext.Webhooks;
+
+[DependsOn(
+ typeof(AbpWebhooksModule),
+ typeof(AbpElsaNextModule))]
+public class AbpElsaNextWebhooksModule : AbpModule
+{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ PreConfigure(elsa =>
+ {
+ elsa.UseAbpWebhooks();
+ });
+ }
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+
+ Configure(options =>
+ {
+ options.Resources
+ .Get()
+ .AddVirtualJson("/LINGYUN/Abp/ElsaNext/Webhooks/Localization/Resources");
+ });
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Webhooks/LINGYUN/Abp/ElsaNext/Webhooks/Activities/PublishWebhook.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Webhooks/LINGYUN/Abp/ElsaNext/Webhooks/Activities/PublishWebhook.cs
new file mode 100644
index 000000000..42671e57f
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Webhooks/LINGYUN/Abp/ElsaNext/Webhooks/Activities/PublishWebhook.cs
@@ -0,0 +1,60 @@
+using Elsa.Extensions;
+using Elsa.Workflows;
+using Elsa.Workflows.Attributes;
+using Elsa.Workflows.Models;
+using Elsa.Workflows.UIHints;
+using LINGYUN.Abp.ElsaNext.Webhooks.UIHints;
+using LINGYUN.Abp.Webhooks;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.ElsaNext.Webhooks.Activities;
+
+[Activity("Elsa", "Webhooks", "Send webhooks to subscriptions.", Kind = ActivityKind.Task)]
+public class PublishWebhook : CodeActivity
+{
+ [Input(
+ Description = "Unique name of the webhook.",
+ UIHint = WebhooksUIHints.WebhookPicker)]
+ public Input WebhookName { get; set; } = default!;
+
+ [Input(Description = "Data to send.")]
+ public Input