Merge branch 'develop' into feature/webhooks-ui
This commit is contained in:
commit
a6c730308d
12
.github/workflows/4testing-multi-build.yml
vendored
12
.github/workflows/4testing-multi-build.yml
vendored
@ -25,13 +25,21 @@ jobs:
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build 4testing
|
||||
run: |
|
||||
export BUILD_NUMBER="$(date "+%Y%m%d%H")"
|
||||
cd ./build/install/docker
|
||||
REPO="onlyoffice" \
|
||||
DOCKER_IMAGE_PREFIX="4testing-docspace" \
|
||||
DOCKER_TAG="develop" \
|
||||
DOCKER_TAG=$GITHUB_REF_NAME \
|
||||
DOCKERFILE="Dockerfile.app" \
|
||||
docker buildx bake -f build.yml \
|
||||
--set *.args.GIT_BRANCH="develop" \
|
||||
--set *.args.GIT_BRANCH=$GITHUB_REF_NAME \
|
||||
--set *.platform=linux/amd64 \
|
||||
--push
|
||||
REPO="onlyoffice" \
|
||||
DOCKER_IMAGE_PREFIX="4testing-docspace" \
|
||||
DOCKER_TAG=$GITHUB_REF_NAME-$BUILD_NUMBER \
|
||||
DOCKERFILE="Dockerfile.app" \
|
||||
docker buildx bake -f build.yml \
|
||||
--set *.args.GIT_BRANCH=$GITHUB_REF_NAME \
|
||||
--push
|
||||
shell: bash
|
||||
|
@ -71,8 +71,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Data.Backup", "common\s
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Webhooks.Core", "common\ASC.Webhooks.Core\ASC.Webhooks.Core.csproj", "{760BFF3A-1A67-43A1-A94C-78D11A4BB1E6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Webhooks.Service", "common\services\ASC.Webhooks.Service\ASC.Webhooks.Service.csproj", "{DAE2912D-1465-4D60-B1D7-90EE835003E4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Data.Backup.BackgroundTasks", "common\services\ASC.Data.Backup.BackgroundTasks\ASC.Data.Backup.BackgroundTasks.csproj", "{C0C28A02-943C-4A38-B474-A2B49C6201ED}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.EventBus", "common\ASC.EventBus\ASC.EventBus.csproj", "{26540DA7-604B-474B-97BA-9CDC85A84B6D}"
|
||||
@ -219,10 +217,6 @@ Global
|
||||
{760BFF3A-1A67-43A1-A94C-78D11A4BB1E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{760BFF3A-1A67-43A1-A94C-78D11A4BB1E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{760BFF3A-1A67-43A1-A94C-78D11A4BB1E6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DAE2912D-1465-4D60-B1D7-90EE835003E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DAE2912D-1465-4D60-B1D7-90EE835003E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DAE2912D-1465-4D60-B1D7-90EE835003E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DAE2912D-1465-4D60-B1D7-90EE835003E4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C0C28A02-943C-4A38-B474-A2B49C6201ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C0C28A02-943C-4A38-B474-A2B49C6201ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C0C28A02-943C-4A38-B474-A2B49C6201ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
@ -20,6 +20,7 @@
|
||||
"common\\ASC.MessagingSystem\\ASC.MessagingSystem.csproj",
|
||||
"common\\ASC.Notify.Textile\\ASC.Notify.Textile.csproj",
|
||||
"common\\ASC.Textile\\ASC.Textile.csproj",
|
||||
"common\\ASC.Webhooks.Core\\ASC.Webhooks.Core.csproj",
|
||||
"common\\services\\ASC.ApiCache\\ASC.ApiCache.csproj",
|
||||
"common\\services\\ASC.ApiSystem\\ASC.ApiSystem.csproj",
|
||||
"common\\services\\ASC.AuditTrail\\ASC.AuditTrail.csproj",
|
||||
|
@ -43,7 +43,7 @@
|
||||
DOCUMENT_SERVER_URL_INTERNAL=http://${DOCUMENT_SERVER_HOST}/
|
||||
|
||||
MYSQL_ROOT_PASSWORD=my-secret-pw
|
||||
MYSQL_DATABASE=${PRODUCT}
|
||||
MYSQL_DATABASE=docspace
|
||||
MYSQL_USER=${PRODUCT}_user
|
||||
MYSQL_PASSWORD=${PRODUCT}_pass
|
||||
MYSQL_HOST=${CONTAINER_PREFIX}mysql-server
|
||||
|
@ -45,6 +45,7 @@ public abstract class BaseStartup
|
||||
protected DIHelper DIHelper { get; }
|
||||
protected bool LoadProducts { get; set; } = true;
|
||||
protected bool LoadConsumers { get; } = true;
|
||||
protected bool WebhooksEnabled { get; set; }
|
||||
|
||||
public BaseStartup(IConfiguration configuration, IHostEnvironment hostEnvironment)
|
||||
{
|
||||
@ -293,9 +294,9 @@ public abstract class BaseStartup
|
||||
|
||||
app.UseLoggerMiddleware();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
app.UseEndpoints(async endpoints =>
|
||||
{
|
||||
endpoints.MapCustom();
|
||||
await endpoints.MapCustom(WebhooksEnabled, app.ApplicationServices);
|
||||
|
||||
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
|
||||
{
|
||||
|
@ -76,13 +76,59 @@ public class CustomEndpointDataSource : EndpointDataSource
|
||||
|
||||
public static class EndpointExtension
|
||||
{
|
||||
public static IEndpointRouteBuilder MapCustom(this IEndpointRouteBuilder endpoints)
|
||||
private static readonly IReadOnlyList<string> _methodList = new List<string>
|
||||
{
|
||||
"POST",
|
||||
"PUT",
|
||||
"DELETE"
|
||||
};
|
||||
|
||||
public static async Task<IEndpointRouteBuilder> MapCustom(this IEndpointRouteBuilder endpoints, bool webhooksEnabled = false, IServiceProvider serviceProvider = null)
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
|
||||
if (webhooksEnabled && serviceProvider != null)
|
||||
{
|
||||
await endpoints.RegisterWebhooks(serviceProvider);
|
||||
}
|
||||
|
||||
var sources = endpoints.DataSources.First();
|
||||
endpoints.DataSources.Clear();
|
||||
endpoints.DataSources.Add(new CustomEndpointDataSource(sources));
|
||||
|
||||
return endpoints;
|
||||
}
|
||||
|
||||
private static async Task<IEndpointRouteBuilder> RegisterWebhooks(this IEndpointRouteBuilder endpoints, IServiceProvider serviceProvider)
|
||||
{
|
||||
var toRegister = endpoints.DataSources.First().Endpoints
|
||||
.Cast<RouteEndpoint>()
|
||||
.SelectMany(r =>
|
||||
{
|
||||
var result = new List<Webhook>();
|
||||
var httpMethodMetadata = r.Metadata.OfType<HttpMethodMetadata>().FirstOrDefault();
|
||||
var disabled = r.Metadata.OfType<WebhookDisableAttribute>().FirstOrDefault();
|
||||
|
||||
if (disabled == null)
|
||||
{
|
||||
foreach (var httpMethod in httpMethodMetadata.HttpMethods)
|
||||
{
|
||||
result.Add(new Webhook { Method = httpMethod, Route = r.RoutePattern.RawText.ToLower() });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
})
|
||||
.Where(r => _methodList.Contains(r.Method))
|
||||
.DistinctBy(r => $"{r.Method}|{r.Route}")
|
||||
.ToList();
|
||||
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
var dbWorker = scope.ServiceProvider.GetService<DbWorker>();
|
||||
if (dbWorker != null)
|
||||
{
|
||||
await dbWorker.Register(toRegister);
|
||||
}
|
||||
|
||||
return endpoints;
|
||||
}
|
||||
}
|
32
common/ASC.Api.Core/Core/WebhookDisableAttribute.cs
Normal file
32
common/ASC.Api.Core/Core/WebhookDisableAttribute.cs
Normal file
@ -0,0 +1,32 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
namespace ASC.Api.Core.Core;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
|
||||
public class WebhookDisableAttribute : Attribute
|
||||
{
|
||||
}
|
@ -33,31 +33,33 @@ public class WebhooksGlobalFilterAttribute : ResultFilterAttribute, IDisposable
|
||||
private Stream _bodyStream;
|
||||
private readonly IWebhookPublisher _webhookPublisher;
|
||||
private readonly ILogger<WebhooksGlobalFilterAttribute> _logger;
|
||||
private static readonly List<string> _methodList = new List<string> { "POST", "UPDATE", "DELETE" };
|
||||
private readonly SettingsManager _settingsManager;
|
||||
private readonly DbWorker _dbWorker;
|
||||
|
||||
public WebhooksGlobalFilterAttribute(IWebhookPublisher webhookPublisher, ILogger<WebhooksGlobalFilterAttribute> logger)
|
||||
public WebhooksGlobalFilterAttribute(
|
||||
IWebhookPublisher webhookPublisher,
|
||||
ILogger<WebhooksGlobalFilterAttribute> logger,
|
||||
SettingsManager settingsManager,
|
||||
DbWorker dbWorker)
|
||||
{
|
||||
_stream = new MemoryStream();
|
||||
_webhookPublisher = webhookPublisher;
|
||||
_logger = logger;
|
||||
_settingsManager = settingsManager;
|
||||
_dbWorker = dbWorker;
|
||||
}
|
||||
|
||||
public override void OnResultExecuting(ResultExecutingContext context)
|
||||
public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
||||
{
|
||||
if (!Skip(context.HttpContext))
|
||||
if (!await Skip(context.HttpContext))
|
||||
{
|
||||
_bodyStream = context.HttpContext.Response.Body;
|
||||
context.HttpContext.Response.Body = _stream;
|
||||
}
|
||||
|
||||
base.OnResultExecuting(context);
|
||||
}
|
||||
|
||||
public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
||||
{
|
||||
await base.OnResultExecutionAsync(context, next);
|
||||
|
||||
if (context.Cancel || Skip(context.HttpContext))
|
||||
if (context.Cancel || await Skip(context.HttpContext))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -74,7 +76,9 @@ public class WebhooksGlobalFilterAttribute : ResultFilterAttribute, IDisposable
|
||||
|
||||
var resultContent = Encoding.UTF8.GetString(_stream.ToArray());
|
||||
|
||||
await _webhookPublisher.PublishAsync(method, routePattern, resultContent);
|
||||
var webhook = await _dbWorker.GetWebhookAsync(method, routePattern);
|
||||
|
||||
await _webhookPublisher.PublishAsync(webhook.Id, resultContent);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -100,16 +104,17 @@ public class WebhooksGlobalFilterAttribute : ResultFilterAttribute, IDisposable
|
||||
return (method, routePattern);
|
||||
}
|
||||
|
||||
private bool Skip(HttpContext context)
|
||||
private async Task<bool> Skip(HttpContext context)
|
||||
{
|
||||
var (method, routePattern) = GetData(context);
|
||||
|
||||
if (!_methodList.Contains(method))
|
||||
if (routePattern == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (routePattern == null)
|
||||
var webhook = await _dbWorker.GetWebhookAsync(method, routePattern);
|
||||
if (webhook == null || _settingsManager.Load<WebHooksSettings>().Ids.Contains(webhook.Id))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -56,6 +56,7 @@ public class S3Storage : BaseStorage
|
||||
|
||||
private EncryptionMethod _encryptionMethod = EncryptionMethod.None;
|
||||
private string _encryptionKey;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public S3Storage(
|
||||
TempStream tempStream,
|
||||
@ -65,9 +66,11 @@ public class S3Storage : BaseStorage
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILoggerProvider factory,
|
||||
ILogger<S3Storage> options,
|
||||
IHttpClientFactory clientFactory)
|
||||
IHttpClientFactory clientFactory,
|
||||
IConfiguration configuration)
|
||||
: base(tempStream, tenantManager, pathUtils, emailValidationKeyProvider, httpContextAccessor, factory, options, clientFactory)
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public Uri GetUriInternal(string path)
|
||||
@ -1225,7 +1228,7 @@ public class S3Storage : BaseStorage
|
||||
|
||||
var uploadId = initResponse.UploadId;
|
||||
|
||||
var partSize = 5 * (long)Math.Pow(2, 20); // Part size is 5 MB.
|
||||
var partSize = GetChunkSize();
|
||||
|
||||
long bytePosition = 0;
|
||||
for (var i = 1; bytePosition < objectSize; i++)
|
||||
@ -1463,7 +1466,19 @@ public class S3Storage : BaseStorage
|
||||
|
||||
return el.ETag;
|
||||
}
|
||||
|
||||
|
||||
private long GetChunkSize()
|
||||
{
|
||||
var configSetting = _configuration["files:uploader:chunk-size"];
|
||||
if (!string.IsNullOrEmpty(configSetting))
|
||||
{
|
||||
configSetting = configSetting.Trim();
|
||||
return long.Parse(configSetting);
|
||||
}
|
||||
long defaultValue = 10 * 1024 * 1024;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private enum EncryptionMethod
|
||||
{
|
||||
None,
|
||||
|
@ -37,4 +37,19 @@
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Resources\WebHookResource.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>WebHookResource.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resources\WebHookResource.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>WebHookResource.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -24,6 +24,8 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using AutoMapper;
|
||||
|
||||
namespace ASC.Webhooks.Core;
|
||||
|
||||
[Scope]
|
||||
@ -32,6 +34,7 @@ public class DbWorker
|
||||
private readonly IDbContextFactory<WebhooksDbContext> _dbContextFactory;
|
||||
private readonly TenantManager _tenantManager;
|
||||
private readonly AuthContext _authContext;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
private int Tenant
|
||||
{
|
||||
@ -41,18 +44,32 @@ public class DbWorker
|
||||
}
|
||||
}
|
||||
|
||||
public DbWorker(IDbContextFactory<WebhooksDbContext> dbContextFactory, TenantManager tenantManager, AuthContext authContext)
|
||||
public DbWorker(
|
||||
IDbContextFactory<WebhooksDbContext> dbContextFactory,
|
||||
TenantManager tenantManager,
|
||||
AuthContext authContext,
|
||||
IMapper mapper)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_tenantManager = tenantManager;
|
||||
_authContext = authContext;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public async Task<WebhooksConfig> AddWebhookConfig(string name, string uri, string secretKey)
|
||||
public async Task<WebhooksConfig> AddWebhookConfig(string uri, string secretKey)
|
||||
{
|
||||
using var webhooksDbContext = _dbContextFactory.CreateDbContext();
|
||||
using var webhooksDbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
var toAdd = new WebhooksConfig { TenantId = Tenant, Uri = uri, SecretKey = secretKey, Name = name };
|
||||
var objForCreate = await webhooksDbContext.WebhooksConfigs
|
||||
.Where(it => it.TenantId == Tenant && it.Uri == uri)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (objForCreate != null)
|
||||
{
|
||||
return objForCreate;
|
||||
}
|
||||
|
||||
var toAdd = new WebhooksConfig { TenantId = Tenant, Uri = uri, SecretKey = secretKey };
|
||||
toAdd = await webhooksDbContext.AddOrUpdateAsync(r => r.WebhooksConfigs, toAdd);
|
||||
await webhooksDbContext.SaveChangesAsync();
|
||||
|
||||
@ -83,7 +100,7 @@ public class DbWorker
|
||||
.AsAsyncEnumerable();
|
||||
}
|
||||
|
||||
public async Task<WebhooksConfig> UpdateWebhookConfig(int id, string name, string uri, string key, bool? enabled)
|
||||
public async Task<WebhooksConfig> UpdateWebhookConfig(int id, string uri, string key, bool? enabled)
|
||||
{
|
||||
using var webhooksDbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
@ -98,11 +115,6 @@ public class DbWorker
|
||||
updateObj.Uri = uri;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
updateObj.Name = name;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(key))
|
||||
{
|
||||
updateObj.SecretKey = key;
|
||||
@ -128,15 +140,18 @@ public class DbWorker
|
||||
|
||||
var removeObj = await webhooksDbContext.WebhooksConfigs
|
||||
.Where(it => it.TenantId == tenant && it.Id == id)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
webhooksDbContext.WebhooksConfigs.Remove(removeObj);
|
||||
await webhooksDbContext.SaveChangesAsync();
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (removeObj != null)
|
||||
{
|
||||
webhooksDbContext.WebhooksConfigs.Remove(removeObj);
|
||||
await webhooksDbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return removeObj;
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<WebhooksLog> ReadJournal(int startIndex, int limit, DateTime? delivery, string hookname, string route)
|
||||
public IAsyncEnumerable<WebhooksLog> ReadJournal(int startIndex, int limit, DateTime? delivery, string hookUri, int hookId)
|
||||
{
|
||||
var webhooksDbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
@ -150,14 +165,14 @@ public class DbWorker
|
||||
q = q.Where(r => r.Delivery == date);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(hookname))
|
||||
if (!string.IsNullOrEmpty(hookUri))
|
||||
{
|
||||
q = q.Where(r => r.Config.Name == hookname);
|
||||
q = q.Where(r => r.Config.Uri == hookUri);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(route))
|
||||
if (hookId != 0)
|
||||
{
|
||||
q = q.Where(r => r.Route == route);
|
||||
q = q.Where(r => r.WebhookId == hookId);
|
||||
}
|
||||
|
||||
if (startIndex != 0)
|
||||
@ -211,5 +226,54 @@ public class DbWorker
|
||||
await webhooksDbContext.SaveChangesAsync();
|
||||
|
||||
return webhook;
|
||||
}
|
||||
|
||||
public async Task Register(List<Webhook> webhooks)
|
||||
{
|
||||
using var webhooksDbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
var dbWebhooks = await webhooksDbContext.Webhooks.ToListAsync();
|
||||
|
||||
foreach (var webhook in webhooks)
|
||||
{
|
||||
if (!dbWebhooks.Any(r => r.Route == webhook.Route && r.Method == webhook.Method))
|
||||
{
|
||||
try
|
||||
{
|
||||
await webhooksDbContext.Webhooks.AddAsync(_mapper.Map<DbWebhook>(webhook));
|
||||
await webhooksDbContext.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<Webhook>> GetWebhooksAsync()
|
||||
{
|
||||
using var webhooksDbContext = _dbContextFactory.CreateDbContext();
|
||||
var webHooks = await webhooksDbContext.Webhooks.AsNoTracking().ToListAsync();
|
||||
return _mapper.Map<List<DbWebhook>, List<Webhook>>(webHooks);
|
||||
}
|
||||
|
||||
public async Task<Webhook> GetWebhookAsync(int id)
|
||||
{
|
||||
using var webhooksDbContext = _dbContextFactory.CreateDbContext();
|
||||
var webHook = await webhooksDbContext.Webhooks.Where(r => r.Id == id).AsNoTracking().FirstOrDefaultAsync();
|
||||
return _mapper.Map<DbWebhook, Webhook>(webHook);
|
||||
}
|
||||
|
||||
public async Task<Webhook> GetWebhookAsync(string method, string routePattern)
|
||||
{
|
||||
using var webhooksDbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
var webHook = await webhooksDbContext.Webhooks
|
||||
.Where(r => r.Method == method && r.Route == routePattern)
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return _mapper.Map<DbWebhook, Webhook>(webHook);
|
||||
}
|
||||
}
|
@ -29,14 +29,16 @@ namespace ASC.Webhooks.Core.EF.Context;
|
||||
public class WebhooksDbContext : DbContext
|
||||
{
|
||||
public DbSet<WebhooksConfig> WebhooksConfigs { get; set; }
|
||||
public DbSet<WebhooksLog> WebhooksLogs { get; set; }
|
||||
public DbSet<WebhooksLog> WebhooksLogs { get; set; }
|
||||
public DbSet<DbWebhook> Webhooks { get; set; }
|
||||
|
||||
public WebhooksDbContext(DbContextOptions<WebhooksDbContext> options) : base(options) { }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
ModelBuilderWrapper
|
||||
.From(modelBuilder, Database)
|
||||
.From(modelBuilder, Database)
|
||||
.AddDbWebhooks()
|
||||
.AddWebhooksConfig()
|
||||
.AddWebhooksLog();
|
||||
}
|
||||
|
95
common/ASC.Webhooks.Core/EF/Model/DbWebhook.cs
Normal file
95
common/ASC.Webhooks.Core/EF/Model/DbWebhook.cs
Normal file
@ -0,0 +1,95 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
namespace ASC.Webhooks.Core.EF.Model;
|
||||
|
||||
public class DbWebhook : IMapFrom<Webhook>
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Route { get; set; }
|
||||
public string Method { get; set; }
|
||||
}
|
||||
|
||||
public static class DbWebhookExtension
|
||||
{
|
||||
public static ModelBuilderWrapper AddDbWebhooks(this ModelBuilderWrapper modelBuilder)
|
||||
{
|
||||
return modelBuilder
|
||||
.Add(MySqlAddDbWebhook, Provider.MySql)
|
||||
.Add(PgSqlAddDbWebhook, Provider.PostgreSql);
|
||||
}
|
||||
|
||||
private static void MySqlAddDbWebhook(this ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<DbWebhook>(entity =>
|
||||
{
|
||||
entity.HasKey(e => new { e.Id })
|
||||
.HasName("PRIMARY");
|
||||
|
||||
entity.ToTable("webhooks")
|
||||
.HasCharSet("utf8");
|
||||
|
||||
entity.Property(e => e.Id)
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id");
|
||||
|
||||
entity.Property(e => e.Route)
|
||||
.HasMaxLength(200)
|
||||
.HasColumnName("route")
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
entity.Property(e => e.Method)
|
||||
.HasMaxLength(10)
|
||||
.HasColumnName("method")
|
||||
.HasDefaultValueSql("''");
|
||||
});
|
||||
}
|
||||
|
||||
private static void PgSqlAddDbWebhook(this ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<DbWebhook>(entity =>
|
||||
{
|
||||
entity.HasKey(e => new { e.Id })
|
||||
.HasName("PRIMARY");
|
||||
|
||||
entity.ToTable("webhooks");
|
||||
|
||||
entity.Property(e => e.Id)
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id");
|
||||
|
||||
entity.Property(e => e.Route)
|
||||
.HasMaxLength(200)
|
||||
.HasColumnName("route")
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
entity.Property(e => e.Method)
|
||||
.HasMaxLength(10)
|
||||
.HasColumnName("method")
|
||||
.HasDefaultValueSql("''");
|
||||
});
|
||||
}
|
||||
}
|
@ -28,8 +28,7 @@ namespace ASC.Webhooks.Core.EF.Model;
|
||||
|
||||
public class WebhooksConfig : BaseEntity
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int Id { get; set; }
|
||||
public string SecretKey { get; set; }
|
||||
public int TenantId { get; set; }
|
||||
public string Uri { get; set; }
|
||||
@ -80,11 +79,6 @@ public static class WebhooksConfigExtension
|
||||
.HasMaxLength(50)
|
||||
.HasColumnName("secret_key")
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
entity.Property(e => e.Name)
|
||||
.HasMaxLength(50)
|
||||
.HasColumnName("name")
|
||||
.IsRequired();
|
||||
|
||||
entity.Property(e => e.Enabled)
|
||||
.HasColumnName("enabled")
|
||||
@ -122,11 +116,6 @@ public static class WebhooksConfigExtension
|
||||
.HasMaxLength(50)
|
||||
.HasColumnName("secret_key")
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
entity.Property(e => e.Name)
|
||||
.HasMaxLength(50)
|
||||
.HasColumnName("name")
|
||||
.IsRequired();
|
||||
|
||||
entity.Property(e => e.Enabled)
|
||||
.HasColumnName("enabled")
|
||||
|
@ -27,12 +27,11 @@
|
||||
namespace ASC.Webhooks.Core.EF.Model;
|
||||
|
||||
public class WebhooksLog
|
||||
{
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int ConfigId { get; set; }
|
||||
public DateTime CreationTime { get; set; }
|
||||
public int Id { get; set; }
|
||||
public string Method { get; set; }
|
||||
public string Route { get; set; }
|
||||
public DateTime CreationTime { get; set; }
|
||||
public int WebhookId { get; set; }
|
||||
public string RequestHeaders { get; set; }
|
||||
public string RequestPayload { get; set; }
|
||||
public string ResponseHeaders { get; set; }
|
||||
@ -109,17 +108,12 @@ public static class WebhooksPayloadExtension
|
||||
|
||||
entity.Property(e => e.ResponseHeaders)
|
||||
.HasColumnName("response_headers")
|
||||
.HasColumnType("json");
|
||||
|
||||
entity.Property(e => e.Method)
|
||||
.HasColumnType("varchar")
|
||||
.HasColumnName("method")
|
||||
.HasMaxLength(100);
|
||||
|
||||
entity.Property(e => e.Route)
|
||||
.HasColumnType("varchar")
|
||||
.HasColumnName("route")
|
||||
.HasMaxLength(100);
|
||||
.HasColumnType("json");
|
||||
|
||||
entity.Property(e => e.WebhookId)
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("webhook_id")
|
||||
.IsRequired();
|
||||
|
||||
entity.Property(e => e.CreationTime)
|
||||
.HasColumnType("datetime")
|
||||
@ -180,15 +174,10 @@ public static class WebhooksPayloadExtension
|
||||
.HasColumnName("response_headers")
|
||||
.HasColumnType("json");
|
||||
|
||||
entity.Property(e => e.Method)
|
||||
.HasColumnType("varchar")
|
||||
.HasColumnName("method")
|
||||
.HasMaxLength(100);
|
||||
|
||||
entity.Property(e => e.Route)
|
||||
.HasColumnType("varchar")
|
||||
.HasColumnName("route")
|
||||
.HasMaxLength(100);
|
||||
entity.Property(e => e.WebhookId)
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("webhook_id")
|
||||
.IsRequired();
|
||||
|
||||
entity.Property(e => e.CreationTime)
|
||||
.HasColumnType("datetime")
|
||||
|
@ -24,13 +24,18 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
global using System.Text.Json.Serialization;
|
||||
|
||||
global using ASC.Common;
|
||||
global using ASC.Common.Caching;
|
||||
global using ASC.Common.Mapping;
|
||||
global using ASC.Core;
|
||||
global using ASC.Core.Common.EF;
|
||||
global using ASC.Core.Common.EF.Model;
|
||||
global using ASC.Core.Common.Settings;
|
||||
global using ASC.Web.Webhooks;
|
||||
global using ASC.Webhooks.Core.EF.Context;
|
||||
global using ASC.Webhooks.Core.EF.Model;
|
||||
|
||||
global using Microsoft.EntityFrameworkCore;
|
||||
global using ASC.Webhooks.Core.Resources;
|
||||
|
||||
global using Microsoft.EntityFrameworkCore;
|
@ -29,6 +29,6 @@ namespace ASC.Webhooks.Core;
|
||||
[Scope]
|
||||
public interface IWebhookPublisher
|
||||
{
|
||||
public Task PublishAsync(string method, string route, string requestPayload);
|
||||
public Task<WebhooksLog> PublishAsync(string method, string route, string requestPayload, int configId);
|
||||
public Task PublishAsync(int webhookId, string requestPayload);
|
||||
public Task<WebhooksLog> PublishAsync(int webhookId, string requestPayload, int configId);
|
||||
}
|
81
common/ASC.Webhooks.Core/Resources/WebHookResource.Designer.cs
generated
Normal file
81
common/ASC.Webhooks.Core/Resources/WebHookResource.Designer.cs
generated
Normal file
@ -0,0 +1,81 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ASC.Webhooks.Core.Resources {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class WebHookResource {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal WebHookResource() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.Webhooks.Core.Resources.WebHookResource", typeof(WebHookResource).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Start Edit.
|
||||
/// </summary>
|
||||
internal static string POST_api_2_0_files_file__fileId__startedit {
|
||||
get {
|
||||
return ResourceManager.GetString("POST|api/2.0/files/file/{fileId}/startedit", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Start Edit Description.
|
||||
/// </summary>
|
||||
internal static string POST_api_2_0_files_file__fileId__startedit_Description {
|
||||
get {
|
||||
return ResourceManager.GetString("POST|api/2.0/files/file/{fileId}/startedit_Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
126
common/ASC.Webhooks.Core/Resources/WebHookResource.resx
Normal file
126
common/ASC.Webhooks.Core/Resources/WebHookResource.resx
Normal file
@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="POST|api/2.0/files/file/{fileId}/startedit" xml:space="preserve">
|
||||
<value>Start Edit</value>
|
||||
</data>
|
||||
<data name="POST|api/2.0/files/file/{fileId}/startedit_Description" xml:space="preserve">
|
||||
<value>Start Edit Description</value>
|
||||
</data>
|
||||
</root>
|
@ -1,51 +1,46 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
|
||||
namespace ASC.Webhooks.Core;
|
||||
|
||||
[Serializable]
|
||||
public class WebHooksSettings : ISettings<WebHooksSettings>
|
||||
{
|
||||
public bool EnableSSLVerification { get; set; }
|
||||
public List<int> Ids { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Guid ID => new Guid("6EFA0EAB-D033-4720-BDB3-DEB057EBC140");
|
||||
|
||||
global using System.Collections.Concurrent;
|
||||
global using System.Security.Cryptography;
|
||||
global using System.Text;
|
||||
global using System.Text.Json;
|
||||
|
||||
global using ASC.Api.Core;
|
||||
global using ASC.Api.Core.Extensions;
|
||||
global using ASC.Common;
|
||||
global using ASC.Common.Caching;
|
||||
global using ASC.Common.Log;
|
||||
global using ASC.Common.Utils;
|
||||
global using ASC.Web.Webhooks;
|
||||
global using ASC.Webhooks.Core;
|
||||
global using ASC.Webhooks.Service;
|
||||
global using ASC.Webhooks.Service.Log;
|
||||
global using ASC.Webhooks.Service.Services;
|
||||
|
||||
global using Microsoft.AspNetCore.Builder;
|
||||
global using Microsoft.Extensions.Hosting.WindowsServices;
|
||||
global using Microsoft.Extensions.Logging;
|
||||
|
||||
global using Polly;
|
||||
global using Polly.Extensions.Http;
|
||||
global using ASC.Webhooks;
|
||||
global using ASC.Webhooks.Extension;
|
||||
public WebHooksSettings GetDefault() => new WebHooksSettings()
|
||||
{
|
||||
EnableSSLVerification = true,
|
||||
Ids = new List<int> { }
|
||||
};
|
||||
}
|
||||
|
||||
|
39
common/ASC.Webhooks.Core/WebhookManager.cs
Normal file
39
common/ASC.Webhooks.Core/WebhookManager.cs
Normal file
@ -0,0 +1,39 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
namespace ASC.Webhooks.Core;
|
||||
|
||||
public class Webhook : IMapFrom<DbWebhook>
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Route { get; set; }
|
||||
public string Method { get; set; }
|
||||
public bool Disable { get; set; }
|
||||
public string Name { get => WebHookResource.ResourceManager.GetString(Endpoint) ?? ""; }
|
||||
public string Description { get => WebHookResource.ResourceManager.GetString($"{Endpoint}_Description") ?? ""; }
|
||||
|
||||
private string Endpoint { get => $"{Method}|{Route}"; }
|
||||
}
|
@ -40,7 +40,7 @@ public class WebhookPublisher : IWebhookPublisher
|
||||
_webhookNotify = webhookNotify;
|
||||
}
|
||||
|
||||
public async Task PublishAsync(string method, string route, string requestPayload)
|
||||
public async Task PublishAsync(int webhookId, string requestPayload)
|
||||
{
|
||||
if (string.IsNullOrEmpty(requestPayload))
|
||||
{
|
||||
@ -51,11 +51,11 @@ public class WebhookPublisher : IWebhookPublisher
|
||||
|
||||
await foreach (var config in webhookConfigs.Where(r => r.Enabled))
|
||||
{
|
||||
_ = await PublishAsync(method, route, requestPayload, config.Id);
|
||||
_ = await PublishAsync(webhookId, requestPayload, config.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<WebhooksLog> PublishAsync(string method, string route, string requestPayload, int configId)
|
||||
public async Task<WebhooksLog> PublishAsync(int webhookId, string requestPayload, int configId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(requestPayload))
|
||||
{
|
||||
@ -64,8 +64,7 @@ public class WebhookPublisher : IWebhookPublisher
|
||||
|
||||
var webhooksLog = new WebhooksLog
|
||||
{
|
||||
Method = method,
|
||||
Route = route,
|
||||
WebhookId = webhookId,
|
||||
CreationTime = DateTime.UtcNow,
|
||||
RequestPayload = requestPayload,
|
||||
ConfigId = configId
|
||||
|
@ -5,19 +5,19 @@
|
||||
{
|
||||
"Provider": "MySql",
|
||||
"ProviderFullName": "MySql.Data.MySqlClient",
|
||||
"ConnectionString": "Server=localhost;Database=onlyoffice;User ID=root;Password=root"
|
||||
"ConnectionString": "Server=localhost;Database=docspace;User ID=root;Password=root"
|
||||
},
|
||||
{
|
||||
"Provider": "PostgreSql",
|
||||
"ProviderFullName": "Npgsql",
|
||||
"ConnectionString": "Host=localhost;Port=5432;Database=onlyoffice;Username=postgres;Password=dev;"
|
||||
"ConnectionString": "Host=localhost;Port=5432;Database=docspace;Username=postgres;Password=dev;"
|
||||
}
|
||||
],
|
||||
"TeamlabsiteProviders": [
|
||||
{
|
||||
"Provider": "MySql",
|
||||
"ProviderFullName": "MySql.Data.MySqlClient",
|
||||
"ConnectionString": "Server=localhost;Database=teamlabsite;User ID=root;Password=root"
|
||||
"ConnectionString": "Server=localhost;Database=docspacesite;User ID=root;Password=root"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
{
|
||||
"Provider": "MySql",
|
||||
"ProviderFullName": "MySql.Data.MySqlClient",
|
||||
"ConnectionString": "Server=localhost;Database=onlyoffice;User ID=root;Password=root"
|
||||
"ConnectionString": "Server=localhost;Database=docspace;User ID=root;Password=root"
|
||||
}
|
||||
],
|
||||
"TeamlabsiteProviders": [
|
||||
|
@ -30,9 +30,13 @@ global using ASC.Common;
|
||||
global using ASC.Common.DependencyInjection;
|
||||
global using ASC.Core.Notify;
|
||||
global using ASC.Notify;
|
||||
global using ASC.Notify.Extension;
|
||||
global using ASC.Studio.Notify;
|
||||
global using ASC.Web.Studio.Core.Notify;
|
||||
|
||||
global using Autofac;
|
||||
|
||||
global using Microsoft.AspNetCore.Builder;
|
||||
global using Microsoft.Extensions.Hosting.WindowsServices;
|
||||
|
||||
global using NLog;
|
@ -24,11 +24,6 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using ASC.Notify.Extension;
|
||||
using ASC.Studio.Notify;
|
||||
|
||||
using NLog;
|
||||
|
||||
var options = new WebApplicationOptions
|
||||
{
|
||||
Args = args,
|
||||
@ -42,13 +37,14 @@ builder.Configuration.AddDefaultConfiguration(builder.Environment)
|
||||
.AddEnvironmentVariables()
|
||||
.AddCommandLine(args);
|
||||
|
||||
var logger = LogManager.Setup()
|
||||
.SetupExtensions(s =>
|
||||
{
|
||||
s.RegisterLayoutRenderer("application-context", (logevent) => AppName);
|
||||
})
|
||||
.LoadConfiguration(builder.Configuration, builder.Environment)
|
||||
.GetLogger(typeof(Startup).Namespace);
|
||||
var logger = LogManager
|
||||
.Setup()
|
||||
.SetupExtensions(s =>
|
||||
{
|
||||
s.RegisterLayoutRenderer("application-context", (logevent) => AppName);
|
||||
})
|
||||
.LoadConfiguration(builder.Configuration, builder.Environment)
|
||||
.GetLogger(typeof(Startup).Namespace);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -1,17 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Worker">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\ASC.Api.Core\ASC.Api.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -1,87 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using NLog;
|
||||
|
||||
var options = new WebApplicationOptions
|
||||
{
|
||||
Args = args,
|
||||
ContentRootPath = WindowsServiceHelpers.IsWindowsService() ? AppContext.BaseDirectory : default
|
||||
};
|
||||
|
||||
var builder = WebApplication.CreateBuilder(options);
|
||||
|
||||
builder.Configuration.AddDefaultConfiguration(builder.Environment)
|
||||
.AddWebhookConfiguration()
|
||||
.AddEnvironmentVariables()
|
||||
.AddCommandLine(args);
|
||||
|
||||
var logger = LogManager.Setup()
|
||||
.SetupExtensions(s =>
|
||||
{
|
||||
s.RegisterLayoutRenderer("application-context", (logevent) => AppName);
|
||||
})
|
||||
.LoadConfiguration(builder.Configuration, builder.Environment)
|
||||
.GetLogger(typeof(Startup).Namespace);
|
||||
|
||||
try
|
||||
{
|
||||
logger.Info("Configuring web host ({applicationContext})...", AppName);
|
||||
builder.Host.ConfigureDefault();
|
||||
builder.WebHost.ConfigureDefaultKestrel();
|
||||
|
||||
var startup = new Startup(builder.Configuration, builder.Environment);
|
||||
|
||||
startup.ConfigureServices(builder.Services);
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
startup.Configure(app);
|
||||
|
||||
logger.Info("Starting web host ({applicationContext})...", AppName);
|
||||
await app.RunWithTasksAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (logger != null)
|
||||
{
|
||||
logger.Error(ex, "Program terminated unexpectedly ({applicationContext})!", AppName);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
|
||||
LogManager.Shutdown();
|
||||
}
|
||||
|
||||
public partial class Program
|
||||
{
|
||||
public static string Namespace = typeof(Startup).Namespace;
|
||||
public static string AppName = Namespace.Substring(Namespace.LastIndexOf('.') + 1).Replace(".", "");
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Kestrel WebServer": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": false,
|
||||
"environmentVariables": {
|
||||
"$STORAGE_ROOT": "../../../Data",
|
||||
"log__name": "webhooks",
|
||||
"log__dir": "../../../Logs",
|
||||
"core__products__folder": "../../../products",
|
||||
"ASPNETCORE_URLS": "http://localhost:5031",
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"WSL 2 : Ubuntu 20.04": {
|
||||
"commandName": "WSL2",
|
||||
"launchBrowser": false,
|
||||
"environmentVariables": {
|
||||
"$STORAGE_ROOT": "../../../Data",
|
||||
"log__name": "webhooks",
|
||||
"log__dir": "../../../Logs",
|
||||
"core__products__folder": "../../../products",
|
||||
"ASPNETCORE_URLS": "http://localhost:5031",
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"distributionName": "Ubuntu-20.04"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"pathToConf": "..\\..\\..\\config"
|
||||
}
|
@ -39,7 +39,7 @@
|
||||
"ConnectionStrings": {
|
||||
"default": {
|
||||
"name": "default",
|
||||
"connectionString": "Server=onlyoffice-mysql-server;Port=3306;Database=onlyoffice;User ID=onlyoffice_user;Password=onlyoffice_pass;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;ConnectionReset=false",
|
||||
"connectionString": "Server=onlyoffice-mysql-server;Port=3306;Database=docspace;User ID=onlyoffice_user;Password=onlyoffice_pass;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;ConnectionReset=false",
|
||||
"providerName": "MySql.Data.MySqlClient"
|
||||
}
|
||||
},
|
||||
|
@ -39,7 +39,7 @@
|
||||
"ConnectionStrings": {
|
||||
"default": {
|
||||
"name": "default",
|
||||
"connectionString": "Server=localhost;Database=onlyoffice;User ID=dev;Password=dev;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;AllowPublicKeyRetrieval=True;ConnectionReset=false",
|
||||
"connectionString": "Server=localhost;Database=docspace;User ID=dev;Password=dev;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;AllowPublicKeyRetrieval=True;ConnectionReset=false",
|
||||
"providerName": "MySql.Data.MySqlClient"
|
||||
}
|
||||
},
|
||||
|
@ -79,8 +79,8 @@
|
||||
"reviewed-docs": [ ".docx", ".docxf" ],
|
||||
"viewed-docs": [ ".pptx", ".pptm", ".ppt", ".ppsx", ".ppsm", ".pps", ".potx", ".potm", ".pot", ".odp", ".fodp", ".otp", ".gslides", ".xlsx", ".xlsm", ".xls", ".xltx", ".xltm", ".xlt", ".ods", ".fods", ".ots", ".gsheet", ".csv", ".docx", ".docxf", ".oform", ".docm", ".doc", ".dotx", ".dotm", ".dot", ".odt", ".fodt", ".ott", ".gdoc", ".txt", ".rtf", ".mht", ".html", ".htm", ".epub", ".pdf", ".djvu", ".xps" ],
|
||||
"secret": {
|
||||
"value": "",
|
||||
"header": ""
|
||||
"value": "secret",
|
||||
"header": "AuthorizationJwt"
|
||||
},
|
||||
"url": {
|
||||
"public": "http://localhost:8085/",
|
||||
@ -93,7 +93,7 @@
|
||||
"exts": [ "avi", "mpeg", "mpg", "wmv" ]
|
||||
},
|
||||
"uploader": {
|
||||
"chunk-size": 10485760,
|
||||
"chunk-size": 20971520,
|
||||
"url": "/"
|
||||
},
|
||||
"viewed-images": [ ".svg", ".bmp", ".gif", ".jpeg", ".jpg", ".png", ".ico", ".tif", ".tiff", ".webp" ],
|
||||
@ -128,22 +128,22 @@
|
||||
"ConnectionStrings": {
|
||||
"default": {
|
||||
"name": "default",
|
||||
"connectionString": "Server=localhost;Database=onlyoffice;User ID=dev;Password=dev;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;AllowPublicKeyRetrieval=True;Connection Timeout=30;Maximum Pool Size=300;ConnectionReset=false",
|
||||
"connectionString": "Server=localhost;Database=docspace;User ID=dev;Password=dev;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;AllowPublicKeyRetrieval=True;Connection Timeout=30;Maximum Pool Size=300;ConnectionReset=false",
|
||||
"providerName": "MySql.Data.MySqlClient"
|
||||
},
|
||||
"postgre": {
|
||||
"name": "postgre",
|
||||
"connectionString": "Host=localhost;Port=5432;Database=onlyoffice;Username=postgres;Password=dev;",
|
||||
"connectionString": "Host=localhost;Port=5432;Database=docspace;Username=postgres;Password=dev;",
|
||||
"providerName": "Npgsql"
|
||||
},
|
||||
"mysql": {
|
||||
"name": "mysql",
|
||||
"connectionString": "Server=localhost;Database=onlyoffice;User ID=dev;Password=dev;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;AllowPublicKeyRetrieval=True;Connection Timeout=30;Maximum Pool Size=300;ConnectionReset=false",
|
||||
"connectionString": "Server=localhost;Database=docspace;User ID=dev;Password=dev;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;AllowPublicKeyRetrieval=True;Connection Timeout=30;Maximum Pool Size=300;ConnectionReset=false",
|
||||
"providerName": "MySql.Data.MySqlClient"
|
||||
},
|
||||
"teamlabsite": {
|
||||
"name": "teamlabsite",
|
||||
"connectionString": "Server=localhost;Database=teamlabsite;User ID=dev;Password=dev;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;AllowPublicKeyRetrieval=True;Connection Timeout=30;Maximum Pool Size=300;ConnectionReset=false",
|
||||
"name": "docspacesite",
|
||||
"connectionString": "Server=localhost;Database=docspacesite;User ID=dev;Password=dev;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;AllowPublicKeyRetrieval=True;Connection Timeout=30;Maximum Pool Size=300;ConnectionReset=false",
|
||||
"providerName": "MySql.Data.MySqlClient"
|
||||
}
|
||||
},
|
||||
|
@ -9,7 +9,7 @@
|
||||
"ConnectionStrings": {
|
||||
"default": {
|
||||
"name": "default",
|
||||
"connectionString": "Server=172.18.0.2;Port=3306;Database=onlyoffice;User ID=onlyoffice_user;Password=onlyoffice_pass;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;ConnectionReset=false",
|
||||
"connectionString": "Server=172.18.0.2;Port=3306;Database=docspace;User ID=onlyoffice_user;Password=onlyoffice_pass;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;ConnectionReset=false",
|
||||
"providerName": "MySql.Data.MySqlClient"
|
||||
}
|
||||
},
|
||||
|
@ -19,7 +19,34 @@ namespace ASC.Migrations.MySql.Migrations
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "6.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.DbWebhook", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Route")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("varchar(200)")
|
||||
.HasColumnName("route");
|
||||
|
||||
b.Property<string>("Method")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("varchar(10)")
|
||||
.HasColumnName("method");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
|
||||
b.ToTable("webhooks", (string)null);
|
||||
|
||||
b.HasAnnotation("MySql:CharSet", "utf8");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.WebhooksConfig", b =>
|
||||
{
|
||||
@ -88,11 +115,6 @@ namespace ASC.Migrations.MySql.Migrations
|
||||
.HasColumnType("datetime")
|
||||
.HasColumnName("delivery");
|
||||
|
||||
b.Property<string>("Method")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)")
|
||||
.HasColumnName("method");
|
||||
|
||||
b.Property<string>("RequestHeaders")
|
||||
.HasColumnType("json")
|
||||
.HasColumnName("request_headers");
|
||||
@ -114,10 +136,9 @@ namespace ASC.Migrations.MySql.Migrations
|
||||
.UseCollation("utf8_general_ci")
|
||||
.HasAnnotation("MySql:CharSet", "utf8");
|
||||
|
||||
b.Property<string>("Route")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)")
|
||||
.HasColumnName("route");
|
||||
b.Property<int>("ConfigId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("config_id");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int")
|
||||
|
@ -1,100 +1,141 @@
|
||||
using System;
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ASC.Migrations.MySql.Migrations
|
||||
namespace ASC.Migrations.MySql.Migrations;
|
||||
|
||||
public partial class WebhooksDbContextMigrate : Migration
|
||||
{
|
||||
public partial class WebhooksDbContextMigrate : Migration
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterDatabase()
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
migrationBuilder.AlterDatabase()
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webhooks",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
route = table.Column<string>(type: "varchar(200)", maxLength: 200, nullable: false, defaultValueSql: "''")
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
method = table.Column<string>(type: "varchar(10)", maxLength: 10, nullable: false, defaultValueSql: "''")
|
||||
.Annotation("MySql:CharSet", "utf8")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PRIMARY", x => x.id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webhooks_config",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
name = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
secret_key = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: true, defaultValueSql: "''")
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
tenant_id = table.Column<uint>(type: "int unsigned", nullable: false),
|
||||
uri = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: true, defaultValueSql: "''")
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
enabled = table.Column<bool>(type: "tinyint(1)", nullable: false, defaultValueSql: "'1'")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PRIMARY", x => x.id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8");
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webhooks_config",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
name = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
secret_key = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: true, defaultValueSql: "''")
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
tenant_id = table.Column<uint>(type: "int unsigned", nullable: false),
|
||||
uri = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: true, defaultValueSql: "''")
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
enabled = table.Column<bool>(type: "tinyint(1)", nullable: false, defaultValueSql: "'1'")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PRIMARY", x => x.id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webhooks_logs",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
config_id = table.Column<int>(type: "int", nullable: false),
|
||||
creation_time = table.Column<DateTime>(type: "datetime", nullable: false),
|
||||
method = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
route = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
request_headers = table.Column<string>(type: "json", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
request_payload = table.Column<string>(type: "text", nullable: false, collation: "utf8_general_ci")
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
response_headers = table.Column<string>(type: "json", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
response_payload = table.Column<string>(type: "text", nullable: true, collation: "utf8_general_ci")
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
status = table.Column<int>(type: "int", nullable: false),
|
||||
tenant_id = table.Column<uint>(type: "int unsigned", nullable: false),
|
||||
uid = table.Column<string>(type: "varchar(36)", nullable: false, collation: "utf8_general_ci")
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
delivery = table.Column<DateTime>(type: "datetime", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PRIMARY", x => x.id);
|
||||
table.ForeignKey(
|
||||
name: "FK_webhooks_logs_webhooks_config_config_id",
|
||||
column: x => x.config_id,
|
||||
principalTable: "webhooks_config",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8");
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webhooks_logs",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
config_id = table.Column<int>(type: "int", nullable: false),
|
||||
creation_time = table.Column<DateTime>(type: "datetime", nullable: false),
|
||||
webhook_id = table.Column<int>(type: "int", nullable: false),
|
||||
request_headers = table.Column<string>(type: "json", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
request_payload = table.Column<string>(type: "text", nullable: false, collation: "utf8_general_ci")
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
response_headers = table.Column<string>(type: "json", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
response_payload = table.Column<string>(type: "text", nullable: true, collation: "utf8_general_ci")
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
status = table.Column<int>(type: "int", nullable: false),
|
||||
tenant_id = table.Column<uint>(type: "int unsigned", nullable: false),
|
||||
uid = table.Column<string>(type: "varchar(36)", nullable: false, collation: "utf8_general_ci")
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
delivery = table.Column<DateTime>(type: "datetime", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PRIMARY", x => x.id);
|
||||
table.ForeignKey(
|
||||
name: "FK_webhooks_logs_webhooks_config_config_id",
|
||||
column: x => x.config_id,
|
||||
principalTable: "webhooks_config",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "tenant_id",
|
||||
table: "webhooks_config",
|
||||
column: "tenant_id");
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "tenant_id",
|
||||
table: "webhooks_config",
|
||||
column: "tenant_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_webhooks_logs_config_id",
|
||||
table: "webhooks_logs",
|
||||
column: "config_id");
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_webhooks_logs_config_id",
|
||||
table: "webhooks_logs",
|
||||
column: "config_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "tenant_id",
|
||||
table: "webhooks_logs",
|
||||
column: "tenant_id");
|
||||
}
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "tenant_id",
|
||||
table: "webhooks_logs",
|
||||
column: "tenant_id");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "webhooks_logs");
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "webhooks");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "webhooks_config");
|
||||
}
|
||||
migrationBuilder.DropTable(
|
||||
name: "webhooks_logs");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "webhooks_config");
|
||||
}
|
||||
}
|
||||
|
@ -16,9 +16,40 @@ namespace ASC.Migrations.MySql.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "6.0.7")
|
||||
.HasAnnotation("ProductVersion", "7.0.2")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.DbWebhook", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Method")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("varchar(10)")
|
||||
.HasColumnName("method")
|
||||
.HasDefaultValueSql("''")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("Route")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)")
|
||||
.HasColumnName("route")
|
||||
.HasDefaultValueSql("''")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
|
||||
b.ToTable("webhooks", (string)null);
|
||||
|
||||
b.HasAnnotation("MySql:CharSet", "utf8");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.WebhooksConfig", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@ -32,12 +63,6 @@ namespace ASC.Migrations.MySql.Migrations
|
||||
.HasColumnName("enabled")
|
||||
.HasDefaultValueSql("'1'");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<string>("SecretKey")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(50)
|
||||
@ -88,7 +113,7 @@ namespace ASC.Migrations.MySql.Migrations
|
||||
|
||||
b.Property<string>("Method")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)")
|
||||
.HasColumnType("varchar")
|
||||
.HasColumnName("method");
|
||||
|
||||
b.Property<string>("RequestHeaders")
|
||||
@ -112,10 +137,9 @@ namespace ASC.Migrations.MySql.Migrations
|
||||
.UseCollation("utf8_general_ci")
|
||||
.HasAnnotation("MySql:CharSet", "utf8");
|
||||
|
||||
b.Property<string>("Route")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)")
|
||||
.HasColumnName("route");
|
||||
b.Property<int>("ConfigId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("config_id");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int")
|
||||
|
@ -21,7 +21,34 @@ namespace ASC.Migrations.PostgreSql.Migrations
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
|
||||
.HasAnnotation("ProductVersion", "6.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.DbWebhook", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Route")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("route");
|
||||
|
||||
b.Property<string>("Method")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("method");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
|
||||
b.ToTable("webhooks", (string)null);
|
||||
|
||||
b.HasAnnotation("MySql:CharSet", "utf8");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.WebhooksConfig", b =>
|
||||
{
|
||||
@ -90,11 +117,6 @@ namespace ASC.Migrations.PostgreSql.Migrations
|
||||
.HasColumnType("datetime")
|
||||
.HasColumnName("delivery");
|
||||
|
||||
b.Property<string>("Method")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar")
|
||||
.HasColumnName("method");
|
||||
|
||||
b.Property<string>("RequestHeaders")
|
||||
.HasColumnType("json")
|
||||
.HasColumnName("request_headers");
|
||||
@ -112,10 +134,9 @@ namespace ASC.Migrations.PostgreSql.Migrations
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("response_payload");
|
||||
|
||||
b.Property<string>("Route")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar")
|
||||
.HasColumnName("route");
|
||||
b.Property<int>("WebhookId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("webhook_id");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int")
|
||||
|
@ -1,85 +1,126 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ASC.Migrations.PostgreSql.Migrations
|
||||
namespace ASC.Migrations.PostgreSql.Migrations;
|
||||
|
||||
public partial class WebhooksDbContextMigrate : Migration
|
||||
{
|
||||
public partial class WebhooksDbContextMigrate : Migration
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webhooks",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
route = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false, defaultValueSql: "''"),
|
||||
method = table.Column<string>(type: "character varying(10)", maxLength: 10, nullable: false, defaultValueSql: "''")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PRIMARY", x => x.id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webhooks_config",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
name = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||
secret_key = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true, defaultValueSql: "''"),
|
||||
tenant_id = table.Column<int>(type: "int unsigned", nullable: false),
|
||||
uri = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true, defaultValueSql: "''"),
|
||||
enabled = table.Column<bool>(type: "boolean", nullable: false, defaultValueSql: "true")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PRIMARY", x => x.id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webhooks_logs",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
config_id = table.Column<int>(type: "int", nullable: false),
|
||||
creation_time = table.Column<DateTime>(type: "datetime", nullable: false),
|
||||
webhook_id = table.Column<int>(type: "int", nullable: false),
|
||||
request_headers = table.Column<string>(type: "json", nullable: true),
|
||||
request_payload = table.Column<string>(type: "text", nullable: false),
|
||||
response_headers = table.Column<string>(type: "json", nullable: true),
|
||||
response_payload = table.Column<string>(type: "text", nullable: true),
|
||||
status = table.Column<int>(type: "int", nullable: false),
|
||||
tenant_id = table.Column<int>(type: "int unsigned", nullable: false),
|
||||
uid = table.Column<string>(type: "varchar", maxLength: 50, nullable: false),
|
||||
delivery = table.Column<DateTime>(type: "datetime", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PRIMARY", x => x.id);
|
||||
table.ForeignKey(
|
||||
name: "FK_webhooks_logs_webhooks_config_config_id",
|
||||
column: x => x.config_id,
|
||||
principalTable: "webhooks_config",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "tenant_id",
|
||||
table: "webhooks_config",
|
||||
column: "tenant_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_webhooks_logs_config_id",
|
||||
table: "webhooks_logs",
|
||||
column: "config_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "tenant_id",
|
||||
table: "webhooks_logs",
|
||||
column: "tenant_id");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webhooks_config",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
name = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||
secret_key = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true, defaultValueSql: "''"),
|
||||
tenant_id = table.Column<int>(type: "int unsigned", nullable: false),
|
||||
uri = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true, defaultValueSql: "''"),
|
||||
enabled = table.Column<bool>(type: "boolean", nullable: false, defaultValueSql: "true")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PRIMARY", x => x.id);
|
||||
});
|
||||
migrationBuilder.DropTable(
|
||||
name: "webhooks");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webhooks_logs",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
config_id = table.Column<int>(type: "int", nullable: false),
|
||||
creation_time = table.Column<DateTime>(type: "datetime", nullable: false),
|
||||
method = table.Column<string>(type: "varchar", maxLength: 100, nullable: true),
|
||||
route = table.Column<string>(type: "varchar", maxLength: 100, nullable: true),
|
||||
request_headers = table.Column<string>(type: "json", nullable: true),
|
||||
request_payload = table.Column<string>(type: "text", nullable: false),
|
||||
response_headers = table.Column<string>(type: "json", nullable: true),
|
||||
response_payload = table.Column<string>(type: "text", nullable: true),
|
||||
status = table.Column<int>(type: "int", nullable: false),
|
||||
tenant_id = table.Column<int>(type: "int unsigned", nullable: false),
|
||||
uid = table.Column<string>(type: "varchar", maxLength: 50, nullable: false),
|
||||
delivery = table.Column<DateTime>(type: "datetime", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PRIMARY", x => x.id);
|
||||
table.ForeignKey(
|
||||
name: "FK_webhooks_logs_webhooks_config_config_id",
|
||||
column: x => x.config_id,
|
||||
principalTable: "webhooks_config",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
migrationBuilder.DropTable(
|
||||
name: "webhooks_logs");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "tenant_id",
|
||||
table: "webhooks_config",
|
||||
column: "tenant_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_webhooks_logs_config_id",
|
||||
table: "webhooks_logs",
|
||||
column: "config_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "tenant_id",
|
||||
table: "webhooks_logs",
|
||||
column: "tenant_id");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "webhooks_logs");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "webhooks_config");
|
||||
}
|
||||
migrationBuilder.DropTable(
|
||||
name: "webhooks_config");
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,37 @@ namespace ASC.Migrations.PostgreSql.Migrations
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
|
||||
.HasAnnotation("ProductVersion", "6.0.7")
|
||||
.HasAnnotation("ProductVersion", "7.0.2")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.DbWebhook", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<string>("Method")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("method")
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
b.Property<string>("Route")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("route")
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
|
||||
b.ToTable("webhooks", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.WebhooksConfig", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@ -35,12 +63,6 @@ namespace ASC.Migrations.PostgreSql.Migrations
|
||||
.HasColumnName("enabled")
|
||||
.HasDefaultValueSql("true");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<string>("SecretKey")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(50)
|
||||
@ -80,6 +102,10 @@ namespace ASC.Migrations.PostgreSql.Migrations
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("config_id");
|
||||
|
||||
b.Property<int>("WebhookId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("webhook_id");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime")
|
||||
.HasColumnName("creation_time");
|
||||
@ -88,11 +114,6 @@ namespace ASC.Migrations.PostgreSql.Migrations
|
||||
.HasColumnType("datetime")
|
||||
.HasColumnName("delivery");
|
||||
|
||||
b.Property<string>("Method")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar")
|
||||
.HasColumnName("method");
|
||||
|
||||
b.Property<string>("RequestHeaders")
|
||||
.HasColumnType("json")
|
||||
.HasColumnName("request_headers");
|
||||
@ -110,11 +131,6 @@ namespace ASC.Migrations.PostgreSql.Migrations
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("response_payload");
|
||||
|
||||
b.Property<string>("Route")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar")
|
||||
.HasColumnName("route");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("status");
|
||||
|
@ -1,10 +1,14 @@
|
||||
{
|
||||
"EmailErrorMessage": "Email address not valid. You can edit the email by clicking on it.",
|
||||
"IndividualInvitation": "Individual invitation",
|
||||
"InviteAccountSearchPlaceholder": "Invite people by email",
|
||||
"InviteRoomSearchPlaceholder": "Invite people by name or email",
|
||||
"InviteUsersToRoom": "Invite users to room",
|
||||
"Invited": "Invited",
|
||||
"LinkCopySuccess": "Link has been copied",
|
||||
"SendInvitation": "Send invitation"
|
||||
"SendInvitation": "Send invitation",
|
||||
"InviteViaLink": "Invite via link",
|
||||
"InviteViaLinkDescriptionRoom": "Create a universal link for self-authorization in the room",
|
||||
"InviteViaLinkDescriptionAccounts": "Create a universal link for self-authorization in DocSpace",
|
||||
"AddManually": "Add manually",
|
||||
"AddManuallyDescriptionRoom": "Add existing DocSpace users to the room using the names or invite new users personally via email",
|
||||
"AddManuallyDescriptionAccounts": "Invite new users to DocSpace personally via email"
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
{
|
||||
"EmailErrorMessage": "Адрес электронной почты недействителен. Вы можете отредактировать адрес, нажав на него.",
|
||||
"IndividualInvitation": "Индивидуальное приглашение",
|
||||
"InviteAccountSearchPlaceholder": "Пригласить людей по электронной почте",
|
||||
"InviteRoomSearchPlaceholder": "Приглашайте людей по имени или электронной почте",
|
||||
"InviteUsersToRoom": "Пригласить пользователей в комнату",
|
||||
"Invited": "Приглашен",
|
||||
"LinkCopySuccess": "Ссылка скопирована",
|
||||
"SendInvitation": "Выслать приглашение"
|
||||
"SendInvitation": "Выслать приглашение",
|
||||
"InviteViaLink": "Пригласить по ссылке",
|
||||
"InviteViaLinkDescriptionRoom": "Создать универсальную ссылку для самостоятельной авторизации в комнате",
|
||||
"InviteViaLinkDescriptionAccounts": "Создать универсальную ссылку для самостоятельной авторизации в DocSpace",
|
||||
"AddManually": "Добавить вручную",
|
||||
"AddManuallyDescriptionRoom": "Добавьте существующих пользователей DocSpace в комнату используя имена или лично пригласите новых пользователей по электронной почте",
|
||||
"AddManuallyDescriptionAccounts": "Приглашайте новых пользователей в DocSpace лично по электронной почте"
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ const StyledSubHeader = styled(Heading)`
|
||||
font-size: 16px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
margin: 16px 0;
|
||||
margin: 16px 0 8px 0;
|
||||
|
||||
${(props) =>
|
||||
props.inline &&
|
||||
@ -60,6 +60,20 @@ const StyledSubHeader = styled(Heading)`
|
||||
`};
|
||||
`;
|
||||
|
||||
const StyledDescription = styled(Text)`
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.commonParam.descriptionColor};
|
||||
margin-bottom: 16px;
|
||||
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
`;
|
||||
|
||||
StyledDescription.defaultProps = { theme: Base };
|
||||
|
||||
const StyledRow = styled.div`
|
||||
width: calc(100% - 32px) !important;
|
||||
|
||||
@ -252,4 +266,5 @@ export {
|
||||
StyledAccessSelector,
|
||||
StyledToggleButton,
|
||||
StyledText,
|
||||
StyledDescription,
|
||||
};
|
||||
|
@ -199,9 +199,7 @@ const InvitePanel = ({
|
||||
zIndex={310}
|
||||
>
|
||||
<StyledBlock>
|
||||
<StyledHeading>
|
||||
{roomId === -1 ? t("Common:InviteUsers") : t("InviteUsersToRoom")}
|
||||
</StyledHeading>
|
||||
<StyledHeading>{t("Common:InviteUsers")}</StyledHeading>
|
||||
</StyledBlock>
|
||||
|
||||
<ExternalLinks
|
||||
|
@ -20,6 +20,7 @@ import {
|
||||
StyledInviteInput,
|
||||
StyledInviteInputContainer,
|
||||
StyledToggleButton,
|
||||
StyledDescription,
|
||||
} from "../StyledInvitePanel";
|
||||
|
||||
const ExternalLinks = ({
|
||||
@ -156,7 +157,7 @@ const ExternalLinks = ({
|
||||
return (
|
||||
<StyledBlock noPadding ref={inputsRef}>
|
||||
<StyledSubHeader inline>
|
||||
{t("SharingPanel:ExternalLink")}
|
||||
{t("InviteViaLink")}
|
||||
{false && ( //TODO: Change to linksVisible after added link information from backend
|
||||
<div style={{ position: "relative" }}>
|
||||
<IconButton
|
||||
@ -186,6 +187,11 @@ const ExternalLinks = ({
|
||||
)}
|
||||
<StyledToggleButton isChecked={linksVisible} onChange={toggleLinks} />
|
||||
</StyledSubHeader>
|
||||
<StyledDescription>
|
||||
{roomId === -1
|
||||
? t("InviteViaLinkDescriptionAccounts")
|
||||
: t("InviteViaLinkDescriptionRoom")}
|
||||
</StyledDescription>
|
||||
{linksVisible && (
|
||||
<StyledInviteInputContainer key={activeLink.id}>
|
||||
<StyledInviteInput>
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
StyledInviteInputContainer,
|
||||
StyledDropDown,
|
||||
SearchItemText,
|
||||
StyledDescription,
|
||||
} from "../StyledInvitePanel";
|
||||
|
||||
const InviteInput = ({
|
||||
@ -223,7 +224,7 @@ const InviteInput = ({
|
||||
return (
|
||||
<>
|
||||
<StyledSubHeader>
|
||||
{t("IndividualInvitation")}
|
||||
{t("AddManually")}
|
||||
{!hideSelector && (
|
||||
<StyledLink
|
||||
fontWeight="600"
|
||||
@ -235,6 +236,11 @@ const InviteInput = ({
|
||||
</StyledLink>
|
||||
)}
|
||||
</StyledSubHeader>
|
||||
<StyledDescription>
|
||||
{roomId === -1
|
||||
? t("AddManuallyDescriptionAccounts")
|
||||
: t("AddManuallyDescriptionRoom")}
|
||||
</StyledDescription>
|
||||
|
||||
<StyledInviteInputContainer ref={inputsRef}>
|
||||
<StyledInviteInput ref={searchRef}>
|
||||
|
@ -18,8 +18,13 @@ const StyledNoItemContainer = styled.div`
|
||||
}
|
||||
|
||||
.no-thumbnail-img-wrapper {
|
||||
width: 96px;
|
||||
height: 100px;
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
|
||||
img {
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
}
|
||||
}
|
||||
|
||||
.no-accounts {
|
||||
|
@ -7,6 +7,11 @@ const StyledSeveralItemsContainer = styled.div`
|
||||
justify-content: center;
|
||||
gap: 32px;
|
||||
padding-top: ${(props) => (props.isAccounts ? "80px" : "0")};
|
||||
|
||||
img {
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
}
|
||||
`;
|
||||
|
||||
export { StyledSeveralItemsContainer };
|
||||
|
@ -31,7 +31,7 @@ const SeveralItems = ({ isAccounts, theme, selectedItems }) => {
|
||||
isAccounts={isAccounts}
|
||||
className="no-thumbnail-img-wrapper"
|
||||
>
|
||||
<img size="96px" src={imgSrc} />
|
||||
<img src={imgSrc} />
|
||||
<Text fontSize="16px" fontWeight={700}>
|
||||
{`${itemsText}: ${selectedItems.length}`}
|
||||
</Text>
|
||||
|
@ -14,7 +14,14 @@ class FilesTableHeader extends React.Component {
|
||||
}
|
||||
|
||||
getTableColumns = (fromUpdate = false) => {
|
||||
const { t, isRooms, isTrashFolder, getColumns } = this.props;
|
||||
const {
|
||||
t,
|
||||
isRooms,
|
||||
isTrashFolder,
|
||||
getColumns,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
} = this.props;
|
||||
|
||||
const defaultColumns = [];
|
||||
|
||||
@ -224,11 +231,15 @@ class FilesTableHeader extends React.Component {
|
||||
this.setState({
|
||||
columns,
|
||||
resetColumnsSize,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
});
|
||||
} else {
|
||||
this.state = {
|
||||
columns,
|
||||
resetColumnsSize,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -267,9 +278,18 @@ class FilesTableHeader extends React.Component {
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
isRooms,
|
||||
isTrashFolder,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
} = this.props;
|
||||
|
||||
if (
|
||||
this.props.isRooms !== prevProps.isRooms ||
|
||||
this.props.isTrashFolder !== prevProps.isTrashFolder
|
||||
isRooms !== prevProps.isRooms ||
|
||||
isTrashFolder !== prevProps.isTrashFolder ||
|
||||
columnStorageName !== prevProps.columnStorageName ||
|
||||
columnInfoPanelStorageName !== prevProps.columnInfoPanelStorageName
|
||||
) {
|
||||
return this.getTableColumns(true);
|
||||
}
|
||||
@ -361,36 +381,22 @@ class FilesTableHeader extends React.Component {
|
||||
firstElemChecked,
|
||||
sortingVisible,
|
||||
infoPanelVisible,
|
||||
columnStorageName,
|
||||
filesColumnStorageName,
|
||||
roomsColumnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
filesColumnInfoPanelStorageName,
|
||||
roomsColumnInfoPanelStorageName,
|
||||
|
||||
withPaging,
|
||||
tagRef,
|
||||
setHideColumns,
|
||||
} = this.props;
|
||||
|
||||
const { columns, resetColumnsSize } = this.state;
|
||||
const {
|
||||
columns,
|
||||
resetColumnsSize,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
} = this.state;
|
||||
|
||||
const sortBy = isRooms ? roomsFilter.sortBy : filter.sortBy;
|
||||
const sortOrder = isRooms ? roomsFilter.sortOrder : filter.sortOrder;
|
||||
|
||||
// TODO: make some better
|
||||
let currentColumnStorageName = columnStorageName;
|
||||
let currentColumnInfoPanelStorageName = columnInfoPanelStorageName;
|
||||
|
||||
if (columns.length === 5 && columnStorageName === filesColumnStorageName) {
|
||||
currentColumnStorageName = roomsColumnStorageName;
|
||||
currentColumnInfoPanelStorageName = roomsColumnInfoPanelStorageName;
|
||||
}
|
||||
|
||||
if (columns.length === 7 && columnStorageName === roomsColumnStorageName) {
|
||||
currentColumnStorageName = filesColumnStorageName;
|
||||
currentColumnInfoPanelStorageName = filesColumnInfoPanelStorageName;
|
||||
}
|
||||
|
||||
return (
|
||||
<TableHeader
|
||||
isLengthenHeader={firstElemChecked || isHeaderChecked}
|
||||
@ -399,8 +405,8 @@ class FilesTableHeader extends React.Component {
|
||||
sortBy={sortBy}
|
||||
containerRef={containerRef}
|
||||
columns={columns}
|
||||
columnStorageName={currentColumnStorageName}
|
||||
columnInfoPanelStorageName={currentColumnInfoPanelStorageName}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
sectionWidth={sectionWidth}
|
||||
resetColumnsSize={resetColumnsSize}
|
||||
sortingVisible={sortingVisible}
|
||||
@ -443,10 +449,6 @@ export default inject(
|
||||
tableStorageName,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
filesColumnStorageName,
|
||||
roomsColumnStorageName,
|
||||
filesColumnInfoPanelStorageName,
|
||||
roomsColumnInfoPanelStorageName,
|
||||
|
||||
nameColumnIsEnabled,
|
||||
authorColumnIsEnabled,
|
||||
@ -494,10 +496,6 @@ export default inject(
|
||||
tableStorageName,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
filesColumnStorageName,
|
||||
roomsColumnStorageName,
|
||||
filesColumnInfoPanelStorageName,
|
||||
roomsColumnInfoPanelStorageName,
|
||||
|
||||
nameColumnIsEnabled,
|
||||
authorColumnIsEnabled,
|
||||
|
@ -26,10 +26,12 @@ const RoomCell = ({ sideColor, item }) => {
|
||||
setIsTooltipLoading(false);
|
||||
};
|
||||
|
||||
const canVisibleTitle = originRoomTitle || originTitle;
|
||||
|
||||
return [
|
||||
<StyledText
|
||||
key="cell"
|
||||
fontSize="12px"
|
||||
fontSize={canVisibleTitle ? "12px" : "13px"}
|
||||
fontWeight={600}
|
||||
color={sideColor}
|
||||
className="row_update-text"
|
||||
@ -38,7 +40,7 @@ const RoomCell = ({ sideColor, item }) => {
|
||||
data-tip={""}
|
||||
data-place={"bottom"}
|
||||
>
|
||||
{originRoomTitle || originTitle}
|
||||
{originRoomTitle || originTitle || "—"}
|
||||
</StyledText>,
|
||||
|
||||
<Tooltip
|
||||
|
@ -924,7 +924,7 @@ const SectionFilterContent = ({
|
||||
filterOptions.push(...thirdPartyOptions);
|
||||
}
|
||||
} else {
|
||||
if (!isRecentFolder && !isFavoritesFolder) {
|
||||
if (!isRecentFolder && !isFavoritesFolder && !isTrash) {
|
||||
const foldersOptions = [
|
||||
{
|
||||
key: FilterGroups.filterFolders,
|
||||
|
@ -70,7 +70,9 @@ const MainProfile = (props) => {
|
||||
<Text as="div" color="#A3A9AE" className="label">
|
||||
{t("Common:Name")}
|
||||
</Text>
|
||||
<Text fontWeight={600}>{profile.displayName}</Text>
|
||||
<Text fontWeight={600} truncate>
|
||||
{profile.displayName}
|
||||
</Text>
|
||||
</div>
|
||||
<IconButton
|
||||
className="edit-button"
|
||||
|
@ -54,6 +54,8 @@ export const StyledInfo = styled.div`
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
max-width: 100%;
|
||||
|
||||
@media ${hugeMobile} {
|
||||
gap: 8px;
|
||||
}
|
||||
@ -64,6 +66,8 @@ export const StyledInfo = styled.div`
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
max-width: 100%;
|
||||
|
||||
@media ${desktop} {
|
||||
height: 20px;
|
||||
}
|
||||
@ -72,6 +76,8 @@ export const StyledInfo = styled.div`
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
|
||||
max-width: calc(100% - 28px);
|
||||
|
||||
& > p {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
@ -514,7 +514,7 @@ class ContextOptionsStore {
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
return options.filter((o) => !!o);
|
||||
};
|
||||
|
||||
onShowInfoPanel = (item) => {
|
||||
@ -589,8 +589,8 @@ class ContextOptionsStore {
|
||||
|
||||
const hasInfoPanel = contextOptions.includes("show-info");
|
||||
|
||||
const emailSendIsDisabled = true;
|
||||
const showSeparator0 = hasInfoPanel || !isMedia || !emailSendIsDisabled;
|
||||
//const emailSendIsDisabled = true;
|
||||
const showSeparator0 = hasInfoPanel || !isMedia; // || !emailSendIsDisabled;
|
||||
|
||||
const separator0 = showSeparator0
|
||||
? {
|
||||
@ -873,13 +873,13 @@ class ContextOptionsStore {
|
||||
onClick: () => this.onCopyLink(item, t),
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "option_send-by-email",
|
||||
key: "send-by-email",
|
||||
label: t("SendByEmail"),
|
||||
icon: MailReactSvgUrl,
|
||||
disabled: emailSendIsDisabled,
|
||||
},
|
||||
// {
|
||||
// id: "option_send-by-email",
|
||||
// key: "send-by-email",
|
||||
// label: t("SendByEmail"),
|
||||
// icon: MailReactSvgUrl,
|
||||
// disabled: emailSendIsDisabled,
|
||||
// },
|
||||
...versionActions,
|
||||
{
|
||||
id: "option_show-info",
|
||||
@ -1048,6 +1048,15 @@ class ContextOptionsStore {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (options[0]?.isSeparator) {
|
||||
options.shift();
|
||||
}
|
||||
|
||||
if (options[options.length - 1]?.isSeparator) {
|
||||
options.pop();
|
||||
}
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
|
@ -177,12 +177,11 @@ class FilesStore {
|
||||
|
||||
const fileInfo = await api.files.getFileInfo(file.id);
|
||||
|
||||
if (this.files.findIndex((x) => x.id === opt?.id) > -1) return;
|
||||
console.log("[WS] create new file", fileInfo.id, fileInfo.title);
|
||||
|
||||
const newFiles = [fileInfo, ...this.files];
|
||||
|
||||
if (this.files.findIndex((x) => x.id === opt?.id) > -1) return;
|
||||
|
||||
if (newFiles.length > this.filter.pageCount && withPaging) {
|
||||
newFiles.pop(); // Remove last
|
||||
}
|
||||
|
@ -21,6 +21,9 @@ import {
|
||||
isMobile as isMobileUtils,
|
||||
isTablet as isTabletUtils,
|
||||
} from "@docspace/components/utils/device";
|
||||
|
||||
const UPLOAD_LIMIT_AT_ONCE = 5;
|
||||
|
||||
class UploadDataStore {
|
||||
authStore;
|
||||
treeFoldersStore;
|
||||
@ -45,6 +48,7 @@ class UploadDataStore {
|
||||
converted = true;
|
||||
uploadPanelVisible = false;
|
||||
selectedUploadFile = [];
|
||||
tempFiles = [];
|
||||
errors = 0;
|
||||
|
||||
isUploading = false;
|
||||
@ -52,6 +56,11 @@ class UploadDataStore {
|
||||
|
||||
isConvertSingleFile = false;
|
||||
|
||||
currentUploadNumber = 0;
|
||||
uploadedFilesSize = 0;
|
||||
|
||||
isParallel = true;
|
||||
|
||||
constructor(
|
||||
authStore,
|
||||
treeFoldersStore,
|
||||
@ -125,6 +134,7 @@ class UploadDataStore {
|
||||
this.uploaded = true;
|
||||
this.converted = true;
|
||||
this.errors = 0;
|
||||
this.uploadedFilesSize = 0;
|
||||
|
||||
this.isUploadingAndConversion = false;
|
||||
this.isUploading = false;
|
||||
@ -297,6 +307,19 @@ class UploadDataStore {
|
||||
const newPercent =
|
||||
((uploadedSize + totalUploadedSize) / newTotalSize) * 100;
|
||||
|
||||
return newPercent;
|
||||
};
|
||||
|
||||
getFilesPercent = (uploadedSize) => {
|
||||
const newSize = this.uploadedFilesSize + uploadedSize;
|
||||
this.uploadedFilesSize = newSize;
|
||||
|
||||
const newTotalSize = sumBy(this.files, (f) =>
|
||||
f.file && !this.uploaded ? f.file.size : 0
|
||||
);
|
||||
|
||||
const newPercent = (newSize / newTotalSize) * 100;
|
||||
|
||||
/*console.log(
|
||||
`newPercent=${newPercent} (newTotalSize=${newTotalSize} totalUploadedSize=${totalUploadedSize} indexOfFile=${indexOfFile})`
|
||||
);*/
|
||||
@ -468,6 +491,7 @@ class UploadDataStore {
|
||||
file.error = error;
|
||||
file.convertProgress = progress;
|
||||
file.inConversion = false;
|
||||
file.fileInfo = fileInfo;
|
||||
|
||||
if (error.indexOf("password") !== -1) {
|
||||
file.needPassword = true;
|
||||
@ -577,13 +601,36 @@ class UploadDataStore {
|
||||
if (this.uploaded && this.converted) {
|
||||
this.files = [];
|
||||
this.filesToConversion = [];
|
||||
this.uploadedFilesSize = 0;
|
||||
}
|
||||
|
||||
let newFiles = this.files;
|
||||
let filesSize = 0;
|
||||
let convertSize = 0;
|
||||
|
||||
for (let index of Object.keys(uploadFiles)) {
|
||||
const uploadFilesArray = Object.keys(uploadFiles);
|
||||
const hasFolder =
|
||||
uploadFilesArray.findIndex((_, ind) => {
|
||||
const file = uploadFiles[ind];
|
||||
|
||||
const filePath = file.path
|
||||
? file.path
|
||||
: file.webkitRelativePath
|
||||
? file.webkitRelativePath
|
||||
: file.name;
|
||||
|
||||
return file.name !== filePath;
|
||||
}) > -1;
|
||||
|
||||
if (hasFolder) {
|
||||
if (this.uploaded) this.isParallel = false;
|
||||
else {
|
||||
this.tempFiles.push({ uploadFiles, folderId, t });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (let index of uploadFilesArray) {
|
||||
const file = uploadFiles[index];
|
||||
|
||||
const parts = file.name.split(".");
|
||||
@ -644,7 +691,9 @@ class UploadDataStore {
|
||||
converted: !!this.tempConversionFiles.length,
|
||||
};
|
||||
|
||||
if (this.uploaded && countUploadingFiles) {
|
||||
const isParallel = this.isParallel ? true : this.uploaded;
|
||||
|
||||
if (isParallel && countUploadingFiles) {
|
||||
this.setUploadData(newUploadData);
|
||||
this.startUploadFiles(t);
|
||||
}
|
||||
@ -779,15 +828,27 @@ class UploadDataStore {
|
||||
return Promise.reject(res.data.message);
|
||||
}
|
||||
|
||||
const fileId = res.data.data.id;
|
||||
const { uploaded, id: fileId } = res.data.data;
|
||||
|
||||
const { uploaded } = res.data.data;
|
||||
let uploadedSize, newPercent;
|
||||
|
||||
const uploadedSize = uploaded
|
||||
? fileSize
|
||||
: index * this.settingsStore.chunkUploadSize;
|
||||
if (!this.isParallel) {
|
||||
uploadedSize = uploaded
|
||||
? fileSize
|
||||
: index * this.settingsStore.chunkUploadSize;
|
||||
|
||||
const newPercent = this.getNewPercent(uploadedSize, indexOfFile);
|
||||
newPercent = this.getNewPercent(uploadedSize, indexOfFile);
|
||||
} else {
|
||||
if (!uploaded) {
|
||||
uploadedSize =
|
||||
fileSize <= this.settingsStore.chunkUploadSize
|
||||
? fileSize
|
||||
: this.settingsStore.chunkUploadSize;
|
||||
} else {
|
||||
uploadedSize = fileSize - index * this.settingsStore.chunkUploadSize;
|
||||
}
|
||||
newPercent = this.getFilesPercent(uploadedSize);
|
||||
}
|
||||
|
||||
const percentCurrentFile = (index / length) * 100;
|
||||
|
||||
@ -808,13 +869,21 @@ class UploadDataStore {
|
||||
this.files[indexOfFile].fileId = fileId;
|
||||
this.files[indexOfFile].fileInfo = fileInfo;
|
||||
this.percent = newPercent;
|
||||
|
||||
if (this.isParallel) {
|
||||
this.currentUploadNumber -= 1;
|
||||
|
||||
const nextFileIndex = this.files.findIndex((f) => !f.inAction);
|
||||
|
||||
if (nextFileIndex !== -1) {
|
||||
this.startSessionFunc(nextFileIndex, t);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (fileInfo.version > 2) {
|
||||
this.filesStore.setUploadedFileIdWithVersion(fileInfo.id);
|
||||
}
|
||||
|
||||
//setUploadData(uploadData);
|
||||
}
|
||||
}
|
||||
|
||||
@ -861,43 +930,71 @@ class UploadDataStore {
|
||||
|
||||
this.primaryProgressDataStore.setPrimaryProgressBarData(progressData);
|
||||
|
||||
let index = 0;
|
||||
let len = files.length;
|
||||
while (index < len) {
|
||||
await this.startSessionFunc(index, t);
|
||||
index++;
|
||||
if (this.isParallel) {
|
||||
// console.log("IS PARALLEL");
|
||||
const notUploadedFiles = this.files.filter((f) => !f.inAction);
|
||||
|
||||
files = this.files;
|
||||
len = files.length;
|
||||
}
|
||||
const countFiles =
|
||||
notUploadedFiles.length >= UPLOAD_LIMIT_AT_ONCE
|
||||
? UPLOAD_LIMIT_AT_ONCE
|
||||
: notUploadedFiles.length;
|
||||
|
||||
if (!this.filesToConversion.length) {
|
||||
this.finishUploadFiles();
|
||||
} else {
|
||||
runInAction(() => (this.uploaded = true));
|
||||
const uploadedFiles = this.files.filter((x) => x.action === "uploaded");
|
||||
const totalErrorsCount = sumBy(uploadedFiles, (f) => (f.error ? 1 : 0));
|
||||
if (totalErrorsCount > 0)
|
||||
console.log("Upload errors: ", totalErrorsCount);
|
||||
|
||||
setTimeout(() => {
|
||||
if (!this.uploadPanelVisible && !totalErrorsCount && this.converted) {
|
||||
this.clearUploadedFiles();
|
||||
for (let i = 0; i < countFiles; i++) {
|
||||
if (this.currentUploadNumber <= UPLOAD_LIMIT_AT_ONCE) {
|
||||
const fileIndex = this.files.findIndex(
|
||||
(f) => f.uniqueId === notUploadedFiles[i].uniqueId
|
||||
);
|
||||
if (fileIndex !== -1) {
|
||||
this.currentUploadNumber += 1;
|
||||
this.startSessionFunc(fileIndex, t);
|
||||
}
|
||||
}
|
||||
}, TIMEOUT);
|
||||
}
|
||||
} else {
|
||||
// console.log("IS NOT PARALLEL");
|
||||
let index = 0;
|
||||
let len = files.length;
|
||||
while (index < len) {
|
||||
await this.startSessionFunc(index, t);
|
||||
index++;
|
||||
|
||||
files = this.files;
|
||||
len = files.length;
|
||||
}
|
||||
|
||||
if (!this.filesToConversion.length) {
|
||||
this.finishUploadFiles();
|
||||
} else {
|
||||
runInAction(() => {
|
||||
this.uploaded = true;
|
||||
this.isParallel = true;
|
||||
});
|
||||
const uploadedFiles = this.files.filter((x) => x.action === "uploaded");
|
||||
const totalErrorsCount = sumBy(uploadedFiles, (f) => (f.error ? 1 : 0));
|
||||
if (totalErrorsCount > 0)
|
||||
console.log("Upload errors: ", totalErrorsCount);
|
||||
|
||||
setTimeout(() => {
|
||||
if (!this.uploadPanelVisible && !totalErrorsCount && this.converted) {
|
||||
this.clearUploadedFiles();
|
||||
}
|
||||
}, TIMEOUT);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
startSessionFunc = (indexOfFile, t) => {
|
||||
//console.log("START UPLOAD SESSION FUNC", uploadData);
|
||||
// console.log("START UPLOAD SESSION FUNC");
|
||||
|
||||
if (!this.uploaded && this.files.length === 0) {
|
||||
this.uploaded = true;
|
||||
this.isParallel = true;
|
||||
//setUploadData(uploadData);
|
||||
return;
|
||||
}
|
||||
|
||||
const item = this.files[indexOfFile];
|
||||
this.files[indexOfFile].inAction = true;
|
||||
|
||||
if (!item) {
|
||||
console.error("Empty files");
|
||||
@ -949,15 +1046,17 @@ class UploadDataStore {
|
||||
return { location, requestsDataArray, fileSize, path };
|
||||
})
|
||||
.then(({ location, requestsDataArray, fileSize, path }) => {
|
||||
this.primaryProgressDataStore.setPrimaryProgressBarData({
|
||||
icon: "upload",
|
||||
visible: true,
|
||||
percent: this.percent,
|
||||
loadingFile: {
|
||||
uniqueId: this.files[indexOfFile].uniqueId,
|
||||
percent: chunks < 2 ? 50 : 0,
|
||||
},
|
||||
});
|
||||
if (!this.isParallel) {
|
||||
this.primaryProgressDataStore.setPrimaryProgressBarData({
|
||||
icon: "upload",
|
||||
visible: true,
|
||||
percent: this.percent,
|
||||
loadingFile: {
|
||||
uniqueId: this.files[indexOfFile].uniqueId,
|
||||
percent: chunks < 2 ? 50 : 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return this.uploadFileChunks(
|
||||
location,
|
||||
@ -992,9 +1091,9 @@ class UploadDataStore {
|
||||
|
||||
this.files[indexOfFile].error = errorMessage;
|
||||
|
||||
//dispatch(setUploadData(uploadData));
|
||||
|
||||
const newPercent = this.getNewPercent(fileSize, indexOfFile);
|
||||
const newPercent = this.isParallel
|
||||
? this.getFilesPercent(fileSize)
|
||||
: this.getNewPercent(fileSize, indexOfFile);
|
||||
|
||||
this.primaryProgressDataStore.setPrimaryProgressBarData({
|
||||
icon: "upload",
|
||||
@ -1004,6 +1103,42 @@ class UploadDataStore {
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
})
|
||||
.finally(() => {
|
||||
if (!this.isParallel) return;
|
||||
|
||||
const allFilesIsUploaded =
|
||||
this.files.findIndex((f) => f.action !== "uploaded" && !f.error) ===
|
||||
-1;
|
||||
|
||||
if (allFilesIsUploaded) {
|
||||
if (!this.filesToConversion.length) {
|
||||
this.finishUploadFiles();
|
||||
} else {
|
||||
runInAction(() => {
|
||||
this.uploaded = true;
|
||||
this.isParallel = true;
|
||||
});
|
||||
const uploadedFiles = this.files.filter(
|
||||
(x) => x.action === "uploaded"
|
||||
);
|
||||
const totalErrorsCount = sumBy(uploadedFiles, (f) =>
|
||||
f.error ? 1 : 0
|
||||
);
|
||||
if (totalErrorsCount > 0)
|
||||
console.log("Upload errors: ", totalErrorsCount);
|
||||
|
||||
setTimeout(() => {
|
||||
if (
|
||||
!this.uploadPanelVisible &&
|
||||
!totalErrorsCount &&
|
||||
this.converted
|
||||
) {
|
||||
this.clearUploadedFiles();
|
||||
}
|
||||
}, TIMEOUT);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@ -1011,6 +1146,21 @@ class UploadDataStore {
|
||||
const { fetchFiles, filter } = this.filesStore;
|
||||
const { withPaging } = this.authStore.settingsStore;
|
||||
|
||||
if (this.tempFiles.length) {
|
||||
this.uploaded = true;
|
||||
this.isParallel = true;
|
||||
this.converted = true;
|
||||
this.uploadedFilesSize = 0;
|
||||
|
||||
for (let item of this.tempFiles) {
|
||||
const { uploadFiles, folderId, t } = item;
|
||||
this.startUpload(uploadFiles, folderId, t);
|
||||
}
|
||||
this.tempFiles = [];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const totalErrorsCount = sumBy(this.files, (f) => {
|
||||
f.error && toastr.error(f.error);
|
||||
return f.error ? 1 : 0;
|
||||
@ -1025,7 +1175,9 @@ class UploadDataStore {
|
||||
}
|
||||
|
||||
this.uploaded = true;
|
||||
this.isParallel = true;
|
||||
this.converted = true;
|
||||
this.uploadedFilesSize = 0;
|
||||
|
||||
const uploadData = {
|
||||
filesSize: 0,
|
||||
|
@ -105,6 +105,7 @@ const FilterBlockItem = ({
|
||||
className="filter-text"
|
||||
noSelect={true}
|
||||
isSelected={item.isSelected}
|
||||
truncate
|
||||
>
|
||||
{item?.selectedLabel?.toLowerCase()}
|
||||
</StyledFilterBlockItemTagText>
|
||||
|
@ -80,7 +80,7 @@ public class FileMarker
|
||||
private readonly AuthContext _authContext;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly FilesSettingsHelper _filesSettingsHelper;
|
||||
|
||||
private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);
|
||||
public FileMarker(
|
||||
TenantManager tenantManager,
|
||||
UserManager userManager,
|
||||
@ -351,30 +351,41 @@ public class FileMarker
|
||||
var tagDao = _daoFactory.GetTagDao<T>();
|
||||
var newTags = new List<Tag>();
|
||||
var updateTags = new List<Tag>();
|
||||
|
||||
foreach (var userID in userEntriesData.Keys)
|
||||
|
||||
try
|
||||
{
|
||||
if (await tagDao.GetNewTagsAsync(userID, obj.FileEntry).AnyAsync())
|
||||
await _semaphore.WaitAsync();
|
||||
|
||||
foreach (var userID in userEntriesData.Keys)
|
||||
{
|
||||
continue;
|
||||
if (await tagDao.GetNewTagsAsync(userID, obj.FileEntry).AnyAsync())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var entries = userEntriesData[userID].Distinct().ToList();
|
||||
|
||||
await GetNewTagsAsync(userID, entries.OfType<FileEntry<int>>().ToList());
|
||||
await GetNewTagsAsync(userID, entries.OfType<FileEntry<string>>().ToList());
|
||||
}
|
||||
|
||||
if (updateTags.Count > 0)
|
||||
{
|
||||
await tagDao.UpdateNewTags(updateTags, obj.CurrentAccountId);
|
||||
}
|
||||
|
||||
if (newTags.Count > 0)
|
||||
{
|
||||
await tagDao.SaveTags(newTags, obj.CurrentAccountId);
|
||||
}
|
||||
|
||||
var entries = userEntriesData[userID].Distinct().ToList();
|
||||
|
||||
await GetNewTagsAsync(userID, entries.OfType<FileEntry<int>>().ToList());
|
||||
await GetNewTagsAsync(userID, entries.OfType<FileEntry<string>>().ToList());
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (updateTags.Count > 0)
|
||||
catch
|
||||
{
|
||||
await tagDao.UpdateNewTags(updateTags, obj.CurrentAccountId);
|
||||
throw;
|
||||
}
|
||||
|
||||
if (newTags.Count > 0)
|
||||
finally
|
||||
{
|
||||
await tagDao.SaveTags(newTags, obj.CurrentAccountId);
|
||||
_semaphore.Release();
|
||||
}
|
||||
|
||||
await Task.WhenAll(ExecMarkAsNewRequest(updateTags.Concat(newTags), socketManager));
|
||||
|
@ -41,7 +41,7 @@ public abstract class ApiControllerBase : ControllerBase
|
||||
_fileDtoHelper = fileDtoHelper;
|
||||
}
|
||||
|
||||
public async Task<FileEntryDto> GetFileEntryWrapperAsync(FileEntry r)
|
||||
protected async Task<FileEntryDto> GetFileEntryWrapperAsync(FileEntry r)
|
||||
{
|
||||
FileEntryDto wrapper = null;
|
||||
if (r.FileEntryType == FileEntryType.Folder)
|
||||
|
@ -32,18 +32,24 @@ public class SettingsController : ApiControllerBase
|
||||
{
|
||||
private readonly FileStorageService<string> _fileStorageServiceString;
|
||||
private readonly FilesSettingsHelper _filesSettingsHelper;
|
||||
private readonly ProductEntryPoint _productEntryPoint;
|
||||
|
||||
private readonly ProductEntryPoint _productEntryPoint;
|
||||
private readonly SettingsManager _settingsManager;
|
||||
private readonly TenantManager _tenantManager;
|
||||
|
||||
public SettingsController(
|
||||
FileStorageService<string> fileStorageServiceString,
|
||||
FilesSettingsHelper filesSettingsHelper,
|
||||
ProductEntryPoint productEntryPoint,
|
||||
FolderDtoHelper folderDtoHelper,
|
||||
FileDtoHelper fileDtoHelper) : base(folderDtoHelper, fileDtoHelper)
|
||||
FileDtoHelper fileDtoHelper,
|
||||
SettingsManager settingsManager,
|
||||
TenantManager tenantManager) : base(folderDtoHelper, fileDtoHelper)
|
||||
{
|
||||
_fileStorageServiceString = fileStorageServiceString;
|
||||
_filesSettingsHelper = filesSettingsHelper;
|
||||
_productEntryPoint = productEntryPoint;
|
||||
_productEntryPoint = productEntryPoint;
|
||||
_settingsManager = settingsManager;
|
||||
_tenantManager = tenantManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -33,7 +33,7 @@ public class Startup : BaseStartup
|
||||
public Startup(IConfiguration configuration, IHostEnvironment hostEnvironment)
|
||||
: base(configuration, hostEnvironment)
|
||||
{
|
||||
|
||||
WebhooksEnabled = true;
|
||||
}
|
||||
|
||||
public override void ConfigureServices(IServiceCollection services)
|
||||
|
@ -32,6 +32,7 @@ public class Startup : BaseStartup
|
||||
|
||||
public Startup(IConfiguration configuration, IHostEnvironment hostEnvironment) : base(configuration, hostEnvironment)
|
||||
{
|
||||
WebhooksEnabled = true;
|
||||
}
|
||||
|
||||
public override void ConfigureServices(IServiceCollection services)
|
||||
|
@ -206,7 +206,7 @@ public class ConnectionsController : ControllerBase
|
||||
}
|
||||
}
|
||||
|
||||
public async Task LogOutAllActiveConnections(Guid? userId = null)
|
||||
private async Task LogOutAllActiveConnections(Guid? userId = null)
|
||||
{
|
||||
var currentUserId = _securityContext.CurrentAccount.ID;
|
||||
var user = _userManager.GetUsers(userId ?? currentUserId);
|
||||
@ -217,7 +217,7 @@ public class ConnectionsController : ControllerBase
|
||||
await _cookiesManager.ResetUserCookie(user.Id);
|
||||
}
|
||||
|
||||
public int GetLoginEventIdFromCookie()
|
||||
private int GetLoginEventIdFromCookie()
|
||||
{
|
||||
var cookie = _cookiesManager.GetCookies(CookiesType.AuthKey);
|
||||
var loginEventId = _cookieStorage.GetLoginEventIdFromCookie(cookie);
|
||||
|
@ -169,7 +169,7 @@ public class RadicaleController : BaseSettingsController
|
||||
|
||||
}
|
||||
|
||||
public string CardDavAllSerialization(Uri uri)
|
||||
private string CardDavAllSerialization(Uri uri)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
var users = _userManager.GetUsers();
|
||||
@ -182,7 +182,7 @@ public class RadicaleController : BaseSettingsController
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public static CardDavItem ItemFromUserInfo(UserInfo u)
|
||||
private static CardDavItem ItemFromUserInfo(UserInfo u)
|
||||
{
|
||||
return new CardDavItem(u.Id, u.FirstName, u.LastName, u.UserName, u.BirthDate, u.Sex, u.Title, u.Email, u.ContactsList, u.MobilePhone);
|
||||
}
|
||||
|
@ -23,7 +23,8 @@
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
|
||||
|
||||
namespace ASC.Web.Api.Controllers.Settings;
|
||||
|
||||
public class WebhooksController : BaseSettingsController
|
||||
@ -33,6 +34,7 @@ public class WebhooksController : BaseSettingsController
|
||||
private readonly DbWorker _webhookDbWorker;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly WebhookPublisher _webhookPublisher;
|
||||
private readonly SettingsManager _settingsManager;
|
||||
|
||||
public WebhooksController(
|
||||
ApiContext context,
|
||||
@ -43,7 +45,8 @@ public class WebhooksController : BaseSettingsController
|
||||
DbWorker dbWorker,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IMapper mapper,
|
||||
WebhookPublisher webhookPublisher)
|
||||
WebhookPublisher webhookPublisher,
|
||||
SettingsManager settingsManager)
|
||||
: base(apiContext, memoryCache, webItemManager, httpContextAccessor)
|
||||
{
|
||||
_context = context;
|
||||
@ -51,6 +54,7 @@ public class WebhooksController : BaseSettingsController
|
||||
_webhookDbWorker = dbWorker;
|
||||
_mapper = mapper;
|
||||
_webhookPublisher = webhookPublisher;
|
||||
_settingsManager = settingsManager;
|
||||
}
|
||||
|
||||
[HttpGet("webhook")]
|
||||
@ -72,7 +76,7 @@ public class WebhooksController : BaseSettingsController
|
||||
ArgumentNullException.ThrowIfNull(model.Uri);
|
||||
ArgumentNullException.ThrowIfNull(model.SecretKey);
|
||||
|
||||
var webhook = await _webhookDbWorker.AddWebhookConfig(model.Name, model.Uri, model.SecretKey);
|
||||
var webhook = await _webhookDbWorker.AddWebhookConfig(model.Uri, model.SecretKey);
|
||||
|
||||
return _mapper.Map<WebhooksConfig, WebhooksConfigDto>(webhook);
|
||||
}
|
||||
@ -85,7 +89,7 @@ public class WebhooksController : BaseSettingsController
|
||||
ArgumentNullException.ThrowIfNull(model.Uri);
|
||||
ArgumentNullException.ThrowIfNull(model.SecretKey);
|
||||
|
||||
var webhook = await _webhookDbWorker.UpdateWebhookConfig(model.Id, model.Name, model.Uri, model.SecretKey, model.Enabled);
|
||||
var webhook = await _webhookDbWorker.UpdateWebhookConfig(model.Id, model.Uri, model.SecretKey, model.Enabled);
|
||||
|
||||
return _mapper.Map<WebhooksConfig, WebhooksConfigDto>(webhook);
|
||||
}
|
||||
@ -107,7 +111,7 @@ public class WebhooksController : BaseSettingsController
|
||||
var startIndex = Convert.ToInt32(_context.StartIndex);
|
||||
var count = Convert.ToInt32(_context.Count);
|
||||
|
||||
await foreach (var j in _webhookDbWorker.ReadJournal(startIndex, count, model.Delivery, model.Hookname, model.Route))
|
||||
await foreach (var j in _webhookDbWorker.ReadJournal(startIndex, count, model.Delivery, model.HookUri, model.WebhookId))
|
||||
{
|
||||
yield return _mapper.Map<WebhooksLog, WebhooksLogDto>(j);
|
||||
}
|
||||
@ -135,7 +139,7 @@ public class WebhooksController : BaseSettingsController
|
||||
throw new HttpException(HttpStatusCode.Forbidden);
|
||||
}
|
||||
|
||||
var result = await _webhookPublisher.PublishAsync(item.Method, item.Route, item.RequestPayload, item.ConfigId);
|
||||
var result = await _webhookPublisher.PublishAsync(item.Id, item.RequestPayload, item.ConfigId);
|
||||
|
||||
return _mapper.Map<WebhooksLog, WebhooksLogDto>(result);
|
||||
}
|
||||
@ -154,9 +158,63 @@ public class WebhooksController : BaseSettingsController
|
||||
continue;
|
||||
}
|
||||
|
||||
var result = await _webhookPublisher.PublishAsync(item.Method, item.Route, item.RequestPayload, item.ConfigId);
|
||||
var result = await _webhookPublisher.PublishAsync(item.Id, item.RequestPayload, item.ConfigId);
|
||||
|
||||
yield return _mapper.Map<WebhooksLog, WebhooksLogDto>(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("webhook/ssl")]
|
||||
public WebhooksSslSettingsDto GetSslSettings()
|
||||
{
|
||||
_permissionContext.DemandPermissions(SecutiryConstants.EditPortalSettings);
|
||||
|
||||
var settings = _settingsManager.Load<WebHooksSettings>();
|
||||
|
||||
return _mapper.Map<WebhooksSslSettingsDto>(settings);
|
||||
}
|
||||
|
||||
[HttpPost("webhook/ssl/{isEnabled}")]
|
||||
public WebhooksSslSettingsDto SetSslSettings(bool isEnabled)
|
||||
{
|
||||
_permissionContext.DemandPermissions(SecutiryConstants.EditPortalSettings);
|
||||
|
||||
var settings = _settingsManager.Load<WebHooksSettings>();
|
||||
settings.EnableSSLVerification = isEnabled;
|
||||
_settingsManager.Save(settings);
|
||||
|
||||
return _mapper.Map<WebhooksSslSettingsDto>(settings);
|
||||
}
|
||||
|
||||
[HttpGet("webhooks")]
|
||||
public async IAsyncEnumerable<Webhook> Settings()
|
||||
{
|
||||
var settings = _settingsManager.Load<WebHooksSettings>();
|
||||
|
||||
foreach (var w in await _webhookDbWorker.GetWebhooksAsync())
|
||||
{
|
||||
w.Disable = settings.Ids.Contains(w.Id);
|
||||
yield return w;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("webhook/{id}")]
|
||||
public async Task<Webhook> DisableWebHook(int id)
|
||||
{
|
||||
var settings = _settingsManager.Load<WebHooksSettings>();
|
||||
|
||||
Webhook result = null;
|
||||
|
||||
if (!settings.Ids.Contains(id) && (result = await _webhookDbWorker.GetWebhookAsync(id)) != null)
|
||||
{
|
||||
settings.Ids.Add(id);
|
||||
}
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
_settingsManager.Save(settings);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@ -29,7 +29,6 @@ namespace ASC.Web.Api.ApiModels.RequestsDto;
|
||||
public class WebhooksConfigRequestsDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Uri { get; set; }
|
||||
public string SecretKey { get; set; }
|
||||
public bool? Enabled { get; set; }
|
||||
|
@ -30,7 +30,7 @@ public class WebhooksLogRequest
|
||||
{
|
||||
public DateTime? Delivery { get; set; }
|
||||
|
||||
public string Hookname { get; set; }
|
||||
public string HookUri { get; set; }
|
||||
|
||||
public string Route { get; set; }
|
||||
public int WebhookId { get; set; }
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ namespace ASC.Web.Api.ApiModels.ResponseDto;
|
||||
public class WebhooksConfigDto : IMapFrom<WebhooksConfig>
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Uri { get; set; }
|
||||
public string SecretKey { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
|
@ -0,0 +1,32 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
namespace ASC.Web.Api.ApiModels.ResponseDto;
|
||||
|
||||
public class WebhooksSslSettingsDto : IMapFrom<WebHooksSettings>
|
||||
{
|
||||
public bool IsEnabled { get; set; }
|
||||
}
|
@ -32,7 +32,7 @@ public class Startup : BaseStartup
|
||||
|
||||
public Startup(IConfiguration configuration, IHostEnvironment hostEnvironment) : base(configuration, hostEnvironment)
|
||||
{
|
||||
|
||||
WebhooksEnabled = true;
|
||||
}
|
||||
|
||||
public override void ConfigureServices(IServiceCollection services)
|
||||
|
@ -13,8 +13,13 @@
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="7.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\common\ASC.Api.Core\ASC.Api.Core.csproj" />
|
||||
<ProjectReference Include="..\..\common\ASC.Webhooks.Core\ASC.Webhooks.Core.csproj" />
|
||||
<ProjectReference Include="..\ASC.Web.Core\ASC.Web.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -24,13 +24,30 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
global using System.Collections.Concurrent;
|
||||
global using System.Net.Security;
|
||||
global using System.Security.Cryptography;
|
||||
global using System.Text;
|
||||
global using System.Text.Json;
|
||||
global using System.Text.Json.Serialization;
|
||||
|
||||
global using ASC.Api.Core;
|
||||
global using ASC.Api.Core.Extensions;
|
||||
global using ASC.Common;
|
||||
global using ASC.Common.Caching;
|
||||
global using ASC.Common.Log;
|
||||
global using ASC.Common.Utils;
|
||||
global using ASC.Core.Common.Settings;
|
||||
global using ASC.Data.Storage;
|
||||
global using ASC.FederatedLogin;
|
||||
global using ASC.FederatedLogin.LoginProviders;
|
||||
global using ASC.Web.Core.HttpHandlers;
|
||||
global using ASC.Web.Studio;
|
||||
global using ASC.Web.Webhooks;
|
||||
global using ASC.Webhooks;
|
||||
global using ASC.Webhooks.Core;
|
||||
global using ASC.Webhooks.Service.Log;
|
||||
global using ASC.Webhooks.Service.Services;
|
||||
|
||||
global using Autofac;
|
||||
|
||||
@ -38,3 +55,7 @@ global using Microsoft.AspNetCore.Builder;
|
||||
global using Microsoft.AspNetCore.Hosting;
|
||||
global using Microsoft.AspNetCore.HttpOverrides;
|
||||
global using Microsoft.Extensions.Hosting.WindowsServices;
|
||||
global using Microsoft.Extensions.Logging;
|
||||
|
||||
global using Polly;
|
||||
global using Polly.Extensions.Http;
|
||||
|
@ -24,6 +24,8 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using ASC.Webhooks.Extension;
|
||||
|
||||
using NLog;
|
||||
|
||||
var options = new WebApplicationOptions
|
||||
@ -35,6 +37,7 @@ var options = new WebApplicationOptions
|
||||
var builder = WebApplication.CreateBuilder(options);
|
||||
|
||||
builder.Configuration.AddDefaultConfiguration(builder.Environment)
|
||||
.AddWebhookConfiguration()
|
||||
.AddEnvironmentVariables()
|
||||
.AddCommandLine(args);
|
||||
|
||||
@ -56,10 +59,7 @@ try
|
||||
|
||||
startup.ConfigureServices(builder.Services);
|
||||
|
||||
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder =>
|
||||
{
|
||||
startup.ConfigureContainer(containerBuilder);
|
||||
});
|
||||
builder.Host.ConfigureContainer<ContainerBuilder>(startup.ConfigureContainer);
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
@ -86,5 +86,5 @@ finally
|
||||
public partial class Program
|
||||
{
|
||||
public static string Namespace = typeof(Startup).Namespace;
|
||||
public static string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1).Replace(".","");
|
||||
public static string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1).Replace(".", "");
|
||||
}
|
@ -76,5 +76,37 @@ public class Startup : BaseStartup
|
||||
DIHelper.TryAdd<FacebookLoginProvider>();
|
||||
DIHelper.TryAdd<LinkedInLoginProvider>();
|
||||
DIHelper.TryAdd<SsoHandlerService>();
|
||||
|
||||
|
||||
services.AddHttpClient();
|
||||
|
||||
DIHelper.TryAdd<DbWorker>();
|
||||
|
||||
services.AddHostedService<WorkerService>();
|
||||
DIHelper.TryAdd<WorkerService>();
|
||||
|
||||
services.AddHttpClient("webhook")
|
||||
.SetHandlerLifetime(TimeSpan.FromMinutes(5))
|
||||
.AddPolicyHandler((s, request) =>
|
||||
{
|
||||
var settings = s.GetRequiredService<Settings>();
|
||||
|
||||
return HttpPolicyExtensions
|
||||
.HandleTransientHttpError()
|
||||
.Or<SslException>()
|
||||
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||
.WaitAndRetryAsync(settings.RepeatCount.HasValue ? settings.RepeatCount.Value : 5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
|
||||
})
|
||||
.ConfigurePrimaryHttpMessageHandler((s) =>
|
||||
{
|
||||
return new HttpClientHandler()
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) =>
|
||||
{
|
||||
var helper = s.GetRequiredService<SslHelper>();
|
||||
return helper.ValidateCertificate(sslPolicyErrors);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -29,4 +29,7 @@ internal static partial class WebhookSenderLogger
|
||||
{
|
||||
[LoggerMessage(Level = LogLevel.Debug, Message = "Procedure: Finish.")]
|
||||
public static partial void DebugProcedureFinish(this ILogger<WorkerService> logger);
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Error)]
|
||||
public static partial void ErrorSSLVerification(this ILogger logger, Exception exception);
|
||||
}
|
@ -24,36 +24,46 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
|
||||
namespace ASC.Webhooks;
|
||||
public class Startup : BaseWorkerStartup
|
||||
[Scope]
|
||||
public class SslHelper
|
||||
{
|
||||
public Startup(IConfiguration configuration, IHostEnvironment hostEnvironment) :
|
||||
base(configuration, hostEnvironment)
|
||||
private readonly SettingsManager _settingsManager;
|
||||
|
||||
public SslHelper(
|
||||
SettingsManager settingsManager
|
||||
)
|
||||
{
|
||||
|
||||
_settingsManager = settingsManager;
|
||||
}
|
||||
|
||||
public override void ConfigureServices(IServiceCollection services)
|
||||
public bool ValidateCertificate(SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
base.ConfigureServices(services);
|
||||
|
||||
services.AddHttpClient();
|
||||
|
||||
DIHelper.TryAdd<DbWorker>();
|
||||
|
||||
services.AddHostedService<WorkerService>();
|
||||
DIHelper.TryAdd<WorkerService>();
|
||||
|
||||
services.AddHttpClient("webhook")
|
||||
.SetHandlerLifetime(TimeSpan.FromMinutes(5))
|
||||
.AddPolicyHandler((s, request) =>
|
||||
if (sslPolicyErrors == SslPolicyErrors.None)
|
||||
{
|
||||
var settings = s.GetRequiredService<Settings>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return HttpPolicyExtensions
|
||||
.HandleTransientHttpError()
|
||||
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||
.WaitAndRetryAsync(settings.RepeatCount.HasValue ? settings.RepeatCount.Value : 5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
|
||||
});
|
||||
var settings = _settingsManager.Load<WebHooksSettings>();
|
||||
|
||||
if (!settings.EnableSSLVerification)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new SslException(sslPolicyErrors);
|
||||
}
|
||||
}
|
||||
|
||||
public class SslException : Exception
|
||||
{
|
||||
public SslPolicyErrors Errors { get; set; }
|
||||
public override string Message { get => Errors.ToString(); }
|
||||
|
||||
public SslException(SslPolicyErrors errors)
|
||||
{
|
||||
Errors = errors;
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,8 @@
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using System.Net;
|
||||
|
||||
namespace ASC.Webhooks;
|
||||
|
||||
@ -35,6 +35,7 @@ public class WebhookSender
|
||||
private readonly ILogger _log;
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
||||
private const string SignatureHeader = "x-docspace-signature-256";
|
||||
|
||||
public WebhookSender(ILoggerProvider options, IServiceScopeFactory scopeFactory, IHttpClientFactory clientFactory)
|
||||
{
|
||||
@ -70,7 +71,7 @@ public class WebhookSender
|
||||
};
|
||||
|
||||
request.Headers.Add("Accept", "*/*");
|
||||
request.Headers.Add("Secret", "SHA256=" + GetSecretHash(entry.Config.SecretKey, entry.RequestPayload));
|
||||
request.Headers.Add(SignatureHeader, $"sha256={GetSecretHash(entry.Config.SecretKey, entry.RequestPayload)}");
|
||||
requestHeaders = JsonSerializer.Serialize(request.Headers.ToDictionary(r => r.Key, v => v.Value), _jsonSerializerOptions);
|
||||
|
||||
var response = await httpClient.SendAsync(request, cancellationToken);
|
||||
@ -78,40 +79,49 @@ public class WebhookSender
|
||||
status = (int)response.StatusCode;
|
||||
responseHeaders = JsonSerializer.Serialize(response.Headers.ToDictionary(r => r.Key, v => v.Value), _jsonSerializerOptions);
|
||||
responsePayload = await response.Content.ReadAsStringAsync();
|
||||
delivery = DateTime.UtcNow;
|
||||
|
||||
_log.DebugResponse(response);
|
||||
}
|
||||
catch (SslException e)
|
||||
{
|
||||
status = (int)e.Errors;
|
||||
responsePayload = e.Message;
|
||||
|
||||
_log.ErrorSSLVerification(e);
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
if (e.StatusCode.HasValue)
|
||||
{
|
||||
status = (int)e.StatusCode.Value;
|
||||
}
|
||||
|
||||
//if (e.InnerException is SocketException se)
|
||||
//{
|
||||
// status = (int)se.SocketErrorCode;
|
||||
//}
|
||||
|
||||
responsePayload = e.Message;
|
||||
delivery = DateTime.UtcNow;
|
||||
|
||||
_log.ErrorWithException(e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
status = (int)HttpStatusCode.InternalServerError;
|
||||
_log.ErrorWithException(e);
|
||||
}
|
||||
|
||||
if (delivery != DateTime.MinValue)
|
||||
{
|
||||
await dbWorker.UpdateWebhookJournal(entry.Id, status, delivery, requestHeaders, responsePayload, responseHeaders);
|
||||
}
|
||||
delivery = DateTime.UtcNow;
|
||||
|
||||
await dbWorker.UpdateWebhookJournal(entry.Id, status, delivery, requestHeaders, responsePayload, responseHeaders);
|
||||
}
|
||||
|
||||
private string GetSecretHash(string secretKey, string body)
|
||||
{
|
||||
var secretBytes = Encoding.UTF8.GetBytes(secretKey);
|
||||
|
||||
using (var hasher = new HMACSHA256(secretBytes))
|
||||
{
|
||||
var data = Encoding.UTF8.GetBytes(body);
|
||||
return BitConverter.ToString(hasher.ComputeHash(data));
|
||||
}
|
||||
using var hasher = new HMACSHA256(secretBytes);
|
||||
var data = Encoding.UTF8.GetBytes(body);
|
||||
var hash = hasher.ComputeHash(data);
|
||||
return Convert.ToHexString(hash);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user