From fef1eb61ebc4e19f6818e213091cf98ad92d4962 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Fri, 25 Mar 2022 18:23:02 +0800 Subject: [PATCH] feat(webhooks): added support webhooks. --- aspnet-core/LINGYUN.MicroService.All.sln | 10 + aspnet-core/LINGYUN.MicroService.Common.sln | 10 + .../LINGYUN.Abp.WebHooks/FodyWeavers.xml | 3 + .../LINGYUN.Abp.WebHooks/FodyWeavers.xsd | 30 ++ .../LINGYUN.Abp.WebHooks.csproj | 18 + .../LINGYUN/Abp/Webhooks/AbpWebhooksModule.cs | 18 + .../Abp/Webhooks/AbpWebhooksOptions.cs | 26 ++ .../BackgroundWorker/WebhookSenderJob.cs | 125 +++++++ .../Abp/Webhooks/DefaultWebhookPublisher.cs | 125 +++++++ .../Abp/Webhooks/DefaultWebhookSender.cs | 133 ++++++++ .../Abp/Webhooks/IWebhookDefinitionContext.cs | 22 ++ .../Abp/Webhooks/IWebhookDefinitionManager.cs | 31 ++ .../Abp/Webhooks/IWebhookEventStore.cs | 18 + .../LINGYUN/Abp/Webhooks/IWebhookManager.cs | 22 ++ .../LINGYUN/Abp/Webhooks/IWebhookPublisher.cs | 56 ++++ .../Abp/Webhooks/IWebhookSendAttemptStore.cs | 25 ++ .../LINGYUN/Abp/Webhooks/IWebhookSender.cs | 16 + .../Webhooks/IWebhookSubscriptionManager.cs | 74 +++++ .../Webhooks/IWebhookSubscriptionsStore.cs | 83 +++++ .../Abp/Webhooks/NullWebhookEventStore.cs | 24 ++ .../Webhooks/NullWebhookSendAttemptStore.cs | 47 +++ .../Webhooks/NullWebhookSubscriptionsStore.cs | 65 ++++ .../LINGYUN/Abp/Webhooks/WebhookDefinition.cs | 57 ++++ .../Abp/Webhooks/WebhookDefinitionContext.cs | 54 +++ .../Abp/Webhooks/WebhookDefinitionManager.cs | 109 +++++++ .../Abp/Webhooks/WebhookDefinitionProvider.cs | 13 + .../LINGYUN/Abp/Webhooks/WebhookEvent.cs | 30 ++ .../LINGYUN/Abp/Webhooks/WebhookHeader.cs | 18 + .../LINGYUN/Abp/Webhooks/WebhookManager.cs | 84 +++++ .../LINGYUN/Abp/Webhooks/WebhookPayload.cs | 35 ++ .../Abp/Webhooks/WebhookSendAttempt.cs | 39 +++ .../LINGYUN/Abp/Webhooks/WebhookSenderArgs.cs | 67 ++++ .../Abp/Webhooks/WebhookSubscriptionInfo.cs | 60 ++++ .../Webhooks/WebhookSubscriptionManager.cs | 175 ++++++++++ .../System/AbpStringCryptographyExtensions.cs | 13 + .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 ++ ...oksManagement.Application.Contracts.csproj | 21 ++ ...sManagementPermissionDefinitionProvider.cs | 22 ++ .../WebhooksManagementPermissions.cs | 8 + ...ooksManagementFeatureDefinitionProvider.cs | 18 + .../WebhooksManagementFeatureNames.cs | 6 + ...oksManagementApplicationContractsModule.cs | 15 + .../WebhooksManagementRemoteServiceConsts.cs | 7 + .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 ++ ....Abp.WebhooksManagement.Application.csproj | 20 ++ .../WebhooksManagementAppServiceBase.cs | 13 + ...hooksManagementApplicationMapperProfile.cs | 10 + .../WebhooksManagementApplicationModule.cs | 24 ++ .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 ++ ....Abp.WebhooksManagement.Dapr.Client.csproj | 16 + .../WebhooksManagementDaprClientModule.cs | 18 + .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 ++ ...bp.WebhooksManagement.Domain.Shared.csproj | 25 ++ .../Localization/Resources/en.json | 8 + .../Localization/Resources/zh-Hans.json | 8 + .../WebhooksManagementResource.cs | 8 + ...sManagementModuleExtensionConfiguration.cs | 16 + ...ensionConfigurationDictionaryExtensions.cs | 19 ++ ...WebhooksManagementModuleExtensionConsts.cs | 11 + .../WebhooksManagementDomainSharedModule.cs | 32 ++ .../WebhooksManagementErrorCodes.cs | 6 + .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 ++ ...NGYUN.Abp.WebhooksManagement.Domain.csproj | 22 ++ .../DefaultWebhookManager.cs | 70 ++++ .../IWebhookEventRecordRepository.cs | 8 + .../IWebhookSendRecordRepository.cs | 21 ++ .../IWebhookSubscriptionRepository.cs | 8 + ...ooksManagementSettingDefinitionProvider.cs | 11 + .../Settings/WebhooksManagementSettings.cs | 7 + .../WebhooksManagement/WebhookEventRecord.cs | 30 ++ .../WebhooksManagement/WebhookEventStore.cs | 57 ++++ .../WebhookSendAttemptStore.cs | 165 ++++++++++ .../WebhooksManagement/WebhookSendRecord.cs | 50 +++ .../WebhookSendRecordFilter.cs | 21 ++ .../WebhooksManagement/WebhookSubscription.cs | 33 ++ .../WebhookSubscriptionsStore.cs | 55 ++++ .../WebhooksManagementDbProperties.cs | 11 + .../WebhooksManagementDomainMapperProfile.cs | 11 + .../WebhooksManagementDomainModule.cs | 43 +++ .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 ++ ...hooksManagement.EntityFrameworkCore.csproj | 19 ++ .../IWebhooksManagementDbContext.cs | 9 + .../WebhooksManagementDbContext.cs | 21 ++ ...agementDbContextModelCreatingExtensions.cs | 21 ++ ...ooksManagementEfCoreQueryableExtensions.cs | 6 + ...ooksManagementEntityFrameworkCoreModule.cs | 19 ++ ...agementModelBuilderConfigurationOptions.cs | 17 + .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 ++ ...p.WebhooksManagement.HttpApi.Client.csproj | 19 ++ .../WebhooksManagementHttpApiClientModule.cs | 18 + .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 ++ ...GYUN.Abp.WebhooksManagement.HttpApi.csproj | 19 ++ .../WebhooksManagementControllerBase.cs | 12 + .../WebhooksManagementHttpApiModule.cs | 40 +++ .../Dockerfile | 4 + ...Service.TaskManagement.HttpApi.Host.csproj | 2 + .../Controllers/HomeController.cs | 12 + .../WebhooksManagementDataSeederWorker.cs | 21 ++ .../Dockerfile | 15 + .../WebhooksManagementMigrationsDbContext.cs | 20 ++ ...oksManagementMigrationsDbContextFactory.cs | 32 ++ .../EventBus/Handlers/TenantSynchronizer.cs | 69 ++++ ...ice.WebhooksManagement.HttpApi.Host.csproj | 66 ++++ .../Program.cs | 58 ++++ .../Properties/launchSettings.json | 28 ++ .../TenantHeaderParamter.cs | 31 ++ ...ksManagementHttpApiHostModule.Configure.cs | 307 ++++++++++++++++++ ...sManagementHttpApiHostModule.DataSeeder.cs | 15 + .../WebhooksManagementHttpApiHostModule.cs | 118 +++++++ .../appsettings.Development.json | 132 ++++++++ .../appsettings.json | 80 +++++ .../dapr.sh | 1 + 120 files changed, 4184 insertions(+) create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xml create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xsd create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN.Abp.WebHooks.csproj create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksModule.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/BackgroundWorker/WebhookSenderJob.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookPublisher.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookSender.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionContext.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionManager.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookEventStore.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookManager.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookPublisher.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSendAttemptStore.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSender.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionManager.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionsStore.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookEventStore.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSendAttemptStore.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSubscriptionsStore.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinition.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionContext.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionProvider.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookEvent.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookHeader.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookManager.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookPayload.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSendAttempt.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSenderArgs.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionInfo.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionManager.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/System/AbpStringCryptographyExtensions.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xml create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xsd create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissionDefinitionProvider.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissions.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureDefinitionProvider.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureNames.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationContractsModule.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementRemoteServiceConsts.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xml create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xsd create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN.Abp.WebhooksManagement.Application.csproj create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementAppServiceBase.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationMapperProfile.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationModule.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xml create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xsd create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN.Abp.WebhooksManagement.Dapr.Client.csproj create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDaprClientModule.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xml create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xsd create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN.Abp.WebhooksManagement.Domain.Shared.csproj create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/en.json create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/zh-Hans.json create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/WebhooksManagementResource.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfiguration.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfigurationDictionaryExtensions.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConsts.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainSharedModule.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementErrorCodes.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xml create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xsd create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN.Abp.WebhooksManagement.Domain.csproj create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/DefaultWebhookManager.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookEventRecordRepository.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSendRecordRepository.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSubscriptionRepository.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettingDefinitionProvider.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettings.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventRecord.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventStore.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendAttemptStore.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecord.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordFilter.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscription.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionsStore.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDbProperties.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainMapperProfile.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainModule.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xml create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xsd create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore.csproj create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/IWebhooksManagementDbContext.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContext.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContextModelCreatingExtensions.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEfCoreQueryableExtensions.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEntityFrameworkCoreModule.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementModelBuilderConfigurationOptions.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xml create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xsd create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN.Abp.WebhooksManagement.HttpApi.Client.csproj create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementHttpApiClientModule.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xml create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xsd create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN.Abp.WebhooksManagement.HttpApi.csproj create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN/Abp/WebhooksManagement/WebhooksManagementControllerBase.cs create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN/Abp/WebhooksManagement/WebhooksManagementHttpApiModule.cs create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Controllers/HomeController.cs create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/DataSeeder/WebhooksManagementDataSeederWorker.cs create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Dockerfile create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EntityFrameworkCore/WebhooksManagementMigrationsDbContext.cs create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EntityFrameworkCore/WebhooksManagementMigrationsDbContextFactory.cs create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/LY.MicroService.WebhooksManagement.HttpApi.Host.csproj create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Program.cs create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Properties/launchSettings.json create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/TenantHeaderParamter.cs create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.Configure.cs create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.DataSeeder.cs create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.cs create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/appsettings.Development.json create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/appsettings.json create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/dapr.sh diff --git a/aspnet-core/LINGYUN.MicroService.All.sln b/aspnet-core/LINGYUN.MicroService.All.sln index 2d20d15c8..3bda2e147 100644 --- a/aspnet-core/LINGYUN.MicroService.All.sln +++ b/aspnet-core/LINGYUN.MicroService.All.sln @@ -394,6 +394,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Saas.HttpApi.Cl EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.MultiTenancy.Saas", "modules\tenants\LINGYUN.Abp.MultiTenancy.Saas\LINGYUN.Abp.MultiTenancy.Saas.csproj", "{F57594AA-10C2-4DFF-87F6-19F2548099EA}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "webhooks", "webhooks", "{13ACF670-F109-404E-B252-2FA34A4EA061}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WebHooks", "modules\webhooks\LINGYUN.Abp.WebHooks\LINGYUN.Abp.WebHooks.csproj", "{91AE01B1-CC82-40E2-8290-B8A84C6E90D1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1020,6 +1024,10 @@ Global {F57594AA-10C2-4DFF-87F6-19F2548099EA}.Debug|Any CPU.Build.0 = Debug|Any CPU {F57594AA-10C2-4DFF-87F6-19F2548099EA}.Release|Any CPU.ActiveCfg = Release|Any CPU {F57594AA-10C2-4DFF-87F6-19F2548099EA}.Release|Any CPU.Build.0 = Release|Any CPU + {91AE01B1-CC82-40E2-8290-B8A84C6E90D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {91AE01B1-CC82-40E2-8290-B8A84C6E90D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91AE01B1-CC82-40E2-8290-B8A84C6E90D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {91AE01B1-CC82-40E2-8290-B8A84C6E90D1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1212,6 +1220,8 @@ Global {8DF50094-6791-4C7C-B07D-C3E995B69C49} = {D01D859E-4B72-478A-BABD-90F0981652D5} {96EF6CDD-CD29-4E7B-B86A-3EBEE6AC9FDC} = {D01D859E-4B72-478A-BABD-90F0981652D5} {F57594AA-10C2-4DFF-87F6-19F2548099EA} = {A5543E56-DA53-494D-A531-DA75091D46FF} + {13ACF670-F109-404E-B252-2FA34A4EA061} = {C5CAD011-DF84-4914-939C-0C029DCEF26F} + {91AE01B1-CC82-40E2-8290-B8A84C6E90D1} = {13ACF670-F109-404E-B252-2FA34A4EA061} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C95FDF91-16F2-4A8B-A4BE-0E62D1B66718} diff --git a/aspnet-core/LINGYUN.MicroService.Common.sln b/aspnet-core/LINGYUN.MicroService.Common.sln index e2fdcc2f0..edeb3b22c 100644 --- a/aspnet-core/LINGYUN.MicroService.Common.sln +++ b/aspnet-core/LINGYUN.MicroService.Common.sln @@ -226,6 +226,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Dapr.Actors.Asp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.MultiTenancy.Editions", "modules\tenants\LINGYUN.Abp.MultiTenancy.Editions\LINGYUN.Abp.MultiTenancy.Editions.csproj", "{3FF4CEA0-1555-4D62-AA81-B3B599253F8D}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "webhooks", "webhooks", "{BD97C98B-0B4B-443D-AB29-145A344F46D3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WebHooks", "modules\webhooks\LINGYUN.Abp.WebHooks\LINGYUN.Abp.WebHooks.csproj", "{AFE75D2B-8853-488B-B5D5-277B58C5DBB2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -580,6 +584,10 @@ Global {3FF4CEA0-1555-4D62-AA81-B3B599253F8D}.Debug|Any CPU.Build.0 = Debug|Any CPU {3FF4CEA0-1555-4D62-AA81-B3B599253F8D}.Release|Any CPU.ActiveCfg = Release|Any CPU {3FF4CEA0-1555-4D62-AA81-B3B599253F8D}.Release|Any CPU.Build.0 = Release|Any CPU + {AFE75D2B-8853-488B-B5D5-277B58C5DBB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AFE75D2B-8853-488B-B5D5-277B58C5DBB2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AFE75D2B-8853-488B-B5D5-277B58C5DBB2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AFE75D2B-8853-488B-B5D5-277B58C5DBB2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -692,6 +700,8 @@ Global {FF518E10-C9AB-440C-8E8D-9CFF67A926AC} = {3A0784A6-AFBF-406F-B79E-9505EB100445} {49E0B90B-8635-43D0-B0AB-9D484CAE68B5} = {7FDFB22F-1BFF-4E05-9427-78B7A8461D50} {3FF4CEA0-1555-4D62-AA81-B3B599253F8D} = {38E21687-5F19-42C9-9D11-4B1D2EF64EDB} + {BD97C98B-0B4B-443D-AB29-145A344F46D3} = {02EA4E78-5891-43BC-944F-3E52FEE032E4} + {AFE75D2B-8853-488B-B5D5-277B58C5DBB2} = {BD97C98B-0B4B-443D-AB29-145A344F46D3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {06C707C6-02C0-411A-AD3B-2D0E13787CB8} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xml new file mode 100644 index 000000000..17d32672d --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.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/webhooks/LINGYUN.Abp.WebHooks/LINGYUN.Abp.WebHooks.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN.Abp.WebHooks.csproj new file mode 100644 index 000000000..ca6031c7a --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN.Abp.WebHooks.csproj @@ -0,0 +1,18 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksModule.cs new file mode 100644 index 000000000..03af8e4ef --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksModule.cs @@ -0,0 +1,18 @@ +using Volo.Abp.BackgroundJobs; +using Volo.Abp.Features; +using Volo.Abp.Guids; +using Volo.Abp.Http.Client; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.Webhooks; + +[DependsOn(typeof(AbpBackgroundJobsAbstractionsModule))] +[DependsOn(typeof(AbpFeaturesModule))] +[DependsOn(typeof(AbpGuidsModule))] +[DependsOn(typeof(AbpHttpClientModule))] +public class AbpWebhooksModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs new file mode 100644 index 000000000..7ac2ec457 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs @@ -0,0 +1,26 @@ +using System; +using Volo.Abp.Collections; + +namespace LINGYUN.Abp.Webhooks; + +public class AbpWebhooksOptions +{ + public TimeSpan TimeoutDuration { get; set; } + + public int MaxSendAttemptCount { get; set; } + + public bool IsAutomaticSubscriptionDeactivationEnabled { get; set; } + + public int MaxConsecutiveFailCountBeforeDeactivateSubscription { get; set; } + + public ITypeList DefinitionProviders { get; } + + public AbpWebhooksOptions() + { + TimeoutDuration = TimeSpan.FromSeconds(60); + MaxSendAttemptCount = 5; + MaxConsecutiveFailCountBeforeDeactivateSubscription = MaxSendAttemptCount * 3; + + DefinitionProviders = new TypeList(); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/BackgroundWorker/WebhookSenderJob.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/BackgroundWorker/WebhookSenderJob.cs new file mode 100644 index 000000000..4d18cb209 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/BackgroundWorker/WebhookSenderJob.cs @@ -0,0 +1,125 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Threading.Tasks; +using Volo.Abp.BackgroundJobs; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Uow; + +namespace LINGYUN.Abp.Webhooks.BackgroundWorker +{ + public class WebhookSenderJob : AsyncBackgroundJob, ITransientDependency + { + private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly IWebhookSubscriptionManager _webhookSubscriptionManager; + private readonly IWebhookSendAttemptStore _webhookSendAttemptStore; + private readonly IWebhookSender _webhookSender; + + private readonly AbpWebhooksOptions _options; + + public WebhookSenderJob( + IUnitOfWorkManager unitOfWorkManager, + IWebhookSubscriptionManager webhookSubscriptionManager, + IWebhookSendAttemptStore webhookSendAttemptStore, + IWebhookSender webhookSender, + IOptions options) + { + _unitOfWorkManager = unitOfWorkManager; + _webhookSubscriptionManager = webhookSubscriptionManager; + _webhookSendAttemptStore = webhookSendAttemptStore; + _webhookSender = webhookSender; + _options = options.Value; + } + + public override async Task ExecuteAsync(WebhookSenderArgs args) + { + if (args.TryOnce) + { + try + { + await SendWebhook(args); + } + catch (Exception e) + { + Logger.LogWarning("An error occured while sending webhook with try once.", e); + // ignored + } + } + else + { + await SendWebhook(args); + } + } + + private async Task SendWebhook(WebhookSenderArgs args) + { + if (args.WebhookEventId == default) + { + return; + } + + if (args.WebhookSubscriptionId == default) + { + return; + } + + if (!args.TryOnce) + { + var sendAttemptCount = await _webhookSendAttemptStore.GetSendAttemptCountAsync( + args.TenantId, + args.WebhookEventId, + args.WebhookSubscriptionId + ); + + if (sendAttemptCount > _options.MaxSendAttemptCount) + { + return; + } + } + + try + { + await _webhookSender.SendWebhookAsync(args); + } + catch (Exception) + { + // no need to retry to send webhook since subscription disabled + if (!await TryDeactivateSubscriptionIfReachedMaxConsecutiveFailCount( + args.TenantId, + args.WebhookSubscriptionId)) + { + throw; //Throw exception to re-try sending webhook + } + } + } + + private async Task TryDeactivateSubscriptionIfReachedMaxConsecutiveFailCount( + Guid? tenantId, + Guid subscriptionId) + { + if (!_options.IsAutomaticSubscriptionDeactivationEnabled) + { + return false; + } + + var hasXConsecutiveFail = await _webhookSendAttemptStore + .HasXConsecutiveFailAsync( + tenantId, + subscriptionId, + _options.MaxConsecutiveFailCountBeforeDeactivateSubscription + ); + + if (!hasXConsecutiveFail) + { + return false; + } + + using (var uow = _unitOfWorkManager.Begin()) + { + await _webhookSubscriptionManager.ActivateWebhookSubscriptionAsync(subscriptionId, false); + await uow.CompleteAsync(); + return true; + } + } + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookPublisher.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookPublisher.cs new file mode 100644 index 000000000..d466ee0f8 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookPublisher.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.BackgroundJobs; +using Volo.Abp.Guids; +using Volo.Abp.Json; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.Webhooks +{ + public class DefaultWebhookPublisher : IWebhookPublisher + { + public IWebhookEventStore WebhookEventStore { get; set; } + + private readonly ICurrentTenant _currentTenant; + private readonly IGuidGenerator _guidGenerator; + private readonly IJsonSerializer _jsonSerializer; + private readonly IBackgroundJobManager _backgroundJobManager; + private readonly IWebhookSubscriptionManager _webhookSubscriptionManager; + + public DefaultWebhookPublisher( + IWebhookSubscriptionManager webhookSubscriptionManager, + ICurrentTenant currentTenant, + IGuidGenerator guidGenerator, + IJsonSerializer jsonSerializer, + IBackgroundJobManager backgroundJobManager) + { + _currentTenant = currentTenant; + _guidGenerator = guidGenerator; + _jsonSerializer = jsonSerializer; + _backgroundJobManager = backgroundJobManager; + _webhookSubscriptionManager = webhookSubscriptionManager; + + WebhookEventStore = NullWebhookEventStore.Instance; + } + + #region Async Publish Methods + public virtual async Task PublishAsync(string webhookName, object data, bool sendExactSameData = false, WebhookHeader headers = null) + { + var subscriptions = await _webhookSubscriptionManager.GetAllSubscriptionsIfFeaturesGrantedAsync(_currentTenant.Id, webhookName); + await PublishAsync(webhookName, data, subscriptions, sendExactSameData, headers); + } + + public virtual async Task PublishAsync(string webhookName, object data, Guid? tenantId, + bool sendExactSameData = false, WebhookHeader headers = null) + { + var subscriptions = await _webhookSubscriptionManager.GetAllSubscriptionsIfFeaturesGrantedAsync(tenantId, webhookName); + await PublishAsync(webhookName, data, subscriptions, sendExactSameData, headers); + } + + public virtual async Task PublishAsync(Guid?[] tenantIds, string webhookName, object data, + bool sendExactSameData = false, WebhookHeader headers = null) + { + var subscriptions = await _webhookSubscriptionManager.GetAllSubscriptionsOfTenantsIfFeaturesGrantedAsync(tenantIds, webhookName); + await PublishAsync(webhookName, data, subscriptions, sendExactSameData, headers); + } + + private async Task PublishAsync(string webhookName, object data, List webhookSubscriptions, + bool sendExactSameData = false, WebhookHeader headers = null) + { + if (webhookSubscriptions.IsNullOrEmpty()) + { + return; + } + + var subscriptionsGroupedByTenant = webhookSubscriptions.GroupBy(x => x.TenantId); + + foreach (var subscriptionGroupedByTenant in subscriptionsGroupedByTenant) + { + var webhookInfo = await SaveAndGetWebhookAsync(subscriptionGroupedByTenant.Key, webhookName, data); + + foreach (var webhookSubscription in subscriptionGroupedByTenant) + { + var headersToSend = webhookSubscription.Headers; + if (headers != null) + { + if (headers.UseOnlyGivenHeaders)//do not use the headers defined in subscription + { + headersToSend = headers.Headers; + } + else + { + //use the headers defined in subscription. If additional headers has same header, use additional headers value. + foreach (var additionalHeader in headers.Headers) + { + headersToSend[additionalHeader.Key] = additionalHeader.Value; + } + } + } + + await _backgroundJobManager.EnqueueAsync(new WebhookSenderArgs + { + TenantId = webhookSubscription.TenantId, + WebhookEventId = webhookInfo.Id, + Data = webhookInfo.Data, + WebhookName = webhookInfo.WebhookName, + WebhookSubscriptionId = webhookSubscription.Id, + Headers = headersToSend, + Secret = webhookSubscription.Secret, + WebhookUri = webhookSubscription.WebhookUri, + SendExactSameData = sendExactSameData + }); + } + } + } + + #endregion + + protected virtual async Task SaveAndGetWebhookAsync(Guid? tenantId, string webhookName, + object data) + { + var webhookInfo = new WebhookEvent + { + Id = _guidGenerator.Create(), + WebhookName = webhookName, + Data = _jsonSerializer.Serialize(data), + TenantId = tenantId + }; + + await WebhookEventStore.InsertAndGetIdAsync(webhookInfo); + return webhookInfo; + } + } +} \ No newline at end of file diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookSender.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookSender.cs new file mode 100644 index 000000000..2dc939dc7 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookSender.cs @@ -0,0 +1,133 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using System; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Webhooks +{ + public class DefaultWebhookSender : IWebhookSender + { + public ILogger Logger { protected get; set; } + + private readonly AbpWebhooksOptions _options; + private readonly IWebhookManager _webhookManager; + + private const string FailedRequestDefaultContent = "Webhook Send Request Failed"; + + public DefaultWebhookSender( + IOptions options, + IWebhookManager webhookManager) + { + _options = options.Value; + _webhookManager = webhookManager; + + Logger = NullLogger.Instance; + } + + public async Task SendWebhookAsync(WebhookSenderArgs webhookSenderArgs) + { + if (webhookSenderArgs.WebhookEventId == default) + { + throw new ArgumentNullException(nameof(webhookSenderArgs.WebhookEventId)); + } + + if (webhookSenderArgs.WebhookSubscriptionId == default) + { + throw new ArgumentNullException(nameof(webhookSenderArgs.WebhookSubscriptionId)); + } + + var webhookSendAttemptId = await _webhookManager.InsertAndGetIdWebhookSendAttemptAsync(webhookSenderArgs); + + var request = CreateWebhookRequestMessage(webhookSenderArgs); + + var serializedBody = await _webhookManager.GetSerializedBodyAsync(webhookSenderArgs); + + _webhookManager.SignWebhookRequest(request, serializedBody, webhookSenderArgs.Secret); + + AddAdditionalHeaders(request, webhookSenderArgs); + + var isSucceed = false; + HttpStatusCode? statusCode = null; + var content = FailedRequestDefaultContent; + + try + { + var response = await SendHttpRequest(request); + isSucceed = response.isSucceed; + statusCode = response.statusCode; + content = response.content; + } + catch (TaskCanceledException) + { + statusCode = HttpStatusCode.RequestTimeout; + content = "Request Timeout"; + } + catch (HttpRequestException e) + { + content = e.Message; + } + catch (Exception e) + { + Logger.LogError("An error occured while sending a webhook request", e); + } + finally + { + await _webhookManager.StoreResponseOnWebhookSendAttemptAsync(webhookSendAttemptId, webhookSenderArgs.TenantId, statusCode, content); + } + + if (!isSucceed) + { + throw new Exception($"Webhook sending attempt failed. WebhookSendAttempt id: {webhookSendAttemptId}"); + } + + return webhookSendAttemptId; + } + + /// + /// You can override this to change request message + /// + /// + protected virtual HttpRequestMessage CreateWebhookRequestMessage(WebhookSenderArgs webhookSenderArgs) + { + return new HttpRequestMessage(HttpMethod.Post, webhookSenderArgs.WebhookUri); + } + + protected virtual void AddAdditionalHeaders(HttpRequestMessage request, WebhookSenderArgs webhookSenderArgs) + { + foreach (var header in webhookSenderArgs.Headers) + { + if (request.Headers.TryAddWithoutValidation(header.Key, header.Value)) + { + continue; + } + + if (request.Content.Headers.TryAddWithoutValidation(header.Key, header.Value)) + { + continue; + } + + throw new Exception($"Invalid Header. SubscriptionId:{webhookSenderArgs.WebhookSubscriptionId},Header: {header.Key}:{header.Value}"); + } + } + + protected virtual async Task<(bool isSucceed, HttpStatusCode statusCode, string content)> SendHttpRequest(HttpRequestMessage request) + { + using (var client = new HttpClient + { + Timeout = _options.TimeoutDuration + }) + { + var response = await client.SendAsync(request); + + var isSucceed = response.IsSuccessStatusCode; + var statusCode = response.StatusCode; + var content = await response.Content.ReadAsStringAsync(); + + return (isSucceed, statusCode, content); + } + } + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionContext.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionContext.cs new file mode 100644 index 000000000..328c214bd --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionContext.cs @@ -0,0 +1,22 @@ +namespace LINGYUN.Abp.Webhooks +{ + public interface IWebhookDefinitionContext + { + /// + /// Adds the specified webhook definition. Throws exception if it is already added + /// + void Add(params WebhookDefinition[] definitions); + + /// + /// Gets a webhook definition by name. + /// Returns null if there is no webhook definition with given name. + /// + WebhookDefinition GetOrNull(string name); + + /// + /// Remove webhook with given name + /// + /// webhook definition name + void Remove(string name); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionManager.cs new file mode 100644 index 000000000..057b0b984 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionManager.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Webhooks +{ + public interface IWebhookDefinitionManager + { + /// + /// Gets a webhook definition by name. + /// Returns null if there is no webhook definition with given name. + /// + WebhookDefinition GetOrNull(string name); + + /// + /// Gets a webhook definition by name. + /// Throws exception if there is no webhook definition with given name. + /// + WebhookDefinition Get(string name); + + /// + /// Gets all webhook definitions. + /// + IReadOnlyList GetAll(); + + /// + /// Checks if given webhook name is available for given tenant. + /// + Task IsAvailableAsync(Guid? tenantId, string name); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookEventStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookEventStore.cs new file mode 100644 index 000000000..1fb8f5c21 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookEventStore.cs @@ -0,0 +1,18 @@ +using System; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Webhooks +{ + public interface IWebhookEventStore + { + /// + /// Inserts to persistent store + /// + Task InsertAndGetIdAsync(WebhookEvent webhookEvent); + + /// + /// Gets Webhook info by id + /// + Task GetAsync(Guid? tenantId, Guid id); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookManager.cs new file mode 100644 index 000000000..1e8e5fd0c --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookManager.cs @@ -0,0 +1,22 @@ +using System; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Webhooks +{ + public interface IWebhookManager + { + Task GetWebhookPayloadAsync(WebhookSenderArgs webhookSenderArgs); + + void SignWebhookRequest(HttpRequestMessage request, string serializedBody, string secret); + + Task GetSerializedBodyAsync(WebhookSenderArgs webhookSenderArgs); + + Task InsertAndGetIdWebhookSendAttemptAsync(WebhookSenderArgs webhookSenderArgs); + + Task StoreResponseOnWebhookSendAttemptAsync( + Guid webhookSendAttemptId, Guid? tenantId, + HttpStatusCode? statusCode, string content); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookPublisher.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookPublisher.cs new file mode 100644 index 000000000..3b53fec83 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookPublisher.cs @@ -0,0 +1,56 @@ +using System; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Webhooks +{ + public interface IWebhookPublisher + { + /// + /// Sends webhooks to current tenant subscriptions (). with given data, (Checks permissions) + /// + /// + /// data to send + /// + /// True: It sends the exact same data as the parameter to clients. + /// + /// False: It sends data in . It is recommended way. + /// + /// + /// Headers to send. Publisher uses subscription defined webhook by default. You can add additional headers from here. If subscription already has given header, publisher uses the one you give here. + Task PublishAsync(string webhookName, object data, bool sendExactSameData = false, WebhookHeader headers = null); + + /// + /// Sends webhooks to given tenant's subscriptions + /// + /// + /// data to send + /// + /// Target tenant id + /// + /// + /// True: It sends the exact same data as the parameter to clients. + /// + /// False: It sends data in . It is recommended way. + /// + /// + /// Headers to send. Publisher uses subscription defined webhook by default. You can add additional headers from here. If subscription already has given header, publisher uses the one you give here. + Task PublishAsync(string webhookName, object data, Guid? tenantId, bool sendExactSameData = false, WebhookHeader headers = null); + + /// + /// Sends webhooks to given tenant's subscriptions + /// + /// + /// data to send + /// + /// Target tenant id(s) + /// + /// + /// True: It sends the exact same data as the parameter to clients. + /// + /// False: It sends data in . It is recommended way. + /// + /// + /// Headers to send. Publisher uses subscription defined webhook by default. You can add additional headers from here. If subscription already has given header, publisher uses the one you give here. + Task PublishAsync(Guid?[] tenantIds, string webhookName, object data, bool sendExactSameData = false, WebhookHeader headers = null); + } +} \ No newline at end of file diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSendAttemptStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSendAttemptStore.cs new file mode 100644 index 000000000..2e32bc63f --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSendAttemptStore.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Webhooks +{ + public interface IWebhookSendAttemptStore + { + Task GetAsync(Guid? tenantId, Guid id); + + /// + /// Returns work item count by given web hook id and subscription id, (How many times publisher tried to send web hook) + /// + Task GetSendAttemptCountAsync(Guid? tenantId, Guid webhookId, Guid webhookSubscriptionId); + + /// + /// Checks is there any successful webhook attempt in last items. Should return true if there are not X number items + /// + Task HasXConsecutiveFailAsync(Guid? tenantId, Guid subscriptionId, int searchCount); + + Task<(int TotalCount, IReadOnlyCollection Webhooks)> GetAllSendAttemptsBySubscriptionAsPagedListAsync(Guid? tenantId, Guid subscriptionId, int maxResultCount, int skipCount); + + Task> GetAllSendAttemptsByWebhookEventIdAsync(Guid? tenantId, Guid webhookEventId); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSender.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSender.cs new file mode 100644 index 000000000..d30e110e6 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSender.cs @@ -0,0 +1,16 @@ +using System; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Webhooks +{ + public interface IWebhookSender + { + /// + /// Tries to send webhook with given transactionId and stores process in + /// Should throw exception if fails or response status not succeed + /// + /// arguments + /// Webhook send attempt id + Task SendWebhookAsync(WebhookSenderArgs webhookSenderArgs); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionManager.cs new file mode 100644 index 000000000..956b2dbd3 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionManager.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Webhooks +{ + public interface IWebhookSubscriptionManager + { + /// + /// Returns subscription for given id. + /// + /// Unique identifier of + Task GetAsync(Guid id); + + /// + /// Returns all subscriptions of tenant + /// + /// + /// Target tenant id. + /// + Task> GetAllSubscriptionsAsync(Guid? tenantId); + + /// + /// Returns all subscriptions for given webhook. + /// + /// + /// + /// Target tenant id. + /// + Task> GetAllSubscriptionsIfFeaturesGrantedAsync(Guid? tenantId, string webhookName); + + /// + /// Returns all subscriptions of tenant + /// + /// + Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds); + + /// + /// Returns all subscriptions for given webhook. + /// + /// + /// + /// Target tenant id(s). + /// + Task> GetAllSubscriptionsOfTenantsIfFeaturesGrantedAsync(Guid?[] tenantIds, string webhookName); + + /// + /// Checks if tenant subscribed for a webhook. (Checks if webhook features are granted) + /// + /// + /// Target tenant id(s). + /// + /// + Task IsSubscribedAsync(Guid? tenantId, string webhookName); + + /// + /// If id is the default(Guid) adds new subscription, else updates current one. (Checks if webhook features are granted) + /// + Task AddOrUpdateSubscriptionAsync(WebhookSubscriptionInfo webhookSubscription); + + /// + /// Activates/Deactivates given webhook subscription + /// + /// unique identifier of + /// IsActive + Task ActivateWebhookSubscriptionAsync(Guid id, bool active); + + /// + /// Delete given webhook subscription. + /// + /// unique identifier of + Task DeleteSubscriptionAsync(Guid id); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionsStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionsStore.cs new file mode 100644 index 000000000..23849ad4e --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionsStore.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Webhooks +{ + /// + /// This interface should be implemented by vendors to make webhooks working. + /// + public interface IWebhookSubscriptionsStore + { + /// + /// returns subscription + /// + /// webhook subscription id + /// + Task GetAsync(Guid id); + + /// + /// Saves webhook subscription to a persistent store. + /// + /// webhook subscription information + Task InsertAsync(WebhookSubscriptionInfo webhookSubscription); + + /// + /// Updates webhook subscription to a persistent store. + /// + /// webhook subscription information + Task UpdateAsync(WebhookSubscriptionInfo webhookSubscription); + + /// + /// Deletes subscription if exists + /// + /// primary key + /// + Task DeleteAsync(Guid id); + + /// + /// Returns all subscriptions of given tenant including deactivated + /// + /// + /// Target tenant id. + /// + Task> GetAllSubscriptionsAsync(Guid? tenantId); + + /// + /// Returns webhook subscriptions which subscribe to given webhook on tenant(s) + /// + /// + /// Target tenant id. + /// + /// + /// + Task> GetAllSubscriptionsAsync(Guid? tenantId, string webhookName); + + /// + /// Returns all subscriptions of given tenant including deactivated + /// + /// + /// Target tenant id(s). + /// + Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds); + + /// + /// Returns webhook subscriptions which subscribe to given webhook on tenant(s) + /// + /// + /// Target tenant id(s). + /// + /// + /// + Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds, string webhookName); + + /// + /// Checks if tenant subscribed for a webhook + /// + /// + /// Target tenant id(s). + /// + /// Name of the webhook + Task IsSubscribedAsync(Guid? tenantId, string webhookName); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookEventStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookEventStore.cs new file mode 100644 index 000000000..069726e67 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookEventStore.cs @@ -0,0 +1,24 @@ +using System; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Webhooks +{ + /// + /// Null pattern implementation of . + /// It's used if is not implemented by actual persistent store + /// + public class NullWebhookEventStore : IWebhookEventStore + { + public static NullWebhookEventStore Instance { get; } = new NullWebhookEventStore(); + + public Task InsertAndGetIdAsync(WebhookEvent webhookEvent) + { + return Task.FromResult(default); + } + + public Task GetAsync(Guid? tenantId, Guid id) + { + return Task.FromResult(default); + } + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSendAttemptStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSendAttemptStore.cs new file mode 100644 index 000000000..75749401b --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSendAttemptStore.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Webhooks +{ + public class NullWebhookSendAttemptStore : IWebhookSendAttemptStore + { + public static NullWebhookSendAttemptStore Instance = new NullWebhookSendAttemptStore(); + + public Task InsertAsync(WebhookSendAttempt webhookSendAttempt) + { + return Task.CompletedTask; + } + + public Task UpdateAsync(WebhookSendAttempt webhookSendAttempt) + { + return Task.CompletedTask; + } + + public Task GetAsync(Guid? tenantId, Guid id) + { + return Task.FromResult(default); + } + + public Task GetSendAttemptCountAsync(Guid? tenantId, Guid webhookId, Guid webhookSubscriptionId) + { + return Task.FromResult(int.MaxValue); + } + + public Task HasXConsecutiveFailAsync(Guid? tenantId, Guid subscriptionId, int searchCount) + { + return default; + } + + public Task> GetAllSendAttemptsBySubscriptionAsPagedListAsync(Guid? tenantId, Guid subscriptionId, int maxResultCount, + int skipCount) + { + return Task.FromResult(new List() as IReadOnlyCollection); + } + + public Task> GetAllSendAttemptsByWebhookEventIdAsync(Guid? tenantId, Guid webhookEventId) + { + return Task.FromResult(new List()); + } + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSubscriptionsStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSubscriptionsStore.cs new file mode 100644 index 000000000..4214d1d25 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSubscriptionsStore.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Webhooks +{ + /// + /// Null pattern implementation of . + /// It's used if is not implemented by actual persistent store + /// + public class NullWebhookSubscriptionsStore : IWebhookSubscriptionsStore + { + public static NullWebhookSubscriptionsStore Instance { get; } = new NullWebhookSubscriptionsStore(); + + public Task GetAsync(Guid id) + { + return Task.FromResult(default); + } + + public WebhookSubscriptionInfo Get(Guid id) + { + return default; + } + + public Task InsertAsync(WebhookSubscriptionInfo webhookSubscription) + { + return Task.CompletedTask; + } + + public Task UpdateAsync(WebhookSubscriptionInfo webhookSubscription) + { + return Task.CompletedTask; + } + + public Task DeleteAsync(Guid id) + { + return Task.CompletedTask; + } + + public Task> GetAllSubscriptionsAsync(Guid? tenantId) + { + return Task.FromResult(new List()); + } + + public Task> GetAllSubscriptionsAsync(Guid? tenantId, string webhookName) + { + return Task.FromResult(new List()); + } + + public Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds) + { + return Task.FromResult(new List()); + } + + public Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds, string webhookName) + { + return Task.FromResult(new List()); + } + + public Task IsSubscribedAsync(Guid? tenantId, string webhookName) + { + return Task.FromResult(false); + } + } +} \ No newline at end of file diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinition.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinition.cs new file mode 100644 index 000000000..88f3a074d --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinition.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using Volo.Abp.Localization; + +namespace LINGYUN.Abp.Webhooks +{ + public class WebhookDefinition + { + /// + /// Unique name of the webhook. + /// + public string Name { get; } + + /// + /// Display name of the webhook. + /// Optional. + /// + public ILocalizableString DisplayName { get; set; } + + /// + /// Description for the webhook. + /// Optional. + /// + public ILocalizableString Description { get; set; } + + public List RequiredFeatures { get; set; } + + public WebhookDefinition(string name, ILocalizableString displayName = null, ILocalizableString description = null) + { + if (name.IsNullOrWhiteSpace()) + { + throw new ArgumentNullException(nameof(name), $"{nameof(name)} can not be null, empty or whitespace!"); + } + + Name = name.Trim(); + DisplayName = displayName; + Description = description; + + RequiredFeatures = new List(); + } + + public WebhookDefinition WithFeature(params string[] features) + { + if (!features.IsNullOrEmpty()) + { + RequiredFeatures.AddRange(features); + } + + return this; + } + + public override string ToString() + { + return $"[{nameof(WebhookDefinition)} {Name}]"; + } + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionContext.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionContext.cs new file mode 100644 index 000000000..4176fc785 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionContext.cs @@ -0,0 +1,54 @@ +using JetBrains.Annotations; +using System.Collections.Generic; +using Volo.Abp; +using Volo.Abp.Localization; + +namespace LINGYUN.Abp.Webhooks +{ + public class WebhookDefinitionContext : IWebhookDefinitionContext + { + protected Dictionary Webhooks { get; } + + public WebhookDefinitionContext(Dictionary webhooks) + { + Webhooks = webhooks; + } + + public void Add(params WebhookDefinition[] definitions) + { + if (definitions.IsNullOrEmpty()) + { + return; + } + + foreach (var definition in definitions) + { + Webhooks[definition.Name] = definition; + } + } + + public WebhookDefinition GetOrNull([NotNull] string name) + { + Check.NotNull(name, nameof(name)); + + if (!Webhooks.ContainsKey(name)) + { + return null; + } + + return Webhooks[name]; + } + + public void Remove(string name) + { + Check.NotNull(name, nameof(name)); + + if (!Webhooks.ContainsKey(name)) + { + throw new AbpException($"Undefined notification webhook: '{name}'."); + } + + Webhooks.Remove(name); + } + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs new file mode 100644 index 000000000..bf70017be --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs @@ -0,0 +1,109 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.Webhooks +{ + internal class WebhookDefinitionManager : IWebhookDefinitionManager, ISingletonDependency + { + protected IDictionary WebhookDefinitions => _lazyWebhookDefinitions.Value; + private readonly Lazy> _lazyWebhookDefinitions; + + private readonly IServiceProvider _serviceProvider; + private readonly AbpWebhooksOptions _options; + + public WebhookDefinitionManager( + IServiceProvider serviceProvider, + IOptions options) + { + _serviceProvider = serviceProvider; + _options = options.Value; + + _lazyWebhookDefinitions = new Lazy>(CreateWebhookDefinitions); + } + + public WebhookDefinition GetOrNull(string name) + { + if (!WebhookDefinitions.ContainsKey(name)) + { + return null; + } + + return WebhookDefinitions[name]; + } + + public WebhookDefinition Get(string name) + { + if (!WebhookDefinitions.ContainsKey(name)) + { + throw new KeyNotFoundException($"Webhook definitions does not contain a definition with the key \"{name}\"."); + } + + return WebhookDefinitions[name]; + } + + public IReadOnlyList GetAll() + { + return WebhookDefinitions.Values.ToImmutableList(); + } + + public async Task IsAvailableAsync(Guid? tenantId, string name) + { + if (tenantId == null) // host allowed to subscribe all webhooks + { + return true; + } + + var webhookDefinition = GetOrNull(name); + + if (webhookDefinition == null) + { + return false; + } + + if (webhookDefinition.RequiredFeatures?.Any() == false) + { + return true; + } + + var currentTenant = _serviceProvider.GetRequiredService(); + var featureChecker = _serviceProvider.GetRequiredService(); + using (currentTenant.Change(tenantId)) + { + if (!await featureChecker.IsEnabledAsync(true, webhookDefinition.RequiredFeatures.ToArray())) + { + return false; + } + } + + return true; + } + + protected virtual Dictionary CreateWebhookDefinitions() + { + var definitions = new Dictionary(); + + using (var scope = _serviceProvider.CreateScope()) + { + var providers = _options + .DefinitionProviders + .Select(p => scope.ServiceProvider.GetRequiredService(p) as WebhookDefinitionProvider) + .ToList(); + + foreach (var provider in providers) + { + provider.Define(new WebhookDefinitionContext(definitions)); + } + } + + return definitions; + } + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionProvider.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionProvider.cs new file mode 100644 index 000000000..ead9ab679 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionProvider.cs @@ -0,0 +1,13 @@ +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.Webhooks +{ + public abstract class WebhookDefinitionProvider : ITransientDependency + { + /// + /// Used to add/manipulate webhook definitions. + /// + /// Context, + public abstract void Define(IWebhookDefinitionContext context); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookEvent.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookEvent.cs new file mode 100644 index 000000000..bd32fa76d --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookEvent.cs @@ -0,0 +1,30 @@ +using System; + +namespace LINGYUN.Abp.Webhooks +{ + /// + /// Store created web hooks. To see who get that webhook check with and you can get + /// + public class WebhookEvent + { + public Guid Id { get; set; } + + /// + /// Webhook unique name + /// + public string WebhookName { get; set; } + + /// + /// Webhook data as JSON string. + /// + public string Data { get; set; } + + public DateTime CreationTime { get; set; } + + public Guid? TenantId { get; set; } + + public bool IsDeleted { get; set; } + + public DateTime? DeletionTime { get; set; } + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookHeader.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookHeader.cs new file mode 100644 index 000000000..91442552f --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookHeader.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace LINGYUN.Abp.Webhooks +{ + public class WebhookHeader + { + /// + /// If true, webhook will only contain given headers. If false given headers will be added to predefined headers in subscription. + /// Default is false + /// + public bool UseOnlyGivenHeaders { get; set; } + + /// + /// That headers will be sent with the webhook. + /// + public IDictionary Headers { get; set; } + } +} \ No newline at end of file diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookManager.cs new file mode 100644 index 000000000..29389e15e --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookManager.cs @@ -0,0 +1,84 @@ +using System; +using System.Globalization; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Json; + +namespace LINGYUN.Abp.Webhooks +{ + public abstract class WebhookManager : IWebhookManager + { + private const string SignatureHeaderKey = "sha256"; + private const string SignatureHeaderValueTemplate = SignatureHeaderKey + "={0}"; + private const string SignatureHeaderName = "abp-webhook-signature"; + + protected IJsonSerializer JsonSerializer { get; } + protected IWebhookSendAttemptStore WebhookSendAttemptStore { get; } + + protected WebhookManager( + IJsonSerializer jsonSerializer, + IWebhookSendAttemptStore webhookSendAttemptStore) + { + JsonSerializer = jsonSerializer; + WebhookSendAttemptStore = webhookSendAttemptStore; + } + + public virtual async Task GetWebhookPayloadAsync(WebhookSenderArgs webhookSenderArgs) + { + var data = JsonSerializer.Serialize(webhookSenderArgs.Data); + + var attemptNumber = await WebhookSendAttemptStore.GetSendAttemptCountAsync( + webhookSenderArgs.TenantId, + webhookSenderArgs.WebhookEventId, + webhookSenderArgs.WebhookSubscriptionId); + + return new WebhookPayload( + webhookSenderArgs.WebhookEventId.ToString(), + webhookSenderArgs.WebhookName, + attemptNumber) + { + Data = data + }; + } + + public virtual void SignWebhookRequest(HttpRequestMessage request, string serializedBody, string secret) + { + if (request == null) + { + throw new ArgumentNullException(nameof(request)); + } + + if (string.IsNullOrWhiteSpace(serializedBody)) + { + throw new ArgumentNullException(nameof(serializedBody)); + } + + request.Content = new StringContent(serializedBody, Encoding.UTF8, "application/json"); + + var secretBytes = Encoding.UTF8.GetBytes(secret); + var headerValue = string.Format(CultureInfo.InvariantCulture, SignatureHeaderValueTemplate, serializedBody.Sha256(secretBytes)); + + request.Headers.Add(SignatureHeaderName, headerValue); + } + + public virtual async Task GetSerializedBodyAsync(WebhookSenderArgs webhookSenderArgs) + { + if (webhookSenderArgs.SendExactSameData) + { + return webhookSenderArgs.Data; + } + + var payload = await GetWebhookPayloadAsync(webhookSenderArgs); + + var serializedBody = JsonSerializer.Serialize(payload); + + return serializedBody; + } + + public abstract Task InsertAndGetIdWebhookSendAttemptAsync(WebhookSenderArgs webhookSenderArgs); + + public abstract Task StoreResponseOnWebhookSendAttemptAsync(Guid webhookSendAttemptId, Guid? tenantId, HttpStatusCode? statusCode, string content); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookPayload.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookPayload.cs new file mode 100644 index 000000000..ff287cbd0 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookPayload.cs @@ -0,0 +1,35 @@ +using System; + +namespace LINGYUN.Abp.Webhooks +{ + public class WebhookPayload + { + public string Id { get; set; } + + public string WebhookEvent { get; set; } + + public int Attempt { get; set; } + + public dynamic Data { get; set; } + + public DateTime CreationTimeUtc { get; set; } + + public WebhookPayload(string id, string webhookEvent, int attempt) + { + if (id.IsNullOrWhiteSpace()) + { + throw new ArgumentNullException(nameof(id)); + } + + if (webhookEvent.IsNullOrWhiteSpace()) + { + throw new ArgumentNullException(nameof(webhookEvent)); + } + + Id = id; + WebhookEvent = webhookEvent; + Attempt = attempt; + CreationTimeUtc = DateTime.UtcNow; + } + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSendAttempt.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSendAttempt.cs new file mode 100644 index 000000000..8189f8f72 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSendAttempt.cs @@ -0,0 +1,39 @@ +using System; +using System.Net; + +namespace LINGYUN.Abp.Webhooks +{ + /// + /// Table for store webhook work items. Each item stores web hook send attempt of to subscribed tenants + /// + public class WebhookSendAttempt + { + public Guid Id { get; set; } + + /// + /// foreign id + /// + public Guid WebhookEventId { get; set; } + + /// + /// foreign id + /// + public Guid WebhookSubscriptionId { get; set; } + + /// + /// Webhook response content that webhook endpoint send back + /// + public string Response { get; set; } + + /// + /// Webhook response status code that webhook endpoint send back + /// + public HttpStatusCode? ResponseStatusCode { get; set; } + + public DateTime CreationTime { get; set; } + + public DateTime? LastModificationTime { get; set; } + + public Guid? TenantId { get; set; } + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSenderArgs.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSenderArgs.cs new file mode 100644 index 000000000..7910ba0b1 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSenderArgs.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; + +namespace LINGYUN.Abp.Webhooks +{ + public class WebhookSenderArgs + { + public Guid? TenantId { get; set; } + + //Webhook information + + /// + /// foreign id + /// + public Guid WebhookEventId { get; set; } + + /// + /// Webhook unique name + /// + public string WebhookName { get; set; } + + /// + /// Webhook data as JSON string. + /// + public string Data { get; set; } + + //Subscription information + + /// + /// foreign id + /// + public Guid WebhookSubscriptionId { get; set; } + + /// + /// Subscription webhook endpoint + /// + public string WebhookUri { get; set; } + + /// + /// Webhook secret + /// + public string Secret { get; set; } + + /// + /// Gets a set of additional HTTP headers.That headers will be sent with the webhook. + /// + public IDictionary Headers { get; set; } + + /// + /// Tries to send webhook only one time without checking to send attempt count + /// + public bool TryOnce { get; set; } + + /// + /// True: It sends the exact same data as the parameter to clients. + /// + /// False: It sends data in . It is recommended way. + /// + /// + public bool SendExactSameData { get; set; } + + public WebhookSenderArgs() + { + Headers = new Dictionary(); + } + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionInfo.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionInfo.cs new file mode 100644 index 000000000..9e4e3bf2a --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionInfo.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; + +namespace LINGYUN.Abp.Webhooks +{ + public class WebhookSubscriptionInfo + { + public Guid Id { get; set; } + /// + /// Subscribed Tenant's id . + /// + public Guid? TenantId { get; set; } + + /// + /// Subscription webhook endpoint + /// + public string WebhookUri { get; set; } + + /// + /// Webhook secret + /// + public string Secret { get; set; } + + /// + /// Is subscription active + /// + public bool IsActive { get; set; } + + /// + /// Subscribed webhook definitions unique names.It contains webhook definitions list as json + /// + /// Do not change it manually. + /// Use , + /// , + /// and + /// to change it. + /// + /// + public List Webhooks { get; set; } + + /// + /// Gets a set of additional HTTP headers.That headers will be sent with the webhook. It contains webhook header dictionary as json + /// + /// Do not change it manually. + /// Use , + /// , + /// , + /// to change it. + /// + /// + public IDictionary Headers { get; set; } + + public WebhookSubscriptionInfo() + { + IsActive = true; + Headers = new Dictionary(); + Webhooks = new List(); + } + } +} \ No newline at end of file diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionManager.cs new file mode 100644 index 000000000..4ed53d52b --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionManager.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Authorization; +using Volo.Abp.Guids; +using Volo.Abp.Uow; + +namespace LINGYUN.Abp.Webhooks +{ + public class WebhookSubscriptionManager : IWebhookSubscriptionManager + { + public IWebhookSubscriptionsStore WebhookSubscriptionsStore { get; set; } + + private readonly IGuidGenerator _guidGenerator; + private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly IWebhookDefinitionManager _webhookDefinitionManager; + + private const string WebhookSubscriptionSecretPrefix = "whs_"; + + public WebhookSubscriptionManager( + IGuidGenerator guidGenerator, + IUnitOfWorkManager unitOfWorkManager, + IWebhookDefinitionManager webhookDefinitionManager) + { + _guidGenerator = guidGenerator; + _unitOfWorkManager = unitOfWorkManager; + _webhookDefinitionManager = webhookDefinitionManager; + + WebhookSubscriptionsStore = NullWebhookSubscriptionsStore.Instance; + } + + public virtual async Task GetAsync(Guid id) + { + return await WebhookSubscriptionsStore.GetAsync(id); + } + + public virtual async Task> GetAllSubscriptionsAsync(Guid? tenantId) + { + return await WebhookSubscriptionsStore.GetAllSubscriptionsAsync(tenantId); + } + + public virtual async Task> GetAllSubscriptionsIfFeaturesGrantedAsync(Guid? tenantId, string webhookName) + { + if (!await _webhookDefinitionManager.IsAvailableAsync(tenantId, webhookName)) + { + return new List(); + } + + return (await WebhookSubscriptionsStore.GetAllSubscriptionsAsync(tenantId, webhookName)).ToList(); + } + + public virtual async Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds) + { + return (await WebhookSubscriptionsStore.GetAllSubscriptionsOfTenantsAsync(tenantIds)).ToList(); + } + + public virtual async Task> GetAllSubscriptionsOfTenantsIfFeaturesGrantedAsync(Guid?[] tenantIds, string webhookName) + { + var featureGrantedTenants = new List(); + foreach (var tenantId in tenantIds) + { + if (await _webhookDefinitionManager.IsAvailableAsync(tenantId, webhookName)) + { + featureGrantedTenants.Add(tenantId); + } + } + + return (await WebhookSubscriptionsStore.GetAllSubscriptionsOfTenantsAsync(featureGrantedTenants.ToArray(), webhookName)).ToList(); + } + + public virtual async Task IsSubscribedAsync(Guid? tenantId, string webhookName) + { + if (!await _webhookDefinitionManager.IsAvailableAsync(tenantId, webhookName)) + { + return false; + } + + return await WebhookSubscriptionsStore.IsSubscribedAsync(tenantId, webhookName); + } + + public virtual async Task AddOrUpdateSubscriptionAsync(WebhookSubscriptionInfo webhookSubscription) + { + using (var uow = _unitOfWorkManager.Begin()) + { + await CheckIfPermissionsGrantedAsync(webhookSubscription); + + if (webhookSubscription.Id == default) + { + webhookSubscription.Id = _guidGenerator.Create(); + webhookSubscription.Secret = WebhookSubscriptionSecretPrefix + Guid.NewGuid().ToString("N"); + await WebhookSubscriptionsStore.InsertAsync(webhookSubscription); + } + else + { + var subscription = await WebhookSubscriptionsStore.GetAsync(webhookSubscription.Id); + subscription.WebhookUri = webhookSubscription.WebhookUri; + subscription.Webhooks = webhookSubscription.Webhooks; + subscription.Headers = webhookSubscription.Headers; + await WebhookSubscriptionsStore.UpdateAsync(subscription); + } + + await uow.SaveChangesAsync(); + } + } + + public virtual async Task ActivateWebhookSubscriptionAsync(Guid id, bool active) + { + using (var uow = _unitOfWorkManager.Begin()) + { + var webhookSubscription = await WebhookSubscriptionsStore.GetAsync(id); + webhookSubscription.IsActive = active; + + await uow.SaveChangesAsync(); + } + } + + public virtual async Task DeleteSubscriptionAsync(Guid id) + { + using (var uow = _unitOfWorkManager.Begin()) + { + await WebhookSubscriptionsStore.DeleteAsync(id); + + await uow.SaveChangesAsync(); + } + } + + public virtual async Task AddWebhookAsync(WebhookSubscriptionInfo subscription, string webhookName) + { + using (var uow = _unitOfWorkManager.Begin()) + { + await CheckPermissionsAsync(subscription.TenantId, webhookName); + webhookName = webhookName.Trim(); + if (webhookName.IsNullOrWhiteSpace()) + { + throw new ArgumentNullException(nameof(webhookName), $"{nameof(webhookName)} can not be null, empty or whitespace!"); + } + + if (!subscription.Webhooks.Contains(webhookName)) + { + subscription.Webhooks.Add(webhookName); + + await WebhookSubscriptionsStore.UpdateAsync(subscription); + } + + await uow.SaveChangesAsync(); + } + } + + #region PermissionCheck + + protected virtual async Task CheckIfPermissionsGrantedAsync(WebhookSubscriptionInfo webhookSubscription) + { + if (webhookSubscription.Webhooks.IsNullOrEmpty()) + { + return; + } + + foreach (var webhookDefinition in webhookSubscription.Webhooks) + { + await CheckPermissionsAsync(webhookSubscription.TenantId, webhookDefinition); + } + } + + protected virtual async Task CheckPermissionsAsync(Guid? tenantId, string webhookName) + { + if (!await _webhookDefinitionManager.IsAvailableAsync(tenantId, webhookName)) + { + throw new AbpAuthorizationException($"Tenant \"{tenantId}\" must have necessary feature(s) to use webhook \"{webhookName}\""); + } + } + + #endregion + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/System/AbpStringCryptographyExtensions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/System/AbpStringCryptographyExtensions.cs new file mode 100644 index 000000000..f4202f6e2 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/System/AbpStringCryptographyExtensions.cs @@ -0,0 +1,13 @@ +using System.Security.Cryptography; + +namespace System; + +internal static class AbpStringCryptographyExtensions +{ + public static string Sha256(this string planText, byte[] salt) + { + var data = planText.GetBytes(); + using var hmacsha256 = new HMACSHA256(salt); + return BitConverter.ToString(hmacsha256.ComputeHash(data)); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xml new file mode 100644 index 000000000..be0de3a90 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/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/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj new file mode 100644 index 000000000..70d317b54 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj @@ -0,0 +1,21 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + + diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissionDefinitionProvider.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissionDefinitionProvider.cs new file mode 100644 index 000000000..b9295955e --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissionDefinitionProvider.cs @@ -0,0 +1,22 @@ +using LINGYUN.Abp.WebhooksManagement.Localization; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Localization; + +namespace LINGYUN.Abp.WebhooksManagement.Authorization; + +public class WebhooksManagementPermissionDefinitionProvider : PermissionDefinitionProvider +{ + public override void Define(IPermissionDefinitionContext context) + { + var group = context.AddGroup(WebhooksManagementPermissions.GroupName, L("Permission:WebhooksManagement")); + + group.AddPermission( + WebhooksManagementPermissions.ManageSettings, + L("Permission:ManageSettings")); + } + + private static LocalizableString L(string name) + { + return LocalizableString.Create(name); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissions.cs new file mode 100644 index 000000000..0a1d9b0f7 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissions.cs @@ -0,0 +1,8 @@ +namespace LINGYUN.Abp.WebhooksManagement.Authorization; + +public static class WebhooksManagementPermissions +{ + public const string GroupName = "WebhooksManagement"; + + public const string ManageSettings = GroupName + ".ManageSettings"; +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureDefinitionProvider.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureDefinitionProvider.cs new file mode 100644 index 000000000..22d14bd79 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureDefinitionProvider.cs @@ -0,0 +1,18 @@ +using LINGYUN.Abp.WebhooksManagement.Localization; +using Volo.Abp.Features; +using Volo.Abp.Localization; + +namespace LINGYUN.Abp.WebhooksManagement.Features; + +public class WebhooksManagementFeatureDefinitionProvider : FeatureDefinitionProvider +{ + public override void Define(IFeatureDefinitionContext context) + { + var group = context.AddGroup(WebhooksManagementFeatureNames.GroupName, L("Features:WebhooksManagement")); + } + + private static ILocalizableString L(string name) + { + return LocalizableString.Create(name); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureNames.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureNames.cs new file mode 100644 index 000000000..6cb71bf6d --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureNames.cs @@ -0,0 +1,6 @@ +namespace LINGYUN.Abp.WebhooksManagement.Features; + +public static class WebhooksManagementFeatureNames +{ + public const string GroupName = "WebhooksManagement"; +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationContractsModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationContractsModule.cs new file mode 100644 index 000000000..977589b9c --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationContractsModule.cs @@ -0,0 +1,15 @@ +using Volo.Abp.Application; +using Volo.Abp.Authorization; +using Volo.Abp.Features; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.WebhooksManagement; + +[DependsOn( + typeof(AbpFeaturesModule), + typeof(AbpAuthorizationModule), + typeof(AbpDddApplicationContractsModule), + typeof(WebhooksManagementDomainSharedModule))] +public class WebhooksManagementApplicationContractsModule : AbpModule +{ +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementRemoteServiceConsts.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementRemoteServiceConsts.cs new file mode 100644 index 000000000..f05d02644 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementRemoteServiceConsts.cs @@ -0,0 +1,7 @@ +namespace LINGYUN.Abp.WebhooksManagement; + +public static class WebhooksManagementRemoteServiceConsts +{ + public const string RemoteServiceName = "WebhooksManagement"; + public const string ModuleName = "WebhooksManagement"; +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xml new file mode 100644 index 000000000..be0de3a90 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/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/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN.Abp.WebhooksManagement.Application.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN.Abp.WebhooksManagement.Application.csproj new file mode 100644 index 000000000..24d0a91e7 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN.Abp.WebhooksManagement.Application.csproj @@ -0,0 +1,20 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementAppServiceBase.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementAppServiceBase.cs new file mode 100644 index 000000000..3953ab3e0 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementAppServiceBase.cs @@ -0,0 +1,13 @@ +using LINGYUN.Abp.WebhooksManagement.Localization; +using Volo.Abp.Application.Services; + +namespace LINGYUN.Abp.WebhooksManagement; + +public abstract class WebhooksManagementAppServiceBase : ApplicationService +{ + protected WebhooksManagementAppServiceBase() + { + LocalizationResource = typeof(WebhooksManagementResource); + ObjectMapperContext = typeof(WebhooksManagementApplicationModule); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationMapperProfile.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationMapperProfile.cs new file mode 100644 index 000000000..dd3323fec --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationMapperProfile.cs @@ -0,0 +1,10 @@ +using AutoMapper; + +namespace LINGYUN.Abp.WebhooksManagement; + +public class WebhooksManagementApplicationMapperProfile : Profile +{ + public WebhooksManagementApplicationMapperProfile() + { + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationModule.cs new file mode 100644 index 000000000..eef27929b --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationModule.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Application; +using Volo.Abp.Authorization; +using Volo.Abp.AutoMapper; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.WebhooksManagement; + +[DependsOn( + typeof(AbpAuthorizationModule), + typeof(AbpDddApplicationModule), + typeof(WebhooksManagementDomainModule))] +public class WebhooksManagementApplicationModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAutoMapperObjectMapper(); + + Configure(options => + { + options.AddProfile(validate: true); + }); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xml new file mode 100644 index 000000000..be0de3a90 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/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/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN.Abp.WebhooksManagement.Dapr.Client.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN.Abp.WebhooksManagement.Dapr.Client.csproj new file mode 100644 index 000000000..e07d4c79d --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN.Abp.WebhooksManagement.Dapr.Client.csproj @@ -0,0 +1,16 @@ + + + + + + + net6.0 + + + + + + + + + diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDaprClientModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDaprClientModule.cs new file mode 100644 index 000000000..bb89de573 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDaprClientModule.cs @@ -0,0 +1,18 @@ +using LINGYUN.Abp.Dapr.Client; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.WebhooksManagement; + +[DependsOn( + typeof(AbpDaprClientModule), + typeof(WebhooksManagementApplicationContractsModule))] +public class WebhooksManagementDaprClientModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddDaprClientProxies( + typeof(WebhooksManagementApplicationContractsModule).Assembly, + WebhooksManagementRemoteServiceConsts.RemoteServiceName); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xml new file mode 100644 index 000000000..be0de3a90 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/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/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN.Abp.WebhooksManagement.Domain.Shared.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN.Abp.WebhooksManagement.Domain.Shared.csproj new file mode 100644 index 000000000..682b8eff5 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN.Abp.WebhooksManagement.Domain.Shared.csproj @@ -0,0 +1,25 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + + + + + + diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/en.json b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/en.json new file mode 100644 index 000000000..f31721006 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/en.json @@ -0,0 +1,8 @@ +{ + "culture": "en", + "texts": { + "Features:WebhooksManagement": "WebhooksManagement", + "Permission:WebhooksManagement": "WebhooksManagement", + "Permission:ManageSettings": "Manage Settings" + } +} \ No newline at end of file diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/zh-Hans.json b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/zh-Hans.json new file mode 100644 index 000000000..759c3f745 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/zh-Hans.json @@ -0,0 +1,8 @@ +{ + "culture": "zh-Hans", + "texts": { + "Features:WebhooksManagement": "WebhooksManagement", + "Permission:WebhooksManagement": "WebhooksManagement", + "Permission:ManageSettings": "管理设置" + } +} \ No newline at end of file diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/WebhooksManagementResource.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/WebhooksManagementResource.cs new file mode 100644 index 000000000..c95515cad --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/WebhooksManagementResource.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Localization; + +namespace LINGYUN.Abp.WebhooksManagement.Localization; + +[LocalizationResourceName("WebhooksManagement")] +public class WebhooksManagementResource +{ +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfiguration.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfiguration.cs new file mode 100644 index 000000000..9b810dd32 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfiguration.cs @@ -0,0 +1,16 @@ +using System; +using Volo.Abp.ObjectExtending.Modularity; + +namespace LINGYUN.Abp.WebhooksManagement.ObjectExtending; + +public class WebhooksManagementModuleExtensionConfiguration : ModuleExtensionConfiguration +{ + public WebhooksManagementModuleExtensionConfiguration ConfigureWebhooksManagement( + Action configureAction) + { + return this.ConfigureEntity( + WebhooksManagementModuleExtensionConsts.EntityNames.Entity, + configureAction + ); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfigurationDictionaryExtensions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfigurationDictionaryExtensions.cs new file mode 100644 index 000000000..b41e10ccb --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfigurationDictionaryExtensions.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Volo.Abp.ObjectExtending.Modularity; + +namespace LINGYUN.Abp.WebhooksManagement.ObjectExtending; + +public static class WebhooksManagementModuleExtensionConfigurationDictionaryExtensions +{ + public static ModuleExtensionConfigurationDictionary ConfigureWebhooksManagement( + this ModuleExtensionConfigurationDictionary modules, + Action configureAction) + { + return modules.ConfigureModule( + WebhooksManagementModuleExtensionConsts.ModuleName, + configureAction + ); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConsts.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConsts.cs new file mode 100644 index 000000000..b16fe772e --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConsts.cs @@ -0,0 +1,11 @@ +namespace LINGYUN.Abp.WebhooksManagement.ObjectExtending; + +public static class WebhooksManagementModuleExtensionConsts +{ + public const string ModuleName = "WebhooksManagement"; + + public static class EntityNames + { + public const string Entity = "Entity"; + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainSharedModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainSharedModule.cs new file mode 100644 index 000000000..1e9234e7a --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainSharedModule.cs @@ -0,0 +1,32 @@ +using LINGYUN.Abp.WebhooksManagement.Localization; +using Volo.Abp.Localization; +using Volo.Abp.Localization.ExceptionHandling; +using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; + +namespace LINGYUN.Abp.WebhooksManagement; + +[DependsOn( + typeof(AbpLocalizationModule))] +public class WebhooksManagementDomainSharedModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + + Configure(options => + { + options.Resources + .Add() + .AddVirtualJson("/LINGYUN/Abp/WebhooksManagement/Localization/Resources"); + }); + + Configure(options => + { + options.MapCodeNamespace(WebhooksManagementErrorCodes.Namespace, typeof(WebhooksManagementResource)); + }); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementErrorCodes.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementErrorCodes.cs new file mode 100644 index 000000000..a7d2af4f9 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementErrorCodes.cs @@ -0,0 +1,6 @@ +namespace LINGYUN.Abp.WebhooksManagement; + +public static class WebhooksManagementErrorCodes +{ + public const string Namespace = "WebhooksManagement"; +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xml new file mode 100644 index 000000000..be0de3a90 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/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/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN.Abp.WebhooksManagement.Domain.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN.Abp.WebhooksManagement.Domain.csproj new file mode 100644 index 000000000..2ba7caa77 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN.Abp.WebhooksManagement.Domain.csproj @@ -0,0 +1,22 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + + + diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/DefaultWebhookManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/DefaultWebhookManager.cs new file mode 100644 index 000000000..c2535823c --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/DefaultWebhookManager.cs @@ -0,0 +1,70 @@ +using LINGYUN.Abp.Webhooks; +using System; +using System.Net; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Guids; +using Volo.Abp.Json; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Uow; + +namespace LINGYUN.Abp.WebhooksManagement; + +public class DefaultWebhookManager : WebhookManager, ITransientDependency +{ + protected ICurrentTenant CurrentTenant { get; } + protected IGuidGenerator GuidGenerator { get; } + protected IUnitOfWorkManager UnitOfWorkManager { get; } + protected IWebhookSendRecordRepository WebhookSendAttemptRepository { get; } + public DefaultWebhookManager( + ICurrentTenant currentTenant, + IGuidGenerator guidGenerator, + IJsonSerializer jsonSerializer, + IWebhookSendAttemptStore webhookSendAttemptStore, + IUnitOfWorkManager unitOfWorkManager, + IWebhookSendRecordRepository webhookSendAttemptRepository) + : base(jsonSerializer, webhookSendAttemptStore) + { + CurrentTenant = currentTenant; + GuidGenerator = guidGenerator; + UnitOfWorkManager = unitOfWorkManager; + WebhookSendAttemptRepository = webhookSendAttemptRepository; + } + + public async override Task InsertAndGetIdWebhookSendAttemptAsync(WebhookSenderArgs webhookSenderArgs) + { + using (var uow = UnitOfWorkManager.Begin()) + { + using (CurrentTenant.Change(webhookSenderArgs.TenantId)) + { + var record = new WebhookSendRecord( + GuidGenerator.Create(), + webhookSenderArgs.WebhookEventId, + webhookSenderArgs.WebhookSubscriptionId, + webhookSenderArgs.TenantId); + + await WebhookSendAttemptRepository.InsertAsync(record); + + await uow.SaveChangesAsync(); + + return record.Id; + } + } + } + + public async override Task StoreResponseOnWebhookSendAttemptAsync(Guid webhookSendAttemptId, Guid? tenantId, HttpStatusCode? statusCode, string content) + { + using (var uow = UnitOfWorkManager.Begin()) + { + using (CurrentTenant.Change(tenantId)) + { + var record = await WebhookSendAttemptRepository.GetAsync(webhookSendAttemptId); + record.SetResponse(content, statusCode); + + await WebhookSendAttemptRepository.UpdateAsync(record); + + await uow.SaveChangesAsync(); + } + } + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookEventRecordRepository.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookEventRecordRepository.cs new file mode 100644 index 000000000..45a69d4f2 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookEventRecordRepository.cs @@ -0,0 +1,8 @@ +using System; +using Volo.Abp.Domain.Repositories; + +namespace LINGYUN.Abp.WebhooksManagement; + +public interface IWebhookEventRecordRepository : IRepository +{ +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSendRecordRepository.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSendRecordRepository.cs new file mode 100644 index 000000000..4cf97509f --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSendRecordRepository.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace LINGYUN.Abp.WebhooksManagement; + +public interface IWebhookSendRecordRepository : IRepository +{ + Task GetCountAsync( + WebhookSendRecordFilter filter, + CancellationToken cancellationToken = default); + + Task> GetListAsync( + WebhookSendRecordFilter filter, + string sorting = nameof(WebhookSendRecord.CreationTime), + int maxResultCount = 10, + int skipCount = 10, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSubscriptionRepository.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSubscriptionRepository.cs new file mode 100644 index 000000000..faf4dbbee --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSubscriptionRepository.cs @@ -0,0 +1,8 @@ +using System; +using Volo.Abp.Domain.Repositories; + +namespace LINGYUN.Abp.WebhooksManagement; + +public interface IWebhookSubscriptionRepository : IRepository +{ +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettingDefinitionProvider.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettingDefinitionProvider.cs new file mode 100644 index 000000000..e3aa21264 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettingDefinitionProvider.cs @@ -0,0 +1,11 @@ +using Volo.Abp.Settings; + +namespace LINGYUN.Abp.WebhooksManagement.Settings +{ + public class WebhooksManagementSettingDefinitionProvider : SettingDefinitionProvider + { + public override void Define(ISettingDefinitionContext context) + { + } + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettings.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettings.cs new file mode 100644 index 000000000..1de9fcda9 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettings.cs @@ -0,0 +1,7 @@ +namespace LINGYUN.Abp.WebhooksManagement.Settings +{ + public static class WebhooksManagementSettings + { + public const string GroupName = "WebhooksManagement"; + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventRecord.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventRecord.cs new file mode 100644 index 000000000..e48bacf8c --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventRecord.cs @@ -0,0 +1,30 @@ +using System; +using Volo.Abp.Auditing; +using Volo.Abp.Domain.Entities; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.WebhooksManagement; + +public class WebhookEventRecord : Entity, IMultiTenant, IHasCreationTime, IHasDeletionTime +{ + public virtual Guid? TenantId { get; protected set; } + public virtual string WebhookName { get; protected set; } + public virtual string Data { get; protected set; } + public virtual DateTime CreationTime { get; set; } + public virtual DateTime? DeletionTime { get; set; } + public virtual bool IsDeleted { get; set; } + protected WebhookEventRecord() + { + } + + public WebhookEventRecord( + Guid id, + string webhookName, + string data, + Guid? tenantId = null) : base(id) + { + WebhookName = webhookName; + Data = data; + TenantId = tenantId; + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventStore.cs new file mode 100644 index 000000000..62d864e8b --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventStore.cs @@ -0,0 +1,57 @@ +using LINGYUN.Abp.Webhooks; +using System; +using System.Threading.Tasks; +using Volo.Abp.Domain.Services; +using Volo.Abp.ObjectMapping; +using Volo.Abp.Uow; + +namespace LINGYUN.Abp.WebhooksManagement; + +public class WebhookEventStore : DomainService, IWebhookEventStore +{ + protected IObjectMapper ObjectMapper => LazyServiceProvider.LazyGetRequiredService>(); + + protected IUnitOfWorkManager UnitOfWorkManager { get; } + protected IWebhookEventRecordRepository WebhookEventRepository { get; } + + public WebhookEventStore( + IUnitOfWorkManager unitOfWorkManager, + IWebhookEventRecordRepository webhookEventRepository) + { + UnitOfWorkManager = unitOfWorkManager; + WebhookEventRepository = webhookEventRepository; + } + + public async virtual Task GetAsync(Guid? tenantId, Guid id) + { + using var uow = UnitOfWorkManager.Begin(); + using (CurrentTenant.Change(tenantId)) + { + var record = await WebhookEventRepository.GetAsync(id); + + return ObjectMapper.Map(record); + } + } + + public async virtual Task InsertAndGetIdAsync(WebhookEvent webhookEvent) + { + using var uow = UnitOfWorkManager.Begin(); + using (CurrentTenant.Change(webhookEvent.TenantId)) + { + var record = new WebhookEventRecord( + GuidGenerator.Create(), + webhookEvent.WebhookName, + webhookEvent.Data, + webhookEvent.TenantId) + { + CreationTime = Clock.Now, + }; + + await WebhookEventRepository.InsertAsync(record); + + await uow.SaveChangesAsync(); + + return record.Id; + } + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendAttemptStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendAttemptStore.cs new file mode 100644 index 000000000..8e024decf --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendAttemptStore.cs @@ -0,0 +1,165 @@ +using LINGYUN.Abp.Webhooks; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Domain.Services; +using Volo.Abp.Linq; +using Volo.Abp.ObjectMapping; +using Volo.Abp.Uow; + +namespace LINGYUN.Abp.WebhooksManagement; + +public class WebhookSendAttemptStore : DomainService, IWebhookSendAttemptStore +{ + protected IObjectMapper ObjectMapper => LazyServiceProvider.LazyGetRequiredService>(); + protected IAsyncQueryableExecuter AsyncQueryableExecuter => LazyServiceProvider.LazyGetRequiredService(); + + protected IUnitOfWorkManager UnitOfWorkManager { get; } + protected IWebhookSendRecordRepository WebhookSendAttemptRepository { get; } + + public WebhookSendAttemptStore( + IUnitOfWorkManager unitOfWorkManager, + IWebhookSendRecordRepository webhookSendAttemptRepository) + { + UnitOfWorkManager = unitOfWorkManager; + WebhookSendAttemptRepository = webhookSendAttemptRepository; + } + + public async virtual Task<(int TotalCount, IReadOnlyCollection Webhooks)> GetAllSendAttemptsBySubscriptionAsPagedListAsync( + Guid? tenantId, + Guid subscriptionId, + int maxResultCount, + int skipCount) + { + (int TotalCount, IReadOnlyCollection Webhooks) sendAttempts; + + using (var uow = UnitOfWorkManager.Begin()) + { + using (CurrentTenant.Change(tenantId)) + { + var filter = new WebhookSendRecordFilter + { + SubscriptionId = subscriptionId, + }; + var totalCount = await WebhookSendAttemptRepository.GetCountAsync(filter); + + var list = await WebhookSendAttemptRepository.GetListAsync( + filter, + maxResultCount: maxResultCount, + skipCount: skipCount); + + var webHooks = ObjectMapper.Map, List>(list); + + sendAttempts = ValueTuple.Create(totalCount, webHooks.ToImmutableList()); + } + + await uow.CompleteAsync(); + } + + return sendAttempts; + } + + public async virtual Task> GetAllSendAttemptsByWebhookEventIdAsync( + Guid? tenantId, + Guid webhookEventId) + { + List sendAttempts; + + using (var uow = UnitOfWorkManager.Begin()) + { + using (CurrentTenant.Change(tenantId)) + { + var queryable = await WebhookSendAttemptRepository.GetQueryableAsync(); + + var list = await AsyncQueryableExecuter.ToListAsync(queryable + .Where(attempt => attempt.WebhookEventId == webhookEventId) + .OrderByDescending(attempt => attempt.CreationTime) + ); + + sendAttempts = ObjectMapper.Map, List>(list); + } + + await uow.CompleteAsync(); + } + + return sendAttempts; + } + + public async virtual Task GetAsync( + Guid? tenantId, + Guid id) + { + WebhookSendRecord sendAttempt; + + using (var uow = UnitOfWorkManager.Begin()) + { + using (CurrentTenant.Change(tenantId)) + { + sendAttempt = await WebhookSendAttemptRepository.GetAsync(id); + } + + await uow.CompleteAsync(); + } + + return ObjectMapper.Map(sendAttempt); + } + + public async virtual Task GetSendAttemptCountAsync( + Guid? tenantId, + Guid webhookEventId, + Guid webhookSubscriptionId) + { + int sendAttemptCount; + + using (var uow = UnitOfWorkManager.Begin()) + { + using (CurrentTenant.Change(tenantId)) + { + sendAttemptCount = await WebhookSendAttemptRepository.CountAsync(attempt => + attempt.WebhookEventId == webhookEventId && + attempt.WebhookSubscriptionId == webhookSubscriptionId); + } + + await uow.CompleteAsync(); + } + + return sendAttemptCount; + } + + public async virtual Task HasXConsecutiveFailAsync( + Guid? tenantId, + Guid subscriptionId, + int failCount) + { + bool result; + + using (var uow = UnitOfWorkManager.Begin()) + { + using (CurrentTenant.Change(tenantId)) + { + if (await WebhookSendAttemptRepository.CountAsync(x => x.WebhookSubscriptionId == subscriptionId) < failCount) + { + result = false; + } + else + { + var queryable = await WebhookSendAttemptRepository.GetQueryableAsync(); + + result = !await AsyncQueryableExecuter.AnyAsync(queryable + .OrderByDescending(attempt => attempt.CreationTime) + .Take(failCount) + .Where(attempt => attempt.ResponseStatusCode == HttpStatusCode.OK) + ); + } + } + + await uow.CompleteAsync(); + } + + return result; + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecord.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecord.cs new file mode 100644 index 000000000..91c3c8a97 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecord.cs @@ -0,0 +1,50 @@ +using System; +using System.Net; +using Volo.Abp.Auditing; +using Volo.Abp.Domain.Entities; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.WebhooksManagement; + +public class WebhookSendRecord : Entity, IHasCreationTime, IHasModificationTime, IMultiTenant +{ + public virtual Guid? TenantId { get; protected set; } + + public virtual Guid WebhookEventId { get; protected set; } + + public virtual Guid WebhookSubscriptionId { get; protected set; } + + public virtual string Response { get; protected set; } + + public virtual HttpStatusCode? ResponseStatusCode { get; set; } + + public virtual DateTime CreationTime { get; set; } + + public virtual DateTime? LastModificationTime { get; set; } + + public virtual WebhookEventRecord WebhookEvent { get; protected set; } + + protected WebhookSendRecord() + { + } + + public WebhookSendRecord( + Guid id, + Guid eventId, + Guid subscriptionId, + Guid? tenantId = null) : base(id) + { + WebhookEventId = eventId; + WebhookSubscriptionId = subscriptionId; + + TenantId = tenantId; + } + + public void SetResponse( + string response, + HttpStatusCode? statusCode = null) + { + Response = response; + ResponseStatusCode = statusCode; + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordFilter.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordFilter.cs new file mode 100644 index 000000000..075a6152b --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordFilter.cs @@ -0,0 +1,21 @@ +using System; +using System.Net; + +namespace LINGYUN.Abp.WebhooksManagement; + +public class WebhookSendRecordFilter +{ + public string Filter { get; set; } + + public Guid? WebhookEventId { get; set; } + + public Guid? SubscriptionId { get; set; } + + public string Response { get; set; } + + public HttpStatusCode? ResponseStatusCode { get; set; } + + public DateTime? BeginCreationTime { get; set; } + + public DateTime? EndCreationTime { get; set; } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscription.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscription.cs new file mode 100644 index 000000000..be342aaa3 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscription.cs @@ -0,0 +1,33 @@ +using System; +using Volo.Abp.Domain.Entities.Auditing; + +namespace LINGYUN.Abp.WebhooksManagement; + +public class WebhookSubscription : CreationAuditedEntity +{ + public virtual Guid? TenantId { get; protected set; } + public virtual string WebhookUri { get; protected set; } + public virtual string Secret { get; protected set; } + public virtual bool IsActive { get; set; } + public virtual string Webhooks { get; protected set; } + public virtual string Headers { get; protected set; } + protected WebhookSubscription() + { + } + public WebhookSubscription( + Guid id, + string webhookUri, + string secret, + string webhooks, + string headers, + Guid? tenantId = null) : base(id) + { + WebhookUri = webhookUri; + Secret = secret; + Webhooks = webhooks; + Headers = headers; + TenantId = tenantId; + + IsActive = true; + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionsStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionsStore.cs new file mode 100644 index 000000000..abdbf33be --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionsStore.cs @@ -0,0 +1,55 @@ +using LINGYUN.Abp.Webhooks; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Domain.Services; + +namespace LINGYUN.Abp.WebhooksManagement; + +public class WebhookSubscriptionsStore : DomainService, IWebhookSubscriptionsStore +{ + public Task DeleteAsync(Guid id) + { + throw new NotImplementedException(); + } + + public Task> GetAllSubscriptionsAsync(Guid? tenantId) + { + throw new NotImplementedException(); + } + + public Task> GetAllSubscriptionsAsync(Guid? tenantId, string webhookName) + { + throw new NotImplementedException(); + } + + public Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds) + { + throw new NotImplementedException(); + } + + public Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds, string webhookName) + { + throw new NotImplementedException(); + } + + public Task GetAsync(Guid id) + { + throw new NotImplementedException(); + } + + public Task InsertAsync(WebhookSubscriptionInfo webhookSubscription) + { + throw new NotImplementedException(); + } + + public Task IsSubscribedAsync(Guid? tenantId, string webhookName) + { + throw new NotImplementedException(); + } + + public Task UpdateAsync(WebhookSubscriptionInfo webhookSubscription) + { + throw new NotImplementedException(); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDbProperties.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDbProperties.cs new file mode 100644 index 000000000..685cccb3e --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDbProperties.cs @@ -0,0 +1,11 @@ +namespace LINGYUN.Abp.WebhooksManagement; + +public static class WebhooksManagementDbProperties +{ + public static string DbTablePrefix { get; set; } = "WebhooksManagement_"; + + public static string DbSchema { get; set; } = null; + + + public const string ConnectionStringName = "WebhooksManagement"; +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainMapperProfile.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainMapperProfile.cs new file mode 100644 index 000000000..cbef9c444 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainMapperProfile.cs @@ -0,0 +1,11 @@ +using AutoMapper; + +namespace LINGYUN.Abp.WebhooksManagement; + +public class WebhooksManagementDomainMapperProfile : Profile +{ + public WebhooksManagementDomainMapperProfile() + { + + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainModule.cs new file mode 100644 index 000000000..bdeb77779 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainModule.cs @@ -0,0 +1,43 @@ +using LINGYUN.Abp.Webhooks; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AutoMapper; +using Volo.Abp.Domain.Entities.Events.Distributed; +using Volo.Abp.Modularity; +using Volo.Abp.Threading; + +namespace LINGYUN.Abp.WebhooksManagement; + +[DependsOn( + typeof(AbpAutoMapperModule), + typeof(AbpWebhooksModule), + typeof(WebhooksManagementDomainSharedModule))] +public class WebhooksManagementDomainModule : AbpModule +{ + private static readonly OneTimeRunner OneTimeRunner = new(); + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAutoMapperObjectMapper(); + + Configure(options => + { + options.AddProfile(validate: true); + }); + + Configure(options => + { + }); + } + + public override void PostConfigureServices(ServiceConfigurationContext context) + { + OneTimeRunner.Run(() => + { + // 扩展实体配置 + //ModuleExtensionConfigurationHelper.ApplyEntityConfigurationToEntity( + // WebhooksManagementModuleExtensionConsts.ModuleName, + // WebhooksManagementModuleExtensionConsts.EntityNames.Entity, + // typeof(Entity) + //); + }); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xml new file mode 100644 index 000000000..be0de3a90 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/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/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore.csproj new file mode 100644 index 000000000..6eb433ff2 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore.csproj @@ -0,0 +1,19 @@ + + + + + + + net6.0 + + + + + + + + + + + + diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/IWebhooksManagementDbContext.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/IWebhooksManagementDbContext.cs new file mode 100644 index 000000000..f6d9c17a8 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/IWebhooksManagementDbContext.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; + +namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore; + +[ConnectionStringName(WebhooksManagementDbProperties.ConnectionStringName)] +public interface IWebhooksManagementDbContext : IEfCoreDbContext +{ +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContext.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContext.cs new file mode 100644 index 000000000..de73188aa --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContext.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; + +namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore; + +[ConnectionStringName(WebhooksManagementDbProperties.ConnectionStringName)] +public class WebhooksManagementDbContext : AbpDbContext, IWebhooksManagementDbContext +{ + public WebhooksManagementDbContext( + DbContextOptions options) : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.ConfigureWebhooksManagement(); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContextModelCreatingExtensions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContextModelCreatingExtensions.cs new file mode 100644 index 000000000..fcf22d3e6 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContextModelCreatingExtensions.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore; +using System; +using Volo.Abp; + +namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore; + +public static class WebhooksManagementDbContextModelCreatingExtensions +{ + public static void ConfigureWebhooksManagement( + this ModelBuilder builder, + Action optionsAction = null) + { + Check.NotNull(builder, nameof(builder)); + + var options = new WebhooksManagementModelBuilderConfigurationOptions( + WebhooksManagementDbProperties.DbTablePrefix, + WebhooksManagementDbProperties.DbSchema + ); + optionsAction?.Invoke(options); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEfCoreQueryableExtensions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEfCoreQueryableExtensions.cs new file mode 100644 index 000000000..5751f330f --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEfCoreQueryableExtensions.cs @@ -0,0 +1,6 @@ +namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore; + +public static class WebhooksManagementEfCoreQueryableExtensions +{ + // 在此聚合仓储服务的扩展方法 +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEntityFrameworkCoreModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEntityFrameworkCoreModule.cs new file mode 100644 index 000000000..c64b2bb15 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEntityFrameworkCoreModule.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore; + +[DependsOn( + typeof(WebhooksManagementDomainModule), + typeof(AbpEntityFrameworkCoreModule))] +public class WebhooksManagementEntityFrameworkCoreModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAbpDbContext(options => + { + options.AddDefaultRepositories(); + }); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementModelBuilderConfigurationOptions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementModelBuilderConfigurationOptions.cs new file mode 100644 index 000000000..dc8a2e564 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementModelBuilderConfigurationOptions.cs @@ -0,0 +1,17 @@ +using JetBrains.Annotations; +using Volo.Abp.EntityFrameworkCore.Modeling; + +namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore; + +public class WebhooksManagementModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions +{ + public WebhooksManagementModelBuilderConfigurationOptions( + [NotNull] string tablePrefix = "", + [CanBeNull] string schema = null) + : base( + tablePrefix, + schema) + { + + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xml new file mode 100644 index 000000000..be0de3a90 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/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/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN.Abp.WebhooksManagement.HttpApi.Client.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN.Abp.WebhooksManagement.HttpApi.Client.csproj new file mode 100644 index 000000000..5cee3e037 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN.Abp.WebhooksManagement.HttpApi.Client.csproj @@ -0,0 +1,19 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementHttpApiClientModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementHttpApiClientModule.cs new file mode 100644 index 000000000..1fff62168 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementHttpApiClientModule.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Http.Client; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.WebhooksManagement; + +[DependsOn( + typeof(AbpHttpClientModule), + typeof(WebhooksManagementApplicationContractsModule))] +public class WebhooksManagementHttpApiClientModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddHttpClientProxies( + typeof(WebhooksManagementApplicationContractsModule).Assembly, + WebhooksManagementRemoteServiceConsts.RemoteServiceName); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xml new file mode 100644 index 000000000..be0de3a90 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xsd new file mode 100644 index 000000000..11da52550 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/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/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN.Abp.WebhooksManagement.HttpApi.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN.Abp.WebhooksManagement.HttpApi.csproj new file mode 100644 index 000000000..e2556eb0b --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN.Abp.WebhooksManagement.HttpApi.csproj @@ -0,0 +1,19 @@ + + + + + + + net6.0 + + + + + + + + + + + + diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN/Abp/WebhooksManagement/WebhooksManagementControllerBase.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN/Abp/WebhooksManagement/WebhooksManagementControllerBase.cs new file mode 100644 index 000000000..5eef82130 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN/Abp/WebhooksManagement/WebhooksManagementControllerBase.cs @@ -0,0 +1,12 @@ +using LINGYUN.Abp.WebhooksManagement.Localization; +using Volo.Abp.AspNetCore.Mvc; + +namespace LINGYUN.Abp.WebhooksManagement; + +public abstract class WebhooksManagementControllerBase : AbpControllerBase +{ + protected WebhooksManagementControllerBase() + { + LocalizationResource = typeof(WebhooksManagementResource); + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN/Abp/WebhooksManagement/WebhooksManagementHttpApiModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN/Abp/WebhooksManagement/WebhooksManagementHttpApiModule.cs new file mode 100644 index 000000000..e4e4699a7 --- /dev/null +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN/Abp/WebhooksManagement/WebhooksManagementHttpApiModule.cs @@ -0,0 +1,40 @@ +using LINGYUN.Abp.WebhooksManagement.Localization; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.Localization; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; +using Volo.Abp.Validation.Localization; + +namespace LINGYUN.Abp.WebhooksManagement; + +[DependsOn( + typeof(AbpAspNetCoreMvcModule), + typeof(WebhooksManagementApplicationContractsModule))] +public class WebhooksManagementHttpApiModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + PreConfigure(mvcBuilder => + { + mvcBuilder.AddApplicationPartIfNotExists(typeof(WebhooksManagementHttpApiModule).Assembly); + }); + + PreConfigure(options => + { + options.AddAssemblyResource( + typeof(WebhooksManagementResource), + typeof(WebhooksManagementApplicationContractsModule).Assembly); + }); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.Resources + .Get() + .AddBaseTypes(typeof(AbpValidationResource)); + }); + } +} diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Dockerfile b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Dockerfile index a38f2b0c8..3e9c90691 100644 --- a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Dockerfile +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Dockerfile @@ -7,6 +7,10 @@ COPY . /app #东8区 ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo '$TZ' > /etc/timezone +## 有些定时作业可能需要建立独立的数据库连接, 可能跨不同的数据库, 配置一下MSSQL +## 解决连接SqlServer TLS版本过高问题 +RUN sed -i 's/TLSv1.2/TLSv1.0/g' /etc/ssl/openssl.cnf +RUN sed -i 's/TLSv1.2/TLSv1.0/g' /usr/lib/ssl/openssl.cnf EXPOSE 80/tcp VOLUME [ "./app/Logs" ] diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj index 98bc88491..293653249 100644 --- a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj +++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj @@ -45,6 +45,8 @@ + + diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Controllers/HomeController.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Controllers/HomeController.cs new file mode 100644 index 000000000..5084440c9 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Controllers/HomeController.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; + +namespace LY..WebhooksManagement.Controllers; + +public class HomeController : AbpController +{ + public IActionResult Index() + { + return Redirect("/swagger/index.html"); + } +} diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/DataSeeder/WebhooksManagementDataSeederWorker.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/DataSeeder/WebhooksManagementDataSeederWorker.cs new file mode 100644 index 000000000..77ffbf33f --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/DataSeeder/WebhooksManagementDataSeederWorker.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.Hosting; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Data; + +namespace LY..WebhooksManagement.DataSeeder; + +public class WebhooksManagementDataSeederWorker : BackgroundService +{ + protected IDataSeeder DataSeeder { get; } + + public WebhooksManagementDataSeederWorker(IDataSeeder dataSeeder) + { + DataSeeder = dataSeeder; + } + + protected async override Task ExecuteAsync(CancellationToken stoppingToken) + { + await DataSeeder.SeedAsync(); + } +} diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Dockerfile b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Dockerfile new file mode 100644 index 000000000..0d02e98ca --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Dockerfile @@ -0,0 +1,15 @@ +FROM mcr.microsoft.com/dotnet/aspnet:6.0 +LABEL maintainer="colin.in@foxmail.com" +WORKDIR /app + +COPY . /app + +#�Ϻ�ʱ�� +#ENV TZ=Asia/Shanghai +#RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo '$TZ' > /etc/timezone + +EXPOSE 80/tcp +VOLUME [ "./app/Logs" ] +VOLUME [ "./app/Modules" ] + +ENTRYPOINT ["dotnet", "LY..WebhooksManagement.HttpApi.Host.dll"] diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EntityFrameworkCore/WebhooksManagementMigrationsDbContext.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EntityFrameworkCore/WebhooksManagementMigrationsDbContext.cs new file mode 100644 index 000000000..61e3ae739 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EntityFrameworkCore/WebhooksManagementMigrationsDbContext.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace LY..WebhooksManagement.EntityFrameworkCore; + +public class WebhooksManagementMigrationsDbContext : AbpDbContext +{ + public WebhooksManagementMigrationsDbContext(DbContextOptions options) + : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.ConfigureWebhooksManagement(); + } +} diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EntityFrameworkCore/WebhooksManagementMigrationsDbContextFactory.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EntityFrameworkCore/WebhooksManagementMigrationsDbContextFactory.cs new file mode 100644 index 000000000..cd4432128 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EntityFrameworkCore/WebhooksManagementMigrationsDbContextFactory.cs @@ -0,0 +1,32 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; +using System.IO; + +namespace LY..WebhooksManagement.EntityFrameworkCore; + +public class WebhooksManagementMigrationsDbContextFactory : IDesignTimeDbContextFactory +{ + public WebhooksManagementMigrationsDbContext CreateDbContext(string[] args) + { + var configuration = BuildConfiguration(); + var connectionString = configuration.GetConnectionString("WebhooksManagement"); + + DbContextOptionsBuilder builder = null; + + builder = new DbContextOptionsBuilder() + .UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)); + + return new WebhooksManagementMigrationsDbContext(builder.Options); + } + + private static IConfigurationRoot BuildConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false) + .AddJsonFile("appsettings.Development.json", optional: true); + + return builder.Build(); + } +} diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs new file mode 100644 index 000000000..ccdd31c65 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs @@ -0,0 +1,69 @@ +using LY..WebhooksManagement.EntityFrameworkCore; +using LINGYUN.Abp.Data.DbMigrator; +using LINGYUN.Abp.MultiTenancy; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Uow; + +namespace LY..WebhooksManagement.EventBus.Handlers; + +public class TenantSynchronizer : + IDistributedEventHandler, + ITransientDependency +{ + protected IDataSeeder DataSeeder { get; } + protected ICurrentTenant CurrentTenant { get; } + protected IDbSchemaMigrator DbSchemaMigrator { get; } + protected IUnitOfWorkManager UnitOfWorkManager { get; } + + protected ILogger Logger { get; } + + public TenantSynchronizer( + IDataSeeder dataSeeder, + ICurrentTenant currentTenant, + IDbSchemaMigrator dbSchemaMigrator, + IUnitOfWorkManager unitOfWorkManager, + ILogger logger) + { + DataSeeder = dataSeeder; + CurrentTenant = currentTenant; + DbSchemaMigrator = dbSchemaMigrator; + UnitOfWorkManager = unitOfWorkManager; + + Logger = logger; + } + + /// + /// 租户创建之后需要预置种子数据 + /// + /// + /// + public virtual async Task HandleEventAsync(CreateEventData eventData) + { + using (var unitOfWork = UnitOfWorkManager.Begin()) + { + using (CurrentTenant.Change(eventData.Id, eventData.Name)) + { + Logger.LogInformation("Migrating the new tenant database with LY..WebhooksManagement..."); + // 迁移租户数据 + await DbSchemaMigrator.MigrateAsync( + (connectionString, builder) => + { + builder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)); + + return new WebhooksManagementDbContext(builder.Options); + }); + Logger.LogInformation("Migrated the new tenant database with LY..WebhooksManagement..."); + + await DataSeeder.SeedAsync(new DataSeedContext(eventData.Id)); + + await unitOfWork.SaveChangesAsync(); + } + } + } +} diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/LY.MicroService.WebhooksManagement.HttpApi.Host.csproj b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/LY.MicroService.WebhooksManagement.HttpApi.Host.csproj new file mode 100644 index 000000000..b108df60e --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/LY.MicroService.WebhooksManagement.HttpApi.Host.csproj @@ -0,0 +1,66 @@ + + + + net6.0 + LY..WebhooksManagement + + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Program.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Program.cs new file mode 100644 index 000000000..f55da1841 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Program.cs @@ -0,0 +1,58 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Serilog; +using System.IO; +using System.Threading.Tasks; +using Volo.Abp.IO; +using Volo.Abp.Modularity.PlugIns; + +namespace LY..WebhooksManagement; + +public class Program +{ + public async static Task Main(string[] args) + { + try + { + Log.Information("Starting web host."); + + var builder = WebApplication.CreateBuilder(args); + builder.Host.AddAppSettingsSecretsJson() + .UseAutofac() + .ConfigureAppConfiguration((context, config) => + { + var configuration = config.Build(); + if (configuration.GetSection("AgileConfig").Exists()) + { + config.AddAgileConfig(new AgileConfig.Client.ConfigClient(configuration)); + } + }) + .UseSerilog((context, provider, config) => + { + config.ReadFrom.Configuration(context.Configuration); + }); + await builder.AddApplicationAsync(options => + { + // 搜索 Modules 目录下所有文件作为插件 + // 取消显示引用所有其他项目的模块,改为通过插件的形式引用 + var pluginFolder = Path.Combine( + Directory.GetCurrentDirectory(), "Modules"); + DirectoryHelper.CreateIfNotExists(pluginFolder); + options.PlugInSources.AddFolder( + pluginFolder, + SearchOption.AllDirectories); + }); + var app = builder.Build(); + await app.InitializeApplicationAsync(); + await app.RunAsync(); + return 0; + } + finally + { + Log.CloseAndFlush(); + } + } +} diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Properties/launchSettings.json b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Properties/launchSettings.json new file mode 100644 index 000000000..6a691ee60 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Properties/launchSettings.json @@ -0,0 +1,28 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:20890", + "sslPort": 0 + } + }, + "profiles": { + "LY..WebhooksManagement.HttpApi.Host": { + "commandName": "Project", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:57264", + "dotnetRunMessages": "true" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/TenantHeaderParamter.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/TenantHeaderParamter.cs new file mode 100644 index 000000000..e793c0e5c --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/TenantHeaderParamter.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.Options; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System.Collections.Generic; +using Volo.Abp.MultiTenancy; + +namespace LY..WebhooksManagement; + +public class TenantHeaderParamter : IOperationFilter +{ + private readonly AbpMultiTenancyOptions _options; + public TenantHeaderParamter( + IOptions options) + { + _options = options.Value; + } + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + if (_options.IsEnabled) + { + operation.Parameters = operation.Parameters ?? new List(); + operation.Parameters.Add(new OpenApiParameter + { + Name = TenantResolverConsts.DefaultTenantKey, + In = ParameterLocation.Header, + Description = "Tenant Id/Name", + Required = false + }); + } + } +} diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.Configure.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.Configure.cs new file mode 100644 index 000000000..95705fa29 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.Configure.cs @@ -0,0 +1,307 @@ +using DotNetCore.CAP; +using LINGYUN.Abp.Dapr.Client.DynamicProxying; +using LINGYUN.Abp.ExceptionHandling; +using LINGYUN.Abp.ExceptionHandling.Emailing; +using LINGYUN.Abp.Serilog.Enrichers.Application; +using LINGYUN.Abp.Serilog.Enrichers.UniqueId; +using LINGYUN.Abp.Wrapper; +using Medallion.Threading; +using Medallion.Threading.Redis; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.Caching.StackExchangeRedis; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.OpenApi.Models; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; +using StackExchange.Redis; +using System; +using System.Text.Encodings.Web; +using System.Text.Unicode; +using Volo.Abp; +using Volo.Abp.Auditing; +using Volo.Abp.Caching; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.GlobalFeatures; +using Volo.Abp.Json; +using Volo.Abp.Json.SystemTextJson; +using Volo.Abp.Localization; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Threading; +using Volo.Abp.VirtualFileSystem; + +namespace LY..WebhooksManagement; + +public partial class WebhooksManagementHttpApiHostModule +{ + protected const string ApplicationName = "WebhooksManagement"; + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + private void PreConfigureFeature() + { + OneTimeRunner.Run(() => + { + GlobalFeatureManager.Instance.Modules.Editions().EnableAll(); + }); + } + + private void PreConfigureApp() + { + AbpSerilogEnrichersConsts.ApplicationName = ApplicationName; + + PreConfigure(options => + { + // 以开放端口区别 + options.SnowflakeIdOptions.WorkerId = 5000; + options.SnowflakeIdOptions.WorkerIdBits = 5; + options.SnowflakeIdOptions.DatacenterId = 1; + }); + } + + private void PreConfigureCAP(IConfiguration configuration) + { + PreConfigure(options => + { + options + .UseMySql(sqlOptions => + { + configuration.GetSection("CAP:MySql").Bind(sqlOptions); + }) + .UseRabbitMQ(rabbitMQOptions => + { + configuration.GetSection("CAP:RabbitMQ").Bind(rabbitMQOptions); + }) + .UseDashboard(); + }); + } + + private void ConfigureDbContext() + { + // 配置Ef + Configure(options => + { + options.UseMySQL(); + }); + } + + private void ConfigureJsonSerializer() + { + // 解决某些不支持类型的序列化 + Configure(options => + { + options.DefaultDateTimeFormat = "yyyy-MM-dd HH:mm:ss"; + }); + // 中文序列化的编码问题 + Configure(options => + { + options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All); + }); + } + + private void ConfigureDistributedLock(IServiceCollection services, IConfiguration configuration) + { + if (configuration.GetSection("DistributedLock").Exists()) + { + var redis = ConnectionMultiplexer.Connect(configuration["DistributedLock:Redis:Configuration"]); + services.AddSingleton(_ => new RedisDistributedSynchronizationProvider(redis.GetDatabase())); + } + } + + private void ConfigureOpenTelemetry(IServiceCollection services, IConfiguration configuration) + { + if (configuration.GetSection("OpenTelemetry").Exists()) + { + services.AddOpenTelemetryTracing(cfg => + { + cfg.AddSource(ApplicationName) + .SetResourceBuilder( + ResourceBuilder.CreateDefault().AddService(ApplicationName)) + .AddHttpClientInstrumentation() + .AddAspNetCoreInstrumentation() + .AddEntityFrameworkCoreInstrumentation() + .AddCapInstrumentation() + .AddZipkinExporter(zipKinOptions => + { + var endpoint = configuration["OpenTelemetry:ZipKin:Endpoint"]; + if (!endpoint.IsNullOrWhiteSpace()) + { + zipKinOptions.Endpoint = new Uri(configuration["OpenTelemetry:ZipKin:Endpoint"]); + } + }); + }); + } + } + + private void ConfigureExceptionHandling() + { + // 自定义需要处理的异常 + Configure(options => + { + // 加入需要处理的异常类型 + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + }); + // 自定义需要发送邮件通知的异常类型 + Configure(options => + { + // 是否发送堆栈信息 + options.SendStackTrace = true; + // 未指定异常接收者的默认接收邮件 + // 指定自己的邮件地址 + }); + } + + private void ConfigureAuditing(IConfiguration configuration) + { + Configure(options => + { + options.ApplicationName = "WebhooksManagement"; + // 是否启用实体变更记录 + var entitiesChangedConfig = configuration.GetSection("App:TrackingEntitiesChanged"); + if (entitiesChangedConfig.Exists() && entitiesChangedConfig.Get()) + { + options + .EntityHistorySelectors + .AddAllEntities(); + } + }); + } + + private void ConfigureCaching(IConfiguration configuration) + { + Configure(options => + { + // 最好统一命名,不然某个缓存变动其他应用服务有例外发生 + options.KeyPrefix = "LINGYUN.Abp.Application"; + // 滑动过期30天 + options.GlobalCacheEntryOptions.SlidingExpiration = TimeSpan.FromDays(30d); + // 绝对过期60天 + options.GlobalCacheEntryOptions.AbsoluteExpiration = DateTimeOffset.Now.AddDays(60d); + }); + + Configure(options => + { + var redisConfig = ConfigurationOptions.Parse(options.Configuration); + options.ConfigurationOptions = redisConfig; + options.InstanceName = configuration["Redis:InstanceName"]; + }); + } + + private void ConfigureVirtualFileSystem() + { + Configure(options => + { + options.FileSets.AddEmbedded("LY..WebhooksManagement"); + }); + } + + private void ConfigureMultiTenancy(IConfiguration configuration) + { + // 多租户 + Configure(options => + { + options.IsEnabled = true; + }); + + var tenantResolveCfg = configuration.GetSection("App:Domains"); + if (tenantResolveCfg.Exists()) + { + Configure(options => + { + var domains = tenantResolveCfg.Get(); + foreach (var domain in domains) + { + options.AddDomainTenantResolver(domain); + } + }); + } + } + + private void ConfigureSwagger(IServiceCollection services) + { + // Swagger + services.AddSwaggerGen( + options => + { + options.SwaggerDoc("v1", new OpenApiInfo { Title = "WebhooksManagement API", Version = "v1" }); + options.DocInclusionPredicate((docName, description) => true); + options.CustomSchemaIds(type => type.FullName); + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", + Name = "Authorization", + In = ParameterLocation.Header, + Scheme = "bearer", + Type = SecuritySchemeType.Http, + BearerFormat = "JWT" + }); + options.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } + }, + new string[] { } + } + }); + options.OperationFilter(); + }); + } + + private void ConfigureLocalization() + { + // 支持本地化语言类型 + Configure(options => + { + options.Languages.Add(new LanguageInfo("en", "en", "English")); + options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文")); + // 动态语言支持 + options.Resources.AddDynamic(); + }); + } + + private void ConfigureSecurity(IServiceCollection services, IConfiguration configuration, bool isDevelopment = false) + { + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + options.Authority = configuration["AuthServer:Authority"]; + options.RequireHttpsMetadata = false; + options.Audience = configuration["AuthServer:ApiName"]; + }); + + if (!isDevelopment) + { + var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]); + services + .AddDataProtection() + .SetApplicationName("LINGYUN.Abp.Application") + .PersistKeysToStackExchangeRedis(redis, "LINGYUN.Abp.Application:DataProtection:Protection-Keys"); + } + } + + private void ConfigureWrapper() + { + Configure(options => + { + // 取消注释包装结果 + // options.IsEnabled = true; + }); + + Configure(options => + { + // dapr服务间调用发送不需要包装结果的请求头 + options.ProxyRequestActions.Add( + (appId, httpRequestMessage) => + { + httpRequestMessage.Headers.TryAddWithoutValidation(AbpHttpWrapConsts.AbpDontWrapResult, "true"); + }); + }); + } +} diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.DataSeeder.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.DataSeeder.cs new file mode 100644 index 000000000..8c2982179 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.DataSeeder.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.DependencyInjection; +using LY..WebhooksManagement.DataSeeder; + +namespace LY..WebhooksManagement; + +public partial class WebhooksManagementHttpApiHostModule +{ + private static void ConfigureSeedWorker(IServiceCollection services, bool isDevelopment = false) + { + if (isDevelopment) + { + services.AddHostedService(); + } + } +} diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.cs new file mode 100644 index 000000000..4dac2cea9 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.cs @@ -0,0 +1,118 @@ +using LINGYUN.Abp.AspNetCore.Mvc.Wrapper; +using LINGYUN.Abp.AuditLogging.Elasticsearch; +using LINGYUN.Abp.EventBus.CAP; +using LINGYUN.Abp.ExceptionHandling.Emailing; +using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; +using LINGYUN.Abp.Saas.EntityFrameworkCore; +using LINGYUN.Abp.Serilog.Enrichers.Application; +using LINGYUN.Abp.Serilog.Enrichers.UniqueId; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using LY..WebhooksManagement.EntityFrameworkCore; +using LY..WebhooksManagement.SettingManagement; +using Volo.Abp; +using Volo.Abp.AspNetCore.Authentication.JwtBearer; +using Volo.Abp.AspNetCore.MultiTenancy; +using Volo.Abp.AspNetCore.Serilog; +using Volo.Abp.Autofac; +using Volo.Abp.Caching.StackExchangeRedis; +using Volo.Abp.DistributedLocking; +using Volo.Abp.EntityFrameworkCore.MySQL; +using Volo.Abp.FeatureManagement.EntityFrameworkCore; +using Volo.Abp.Http.Client.IdentityModel.Web; +using Volo.Abp.Modularity; +using Volo.Abp.PermissionManagement.EntityFrameworkCore; +using Volo.Abp.SettingManagement.EntityFrameworkCore; +using Volo.Abp.Swashbuckle; + +namespace LY.WebhooksManagement; + +[DependsOn( + typeof(AbpSerilogEnrichersApplicationModule), + typeof(AbpSerilogEnrichersUniqueIdModule), + typeof(AbpAuditLoggingElasticsearchModule), + typeof(AbpAspNetCoreSerilogModule), + typeof(WebhooksManagementApplicationModule), + typeof(WebhooksManagementHttpApiModule), + typeof(WebhooksManagementEntityFrameworkCoreModule), + typeof(WebhooksManagementSettingManagementModule), + typeof(AbpEntityFrameworkCoreMySQLModule), + typeof(AbpAspNetCoreAuthenticationJwtBearerModule), + typeof(AbpEmailingExceptionHandlingModule), + typeof(AbpCAPEventBusModule), + typeof(AbpHttpClientIdentityModelWebModule), + typeof(AbpAspNetCoreMultiTenancyModule), + typeof(AbpSaasEntityFrameworkCoreModule), + typeof(AbpFeatureManagementEntityFrameworkCoreModule), + typeof(AbpPermissionManagementEntityFrameworkCoreModule), + typeof(AbpSettingManagementEntityFrameworkCoreModule), + typeof(AbpLocalizationManagementEntityFrameworkCoreModule), + typeof(AbpCachingStackExchangeRedisModule), + typeof(AbpDistributedLockingModule), + typeof(AbpAspNetCoreMvcWrapperModule), + typeof(AbpSwashbuckleModule), + typeof(AbpAutofacModule) + )] +public partial class WebhooksManagementHttpApiHostModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + var configuration = context.Services.GetConfiguration(); + + PreConfigureApp(); + PreConfigureFeature(); + PreConfigureCAP(configuration); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + var hostingEnvironment = context.Services.GetHostingEnvironment(); + var configuration = context.Services.GetConfiguration(); + + ConfigureWrapper(); + ConfigureDbContext(); + ConfigureLocalization(); + ConfigureJsonSerializer(); + ConfigureExceptionHandling(); + ConfigureVirtualFileSystem(); + ConfigureCaching(configuration); + ConfigureAuditing(configuration); + ConfigureMultiTenancy(configuration); + ConfigureSwagger(context.Services); + ConfigureOpenTelemetry(context.Services, configuration); + ConfigureDistributedLock(context.Services, configuration); + ConfigureSeedWorker(context.Services, hostingEnvironment.IsDevelopment()); + ConfigureSecurity(context.Services, configuration, hostingEnvironment.IsDevelopment()); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + var app = context.GetApplicationBuilder(); + var env = context.GetEnvironment(); + + app.UseCorrelationId(); + app.UseStaticFiles(); + app.UseRouting(); + app.UseCors(); + app.UseAuthentication(); + app.UseJwtTokenMiddleware(); + app.UseMultiTenancy(); + app.UseMapRequestLocalization(); + app.UseAuthorization(); + app.UseSwagger(); + app.UseAbpSwaggerUI(options => + { + options.SwaggerEndpoint("/swagger/v1/swagger.json", "Support APP API"); + + var configuration = context.GetConfiguration(); + options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]); + options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]); + options.OAuthScopes("WebhooksManagement"); + }); + app.UseAuditing(); + app.UseAbpSerilogEnrichers(); + app.UseConfiguredEndpoints(); + } +} diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/appsettings.Development.json b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/appsettings.Development.json new file mode 100644 index 000000000..7c28c17fe --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/appsettings.Development.json @@ -0,0 +1,132 @@ +{ + "AgileConfig": { + "env": "DEV", + "appId": "LY.MicroService.WebhooksManagement", + "secret": "1q2w3E*", + "nodes": "http://127.0.0.1:15000", + "name": "LY.MicroService.WebhooksManagement", + "tag": "LY.MicroService.WebhooksManagement" + }, + "App": { + "TrackingEntitiesChanged": true + }, + "ConnectionStrings": { + "Default": "Server=127.0.0.1;Database=WebhooksManagement;User Id=root;Password=123456", + "WebhooksManagement": "Server=127.0.0.1;Database=WebhooksManagement;User Id=root;Password=123456", + "AbpSaas": "Server=127.0.0.1;Database=WebhooksManagement;User Id=root;Password=123456", + "AbpFeatureManagement": "Server=127.0.0.1;Database=WebhooksManagement;User Id=root;Password=123456", + "AbpPermissionManagement": "Server=127.0.0.1;Database=WebhooksManagement;User Id=root;Password=123456", + "AbpSettingManagement": "Server=127.0.0.1;Database=WebhooksManagement;User Id=root;Password=123456" + }, + "DistributedLock": { + "Redis": { + "Configuration": "127.0.0.1,defaultDatabase=15" + } + }, + "OpenTelemetry": { + "ZipKin": { + "Endpoint": "http://127.0.0.1:9411/api/v2/spans" + } + }, + "RemoteServices": {}, + "IdentityClients": { + "InternalServiceClient": { + "Authority": "http://127.0.0.1:44385", + "RequireHttps": false, + "GrantType": "client_credentials", + "Scope": "lingyun-abp-application", + "ClientId": "InternalServiceClient", + "ClientSecret": "1q2w3E*" + } + }, + "CAP": { + "EventBus": { + "DefaultGroupName": "WebhooksManagement", + "GroupNamePrefix": "Dev", + "Version": "v1", + "FailedRetryInterval": 300, + "FailedRetryCount": 10 + }, + "MySql": { + "TableNamePrefix": "WebhooksManagement_cap", + "ConnectionString": "Server=127.0.0.1;Database=WebhooksManagement;User Id=root;Password=123456" + }, + "SqlServer": { + "TableNamePrefix": "WebhooksManagement_cap", + "ConnectionString": "Server=127.0.0.1;Database=WebhooksManagement;User Id=root;Password=123456" + }, + "Sqlite": { + "TableNamePrefix": "WebhooksManagement_cap", + "ConnectionString": "Data Source=./cap.db" + }, + "Oracle": { + "TableNamePrefix": "WebhooksManagement_cap", + "ConnectionString": "Data Source=WebhooksManagement;Integrated Security=yes;" + }, + "PostgreSql": { + "TableNamePrefix": "WebhooksManagement_cap", + "ConnectionString": "Host=localhost;Port=5432;Database=WebhooksManagement;User ID=root;Password=123456;" + }, + "RabbitMQ": { + "HostName": "127.0.0.1", + "Port": 5672, + "UserName": "admin", + "Password": "123456", + "ExchangeName": "LINGYUN.Abp.Application", + "VirtualHost": "/" + } + }, + "Redis": { + "Configuration": "127.0.0.1,defaultDatabase=10", + "InstanceName": "LINGYUN.Abp.Application" + }, + "AuthServer": { + "Authority": "http://127.0.0.1:44385/", + "ApiName": "lingyun-abp-application", + "SwaggerClientId": "InternalServiceClient", + "SwaggerClientSecret": "1q2w3E*" + }, + "Logging": { + "Serilog": { + "Elasticsearch": { + "IndexFormat": "abp.dev.logging-{0:yyyy.MM.dd}" + } + } + }, + "AuditLogging": { + "Elasticsearch": { + "IndexPrefix": "abp.dev.auditing" + } + }, + "Elasticsearch": { + "NodeUris": "http://127.0.0.1:9200" + }, + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "System": "Warning", + "Microsoft": "Warning", + "DotNetCore": "Debug" + } + }, + "WriteTo": [ + { + "Name": "Console", + "Args": { + "restrictedToMinimumLevel": "Debug", + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "Elasticsearch", + "Args": { + "nodeUris": "http://127.0.0.1:9200", + "indexFormat": "abp.dev.logging-{0:yyyy.MM.dd}", + "autoRegisterTemplate": true, + "autoRegisterTemplateVersion": "ESv7" + } + } + ] + } +} diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/appsettings.json b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/appsettings.json new file mode 100644 index 000000000..67e1bc4bd --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/appsettings.json @@ -0,0 +1,80 @@ +{ + "StringEncryption": { + "DefaultPassPhrase": "s46c5q55nxpeS8Ra", + "InitVectorBytes": "s83ng0abvd02js84", + "DefaultSalt": "sf&5)s3#" + }, + "AllowedHosts": "*", + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "Microsoft.EntityFrameworkCore": "Debug", + "System": "Information", + "Microsoft": "Information" + } + }, + "Enrich": [ "FromLogContext", "WithProcessId", "WithThreadId", "WithEnvironmentName", "WithMachineName", "WithApplicationName", "WithUniqueId" ], + "WriteTo": [ + { + "Name": "Console", + "Args": { + "initialMinimumLevel": "Verbose", + "standardErrorFromLevel": "Verbose", + "restrictedToMinimumLevel": "Verbose", + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "Logs/Debug-.log", + "restrictedToMinimumLevel": "Debug", + "rollingInterval": "Day", + "fileSizeLimitBytes": 5242880, + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "Logs/Info-.log", + "restrictedToMinimumLevel": "Information", + "rollingInterval": "Day", + "fileSizeLimitBytes": 5242880, + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "Logs/Warn-.log", + "restrictedToMinimumLevel": "Warning", + "rollingInterval": "Day", + "fileSizeLimitBytes": 5242880, + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "Logs/Error-.log", + "restrictedToMinimumLevel": "Error", + "rollingInterval": "Day", + "fileSizeLimitBytes": 5242880, + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "Logs/Fatal-.log", + "restrictedToMinimumLevel": "Fatal", + "rollingInterval": "Day", + "fileSizeLimitBytes": 5242880, + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}" + } + } + ] + } +} diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/dapr.sh b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/dapr.sh new file mode 100644 index 000000000..ddfd59d01 --- /dev/null +++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/dapr.sh @@ -0,0 +1 @@ +dapr run --app-id WebhooksManagement --app-port 57264 -H 63751 -- dotnet run --no-build \ No newline at end of file