Compare commits
30 Commits
bugfix/rea
...
b6e63841cd
| Author | SHA1 | Date | |
|---|---|---|---|
| b6e63841cd | |||
| f051896296 | |||
| 92b93e862e | |||
| 8876f5c286 | |||
| e93c7e8bc1 | |||
| 16493b4594 | |||
| 938504b2d1 | |||
| 3eb718f6ac | |||
| 99781aeb8a | |||
| ffcd41f4dc | |||
| a7ed9be1de | |||
| 32fbf782fa | |||
| bfae72529c | |||
| 67e6f288eb | |||
| 823bafeeb9 | |||
| 750b9f1b57 | |||
| 0a4daccc0f | |||
| bc4905d2f4 | |||
| 7c737ee6ad | |||
| 8a796a2eec | |||
| 83957d28e9 | |||
| fe3f1347d5 | |||
| 1e35e0447f | |||
| 7828ed237d | |||
|
|
45377ea61c | ||
|
|
b5748550d1 | ||
| 64c018b92e | |||
| 176672d7eb | |||
| 05d54e87c3 | |||
| 06c2a07fbc |
@@ -1,3 +1,4 @@
|
|||||||
|
using DigitalData.Auth.Claims;
|
||||||
using EnvelopeGenerator.API.Controllers.Interfaces;
|
using EnvelopeGenerator.API.Controllers.Interfaces;
|
||||||
using EnvelopeGenerator.API.Models;
|
using EnvelopeGenerator.API.Models;
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
@@ -73,4 +74,44 @@ public partial class AuthController(IOptions<AuthTokenKeys> authTokenKeyOptions,
|
|||||||
=> role is not null && !User.IsInRole(role)
|
=> role is not null && !User.IsInRole(role)
|
||||||
? Unauthorized()
|
? Unauthorized()
|
||||||
: Ok();
|
: Ok();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the caller holds a valid per-envelope receiver token for the given envelope key.
|
||||||
|
/// The request must carry a cookie named <c>AuthTokenSignFLOWReceiver.{envelopeKey}</c>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="envelopeKey">The unique envelope key extracted from the route.</param>
|
||||||
|
/// <response code="200">Valid per-envelope token found.</response>
|
||||||
|
/// <response code="401">Token is missing, expired or invalid.</response>
|
||||||
|
[ProducesResponseType(typeof(void), StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
||||||
|
[Authorize(Policy = AuthPolicy.Receiver)]
|
||||||
|
[HttpGet("check/envelope/{envelopeKey}")]
|
||||||
|
public IActionResult CheckEnvelopeReceiver([FromRoute] string envelopeKey) => Ok();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the per-envelope receiver cookie for the given envelope key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="envelopeKey">The unique envelope key whose cookie should be deleted.</param>
|
||||||
|
/// <response code="200">Cookie successfully deleted.</response>
|
||||||
|
[ProducesResponseType(typeof(void), StatusCodes.Status200OK)]
|
||||||
|
[HttpPost("logout/envelope/{envelopeKey}")]
|
||||||
|
public IActionResult LogoutEnvelopeReceiver([FromRoute] string envelopeKey)
|
||||||
|
{
|
||||||
|
var cookieName = CookieNames.GetEnvelopeReceiverCookieName(authTokenKeys.Cookie, envelopeKey);
|
||||||
|
Response.Cookies.Delete(cookieName);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all per-envelope receiver cookies from the current request.
|
||||||
|
/// </summary>
|
||||||
|
/// <response code="200">All envelope receiver cookies successfully deleted.</response>
|
||||||
|
[ProducesResponseType(typeof(void), StatusCodes.Status200OK)]
|
||||||
|
[HttpPost("logout/envelope")]
|
||||||
|
public IActionResult LogoutAllEnvelopeReceivers()
|
||||||
|
{
|
||||||
|
foreach (var cookieName in Request.Cookies.Keys.Where(k => CookieNames.IsEnvelopeReceiverCookie(k, authTokenKeys.Cookie)))
|
||||||
|
Response.Cookies.Delete(cookieName);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using DigitalData.Auth.Claims;
|
||||||
using EnvelopeGenerator.API.Controllers.Interfaces;
|
using EnvelopeGenerator.API.Controllers.Interfaces;
|
||||||
using EnvelopeGenerator.API.Extensions;
|
using EnvelopeGenerator.API.Extensions;
|
||||||
using EnvelopeGenerator.Application.Documents.Queries;
|
using EnvelopeGenerator.Application.Documents.Queries;
|
||||||
@@ -5,6 +6,7 @@ using EnvelopeGenerator.Domain.Constants;
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Controllers;
|
namespace EnvelopeGenerator.API.Controllers;
|
||||||
|
|
||||||
@@ -17,7 +19,7 @@ namespace EnvelopeGenerator.API.Controllers;
|
|||||||
[Authorize]
|
[Authorize]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class DocumentController(IMediator mediator, IAuthorizationService authService) : ControllerBase, IAuthController
|
public class DocumentController(IMediator mediator, IAuthorizationService authService, ILogger<DocumentController> logger) : ControllerBase, IAuthController
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
@@ -60,4 +62,33 @@ public class DocumentController(IMediator mediator, IAuthorizationService authSe
|
|||||||
|
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="envelopeKey"></param>
|
||||||
|
/// <param name="cancel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet("{envelopeKey}")]
|
||||||
|
[Authorize(Policy = AuthPolicy.Receiver)]
|
||||||
|
public async Task<IActionResult> GetDocumentOfReceiver(string envelopeKey, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var envelopeIdStr = User.FindFirst(EnvelopeClaimNames.EnvelopeId)?.Value;
|
||||||
|
|
||||||
|
if (!int.TryParse(envelopeIdStr, out int envelopeId))
|
||||||
|
{
|
||||||
|
logger.LogError(
|
||||||
|
"Inner service error: Failed to parse Envelope ID from claims. Claim '{ClaimName}' had an invalid or missing value: '{ClaimValue}'.",
|
||||||
|
EnvelopeClaimNames.EnvelopeId,
|
||||||
|
envelopeIdStr ?? "null");
|
||||||
|
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError, "Inner service error: Invalid envelope claim.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var senderDoc = await mediator.Send(new ReadDocumentQuery() { EnvelopeId = envelopeId }, cancel);
|
||||||
|
|
||||||
|
return senderDoc.ByteData is byte[] senderDocByte
|
||||||
|
? File(senderDocByte, "application/octet-stream")
|
||||||
|
: NotFound("Document is empty.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,7 @@ using EnvelopeGenerator.Application.Common.SQL;
|
|||||||
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
||||||
using EnvelopeGenerator.API.Extensions;
|
using EnvelopeGenerator.API.Extensions;
|
||||||
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Controllers;
|
namespace EnvelopeGenerator.API.Controllers;
|
||||||
|
|
||||||
@@ -73,6 +74,24 @@ public class EnvelopeReceiverController : ControllerBase
|
|||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="envelopeKey"></param>
|
||||||
|
/// <param name="cancel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Authorize(Policy = AuthPolicy.Receiver)]
|
||||||
|
[HttpGet("{envelopeKey}")]
|
||||||
|
public async Task<IActionResult> GetEnvelopeReceiverOfReceiver([FromRoute] string envelopeKey, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var er = await _mediator.Send(new ReadEnvelopeReceiverQuery()
|
||||||
|
{
|
||||||
|
Key = envelopeKey
|
||||||
|
}, cancel);
|
||||||
|
|
||||||
|
return Ok(er);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ruft den Namen des zuletzt verwendeten Empfängers basierend auf der angegebenen E-Mail-Adresse ab.
|
/// Ruft den Namen des zuletzt verwendeten Empfängers basierend auf der angegebenen E-Mail-Adresse ab.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -16,6 +16,12 @@ public sealed class AuthProxyDocumentFilter : IDocumentFilter
|
|||||||
/// <param name="swaggerDoc"></param>
|
/// <param name="swaggerDoc"></param>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
|
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
|
||||||
|
{
|
||||||
|
AddLoginOperation(swaggerDoc, context);
|
||||||
|
AddEnvelopeReceiverLoginOperation(swaggerDoc, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddLoginOperation(OpenApiDocument swaggerDoc, DocumentFilterContext context)
|
||||||
{
|
{
|
||||||
const string path = "/api/auth";
|
const string path = "/api/auth";
|
||||||
|
|
||||||
@@ -67,4 +73,51 @@ public sealed class AuthProxyDocumentFilter : IDocumentFilter
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void AddEnvelopeReceiverLoginOperation(OpenApiDocument swaggerDoc, DocumentFilterContext context)
|
||||||
|
{
|
||||||
|
const string path = "/api/Auth/envelope-receiver/{key}";
|
||||||
|
|
||||||
|
var bodySchema = context.SchemaGenerator.GenerateSchema(typeof(EnvelopeReceiverLogin), context.SchemaRepository);
|
||||||
|
|
||||||
|
var operation = new OpenApiOperation
|
||||||
|
{
|
||||||
|
Summary = "Envelope receiver login (auth-hub proxy)",
|
||||||
|
Description = "Proxies the envelope receiver login to the auth service. " +
|
||||||
|
"The `cookie` query parameter is always forwarded as `true` so the auth service sets the per-envelope cookie automatically.",
|
||||||
|
Tags = [new() { Name = "Auth" }],
|
||||||
|
Parameters =
|
||||||
|
{
|
||||||
|
new OpenApiParameter
|
||||||
|
{
|
||||||
|
Name = "key",
|
||||||
|
In = ParameterLocation.Path,
|
||||||
|
Required = true,
|
||||||
|
Schema = new OpenApiSchema { Type = "string" },
|
||||||
|
Description = "The unique envelope receiver key."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RequestBody = new OpenApiRequestBody
|
||||||
|
{
|
||||||
|
Required = false,
|
||||||
|
Content =
|
||||||
|
{
|
||||||
|
["multipart/form-data"] = new OpenApiMediaType { Schema = bodySchema }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Responses =
|
||||||
|
{
|
||||||
|
["200"] = new OpenApiResponse { Description = "OK – per-envelope cookie set by auth service." },
|
||||||
|
["401"] = new OpenApiResponse { Description = "Unauthorized – invalid or missing access code." }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
swaggerDoc.Paths[path] = new OpenApiPathItem
|
||||||
|
{
|
||||||
|
Operations =
|
||||||
|
{
|
||||||
|
[OperationType.Post] = operation
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AspNetCore.Scalar" Version="1.1.8" />
|
<PackageReference Include="AspNetCore.Scalar" Version="1.1.8" />
|
||||||
|
<PackageReference Include="DigitalData.Auth.Claims" Version="1.0.3" />
|
||||||
<PackageReference Include="DigitalData.Auth.Client" Version="1.3.7" />
|
<PackageReference Include="DigitalData.Auth.Client" Version="1.3.7" />
|
||||||
<PackageReference Include="DigitalData.Core.API" Version="2.2.1" />
|
<PackageReference Include="DigitalData.Core.API" Version="2.2.1" />
|
||||||
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
|
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
|
||||||
|
|||||||
7
EnvelopeGenerator.API/Models/EnvelopeReceiverLogin.cs
Normal file
7
EnvelopeGenerator.API/Models/EnvelopeReceiverLogin.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace EnvelopeGenerator.API.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request body for the envelope-receiver login endpoint.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="AccessCode">The access code sent to the receiver.</param>
|
||||||
|
public record EnvelopeReceiverLogin(string? AccessCode = null);
|
||||||
@@ -19,6 +19,7 @@ using DigitalData.Core.Abstractions.Security.Extensions;
|
|||||||
using EnvelopeGenerator.API.Middleware;
|
using EnvelopeGenerator.API.Middleware;
|
||||||
using NLog.Web;
|
using NLog.Web;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using DigitalData.Auth.Claims;
|
||||||
|
|
||||||
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
||||||
logger.Info("Logging initialized!");
|
logger.Info("Logging initialized!");
|
||||||
@@ -112,7 +113,7 @@ try
|
|||||||
});
|
});
|
||||||
builder.Services.AddOpenApi();
|
builder.Services.AddOpenApi();
|
||||||
|
|
||||||
//AddEF Core dbcontext
|
//Add EF Core dbcontext
|
||||||
var useDbMigration = Environment.GetEnvironmentVariable("MIGRATION_TEST_MODE") == true.ToString() || config.GetValue<bool>("UseDbMigration");
|
var useDbMigration = Environment.GetEnvironmentVariable("MIGRATION_TEST_MODE") == true.ToString() || config.GetValue<bool>("UseDbMigration");
|
||||||
var cnnStrName = useDbMigration ? "DbMigrationTest" : "Default";
|
var cnnStrName = useDbMigration ? "DbMigrationTest" : "Default";
|
||||||
var connStr = config.GetConnectionString(cnnStrName)
|
var connStr = config.GetConnectionString(cnnStrName)
|
||||||
@@ -126,6 +127,9 @@ try
|
|||||||
|
|
||||||
var authTokenKeys = config.GetOrDefault<AuthTokenKeys>();
|
var authTokenKeys = config.GetOrDefault<AuthTokenKeys>();
|
||||||
|
|
||||||
|
// Scheme name used for per-envelope receiver JWT authentication.
|
||||||
|
const string EnvelopeReceiverScheme = "EnvelopeReceiverJwt";
|
||||||
|
|
||||||
builder.Services.AddAuthentication(options =>
|
builder.Services.AddAuthentication(options =>
|
||||||
{
|
{
|
||||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
@@ -163,6 +167,61 @@ try
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
})
|
||||||
|
// Per-envelope receiver scheme: reads the JWT from the cookie named
|
||||||
|
// AuthTokenSignFLOWReceiver.{envelope_key} where envelope_key is the
|
||||||
|
// last path segment of the request URL.
|
||||||
|
// This enables simultaneous authentication for multiple envelopes
|
||||||
|
// within the same browser session.
|
||||||
|
.AddJwtBearer(EnvelopeReceiverScheme, opt =>
|
||||||
|
{
|
||||||
|
opt.TokenValidationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
IssuerSigningKeyResolver = (token, securityToken, identifier, parameters) =>
|
||||||
|
{
|
||||||
|
var clientParams = deferredProvider.GetOptions<ClientParams>();
|
||||||
|
var publicKey = clientParams!.PublicKeys.Get(authTokenKeys.Issuer, authTokenKeys.Audience);
|
||||||
|
return [publicKey.SecurityKey];
|
||||||
|
},
|
||||||
|
ValidateIssuer = true,
|
||||||
|
ValidIssuer = authTokenKeys.Issuer,
|
||||||
|
ValidateAudience = true,
|
||||||
|
ValidAudience = authTokenKeys.Audience,
|
||||||
|
};
|
||||||
|
|
||||||
|
opt.Events = new JwtBearerEvents
|
||||||
|
{
|
||||||
|
OnMessageReceived = context =>
|
||||||
|
{
|
||||||
|
var paths = context.Request.Path.Value?.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
// Derive the envelope key from the last route segment: /{envelope_key}
|
||||||
|
var envelopeKey = paths?.LastOrDefault();
|
||||||
|
|
||||||
|
if (envelopeKey is not null)
|
||||||
|
{
|
||||||
|
var cookieName = CookieNames.GetEnvelopeReceiverCookieName(authTokenKeys.Cookie, envelopeKey);
|
||||||
|
if (context.Request.Cookies.TryGetValue(cookieName, out var cookieToken) && cookieToken is not null)
|
||||||
|
context.Token = cookieToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
},
|
||||||
|
OnTokenValidated = context =>
|
||||||
|
{
|
||||||
|
var paths = context.Request.Path.Value?.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var envelopeKey = paths?.LastOrDefault();
|
||||||
|
|
||||||
|
var sub = context.Principal?.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value
|
||||||
|
?? context.Principal?.FindFirst("sub")?.Value;
|
||||||
|
|
||||||
|
if (envelopeKey is null || sub != envelopeKey)
|
||||||
|
context.Fail("Envelope key in the path does not match the token subject.");
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Authentication
|
// Authentication
|
||||||
@@ -182,8 +241,13 @@ try
|
|||||||
policy.RequireRole(Role.Sender, Role.Receiver.Full))
|
policy.RequireRole(Role.Sender, Role.Receiver.Full))
|
||||||
.AddPolicy(AuthPolicy.Sender, policy =>
|
.AddPolicy(AuthPolicy.Sender, policy =>
|
||||||
policy.RequireRole(Role.Sender))
|
policy.RequireRole(Role.Sender))
|
||||||
|
// Per-envelope policy: uses the dedicated EnvelopeReceiverJwt scheme so it
|
||||||
|
// never conflicts with the default JwtBearer scheme.
|
||||||
.AddPolicy(AuthPolicy.Receiver, policy =>
|
.AddPolicy(AuthPolicy.Receiver, policy =>
|
||||||
policy.RequireRole(Role.Receiver.Full))
|
policy
|
||||||
|
.AddAuthenticationSchemes(EnvelopeReceiverScheme)
|
||||||
|
.RequireAuthenticatedUser()
|
||||||
|
.RequireRole(Role.Receiver.Full, "receiver"))
|
||||||
.AddPolicy(AuthPolicy.ReceiverTFA, policy =>
|
.AddPolicy(AuthPolicy.ReceiverTFA, policy =>
|
||||||
policy.RequireRole(Role.Receiver.TFA));
|
policy.RequireRole(Role.Receiver.TFA));
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"UseSwagger": true,
|
"UseSwagger": true,
|
||||||
"UseDbMigration": true,
|
"UseDbMigration": false,
|
||||||
"DiPMode": true,
|
"DiPMode": true,
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
|
|||||||
@@ -10,6 +10,17 @@
|
|||||||
"Transforms": [
|
"Transforms": [
|
||||||
{ "PathSet": "/api/auth/sign-flow" }
|
{ "PathSet": "/api/auth/sign-flow" }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"auth-envelope-receiver-login": {
|
||||||
|
"ClusterId": "auth-hub",
|
||||||
|
"Match": {
|
||||||
|
"Path": "/api/Auth/envelope-receiver/{key}",
|
||||||
|
"Methods": [ "POST" ]
|
||||||
|
},
|
||||||
|
"Transforms": [
|
||||||
|
{ "PathPattern": "/api/auth/envelope-receiver/{key}" },
|
||||||
|
{ "QueryValueParameter": "cookie", "Set": "true" }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Clusters": {
|
"Clusters": {
|
||||||
|
|||||||
@@ -13,12 +13,12 @@ public static class AutoMapperAuditingExtensions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static IMappingExpression<TSource, TDestination> MapAddedWhen<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
|
public static IMappingExpression<TSource, TDestination> MapAddedWhen<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
|
||||||
where TDestination : IHasAddedWhen
|
where TDestination : IHasAddedWhen
|
||||||
=> expression.ForMember(dest => dest.AddedWhen, opt => opt.MapFrom(_ => DateTime.UtcNow));
|
=> expression.ForMember(dest => dest.AddedWhen, opt => opt.MapFrom(_ => DateTime.Now));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maps <see cref="IHasChangedWhen.ChangedWhen"/> to the current UTC time.
|
/// Maps <see cref="IHasChangedWhen.ChangedWhen"/> to the current UTC time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IMappingExpression<TSource, TDestination> MapChangedWhen<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
|
public static IMappingExpression<TSource, TDestination> MapChangedWhen<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
|
||||||
where TDestination : IHasChangedWhen
|
where TDestination : IHasChangedWhen
|
||||||
=> expression.ForMember(dest => dest.ChangedWhen, opt => opt.MapFrom(_ => DateTime.UtcNow));
|
=> expression.ForMember(dest => dest.ChangedWhen, opt => opt.MapFrom(_ => DateTime.Now));
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ public abstract class SendMailHandler<TNotification> : INotificationHandler<TNot
|
|||||||
EmailAddress = notification.EmailAddress,
|
EmailAddress = notification.EmailAddress,
|
||||||
EmailBody = temp.Body,
|
EmailBody = temp.Body,
|
||||||
EmailSubj = temp.Subject,
|
EmailSubj = temp.Subject,
|
||||||
AddedWhen = DateTime.UtcNow,
|
AddedWhen = DateTime.Now,
|
||||||
AddedWho = DispatcherParams.AddedWho,
|
AddedWho = DispatcherParams.AddedWho,
|
||||||
SendingProfile = DispatcherParams.SendingProfile,
|
SendingProfile = DispatcherParams.SendingProfile,
|
||||||
ReminderTypeId = DispatcherParams.ReminderTypeId,
|
ReminderTypeId = DispatcherParams.ReminderTypeId,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class MappingProfile : Profile
|
|||||||
CreateMap<UpdateDocStatusCommand, DocumentStatus>()
|
CreateMap<UpdateDocStatusCommand, DocumentStatus>()
|
||||||
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
|
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
|
||||||
.ForMember(dest => dest.Receiver, opt => opt.Ignore())
|
.ForMember(dest => dest.Receiver, opt => opt.Ignore())
|
||||||
.ForMember(dest => dest.StatusChangedWhen, opt => opt.MapFrom(src => DateTime.UtcNow))
|
.ForMember(dest => dest.StatusChangedWhen, opt => opt.MapFrom(src => DateTime.Now))
|
||||||
.MapChangedWhen();
|
.MapChangedWhen();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using EnvelopeGenerator.Application.Envelopes.Queries;
|
||||||
|
using EnvelopeGenerator.Application.Receivers.Queries;
|
||||||
|
using MediatR;
|
||||||
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
||||||
|
using EnvelopeGenerator.Application.Common.Query;
|
||||||
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a query for reading an envelope receiver including sensitive fields
|
||||||
|
/// (access code, phone number) that are excluded from the standard <see cref="ReadEnvelopeReceiverQuery"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Returns a single <see cref="EnvelopeReceiverSecretDto"/> matched by UUID and receiver signature.
|
||||||
|
/// Equivalent to the legacy <c>ReadWithSecretByUuidSignatureAsync</c> service method.
|
||||||
|
/// </remarks>
|
||||||
|
public record ReadEnvelopeReceiverSecretQuery
|
||||||
|
: EnvelopeReceiverQueryBase<ReadEnvelopeQuery, ReadReceiverQuery>,
|
||||||
|
IRequest<EnvelopeReceiverSecretDto?>;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for dispatching <see cref="ReadEnvelopeReceiverSecretQuery"/> via <see cref="IMediator"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class ReadEnvelopeReceiverSecretQueryExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a <see cref="ReadEnvelopeReceiverSecretQuery"/> using the composite key (uuid::signature).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mediator">The mediator instance.</param>
|
||||||
|
/// <param name="key">Composite key in the format <c>uuid::signature</c>.</param>
|
||||||
|
/// <param name="cancel">Cancellation token.</param>
|
||||||
|
/// <returns>The matching <see cref="EnvelopeReceiverSecretDto"/>, or <c>null</c> if not found.</returns>
|
||||||
|
public static Task<EnvelopeReceiverSecretDto?> ReadEnvelopeReceiverSecretAsync(
|
||||||
|
this IMediator mediator,
|
||||||
|
string key,
|
||||||
|
CancellationToken cancel = default)
|
||||||
|
=> mediator.Send(new ReadEnvelopeReceiverSecretQuery { Key = key }, cancel);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a <see cref="ReadEnvelopeReceiverSecretQuery"/> using UUID and receiver signature.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mediator">The mediator instance.</param>
|
||||||
|
/// <param name="uuid">Envelope UUID.</param>
|
||||||
|
/// <param name="signature">Receiver signature.</param>
|
||||||
|
/// <param name="cancel">Cancellation token.</param>
|
||||||
|
/// <returns>The matching <see cref="EnvelopeReceiverSecretDto"/>, or <c>null</c> if not found.</returns>
|
||||||
|
public static Task<EnvelopeReceiverSecretDto?> ReadEnvelopeReceiverSecretAsync(
|
||||||
|
this IMediator mediator,
|
||||||
|
string uuid,
|
||||||
|
string signature,
|
||||||
|
CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
var q = new ReadEnvelopeReceiverSecretQuery();
|
||||||
|
q.Envelope.Uuid = uuid;
|
||||||
|
q.Receiver.Signature = signature;
|
||||||
|
return mediator.Send(q, cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles <see cref="ReadEnvelopeReceiverSecretQuery"/> and returns a
|
||||||
|
/// <see cref="EnvelopeReceiverSecretDto"/> containing sensitive fields.
|
||||||
|
/// </summary>
|
||||||
|
public class ReadEnvelopeReceiverSecretQueryHandler
|
||||||
|
: IRequestHandler<ReadEnvelopeReceiverSecretQuery, EnvelopeReceiverSecretDto?>
|
||||||
|
{
|
||||||
|
private readonly IRepository<EnvelopeReceiver> _repo;
|
||||||
|
private readonly IRepository<Receiver> _rcvRepo;
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="ReadEnvelopeReceiverSecretQueryHandler"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="envelopeReceiver">Repository for <see cref="EnvelopeReceiver"/>.</param>
|
||||||
|
/// <param name="rcvRepo">Repository for <see cref="Receiver"/>.</param>
|
||||||
|
/// <param name="mapper">AutoMapper instance.</param>
|
||||||
|
public ReadEnvelopeReceiverSecretQueryHandler(
|
||||||
|
IRepository<EnvelopeReceiver> envelopeReceiver,
|
||||||
|
IRepository<Receiver> rcvRepo,
|
||||||
|
IMapper mapper)
|
||||||
|
{
|
||||||
|
_repo = envelopeReceiver;
|
||||||
|
_rcvRepo = rcvRepo;
|
||||||
|
_mapper = mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the query and returns the matching <see cref="EnvelopeReceiverSecretDto"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">The query containing filter criteria.</param>
|
||||||
|
/// <param name="cancel">Cancellation token.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// The matched <see cref="EnvelopeReceiverSecretDto"/>, or <c>null</c> if no record is found.
|
||||||
|
/// </returns>
|
||||||
|
public async Task<EnvelopeReceiverSecretDto?> Handle(
|
||||||
|
ReadEnvelopeReceiverSecretQuery request,
|
||||||
|
CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var q = _repo.Query.Where(request, notnull: false);
|
||||||
|
|
||||||
|
var envRcvs = await q
|
||||||
|
.Include(er => er.Envelope).ThenInclude(e => e!.Documents!).ThenInclude(d => d.Elements)
|
||||||
|
.Include(er => er.Envelope).ThenInclude(e => e!.Histories)
|
||||||
|
.Include(er => er.Envelope).ThenInclude(e => e!.User)
|
||||||
|
.Include(er => er.Receiver)
|
||||||
|
.ToListAsync(cancel);
|
||||||
|
|
||||||
|
if (request.Receiver.HasAnyCriteria && envRcvs.Count != 0)
|
||||||
|
{
|
||||||
|
var receiver = await _rcvRepo.Query.Where(request.Receiver).FirstAsync(cancel);
|
||||||
|
|
||||||
|
foreach (var item in envRcvs)
|
||||||
|
item.Envelope?.Documents?.FirstOrDefault()?.Elements?.RemoveAll(s => s.ReceiverId != receiver.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
var envRcv = envRcvs.FirstOrDefault();
|
||||||
|
if (envRcv is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return _mapper.Map<EnvelopeReceiverSecretDto>(envRcv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,7 +34,7 @@ public record CreateHistoryCommand : EnvelopeReceiverQueryBase, IRequest<History
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime AddedWhen { get; } = DateTime.UtcNow;
|
public DateTime AddedWhen { get; } = DateTime.Now;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -448,7 +448,7 @@
|
|||||||
<value>Document has been reset.</value>
|
<value>Document has been reset.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DocumentSuccessfullyConfirmed" xml:space="preserve">
|
<data name="DocumentSuccessfullyConfirmed" xml:space="preserve">
|
||||||
<value>Document successfully red and confirmed!</value>
|
<value>Document successfully read and confirmed!</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DocumentConfirmedConfirmationMessage" xml:space="preserve">
|
<data name="DocumentConfirmedConfirmationMessage" xml:space="preserve">
|
||||||
<value>You have read and confirmed the document. You will receive a written confirmation afterwards.</value>
|
<value>You have read and confirmed the document. You will receive a written confirmation afterwards.</value>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ Imports DigitalData.Core.Abstraction.Application
|
|||||||
Imports EnvelopeGenerator.Infrastructure
|
Imports EnvelopeGenerator.Infrastructure
|
||||||
Imports Microsoft.EntityFrameworkCore
|
Imports Microsoft.EntityFrameworkCore
|
||||||
Imports DigitalData.Core.Abstractions
|
Imports DigitalData.Core.Abstractions
|
||||||
|
Imports EnvelopeGenerator.Domain.Interfaces
|
||||||
|
|
||||||
Namespace Jobs
|
Namespace Jobs
|
||||||
Public Class FinalizeDocumentJob
|
Public Class FinalizeDocumentJob
|
||||||
@@ -350,7 +351,7 @@ Namespace Jobs
|
|||||||
Logger.Warn($"No SendFinalEmailToCreator - oMailToCreator [{oMailToCreator}] <> [{FinalEmailType.No}] ")
|
Logger.Warn($"No SendFinalEmailToCreator - oMailToCreator [{oMailToCreator}] <> [{FinalEmailType.No}] ")
|
||||||
End If
|
End If
|
||||||
|
|
||||||
If oMailToReceivers <> FinalEmailType.No Then
|
If oMailToReceivers <> FinalEmailType.No And pEnvelope.IsReadAndSign() Then
|
||||||
Logger.Debug("Sending emails to receivers..")
|
Logger.Debug("Sending emails to receivers..")
|
||||||
SendFinalEmailToReceivers(pEnvelope) ', pAttachment
|
SendFinalEmailToReceivers(pEnvelope) ', pAttachment
|
||||||
Else
|
Else
|
||||||
|
|||||||
145
EnvelopeGenerator.DependencyInjection/DependencyInjection.cs
Normal file
145
EnvelopeGenerator.DependencyInjection/DependencyInjection.cs
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using EnvelopeGenerator.Application;
|
||||||
|
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||||
|
using EnvelopeGenerator.Application.Services;
|
||||||
|
using EnvelopeGenerator.Infrastructure;
|
||||||
|
using DigitalData.EmailProfilerDispatcher;
|
||||||
|
using DigitalData.UserManager.DependencyInjection;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.DependencyInjection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Controls which optional services are registered by <see cref="DependencyInjection.AddEnvelopeGenerator"/>.
|
||||||
|
/// All flags default to <c>true</c>. Set a flag to <c>false</c> if the consuming project
|
||||||
|
/// already registers that service itself or simply does not need it.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class EnvelopeGeneratorOptions
|
||||||
|
{
|
||||||
|
/// <summary>Calls <c>AddHttpContextAccessor()</c>. Default: <c>true</c>.</summary>
|
||||||
|
public bool AddHttpContextAccessor { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calls <c>AddDistributedSqlServerCache()</c> with the supplied <see cref="SqlCacheOptions"/>.
|
||||||
|
/// Requires <see cref="SqlCacheOptions"/> to be configured. Default: <c>true</c>.
|
||||||
|
/// </summary>
|
||||||
|
public bool AddDistributedSqlServerCache { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Options for the distributed SQL Server cache.
|
||||||
|
/// Required when <see cref="AddDistributedSqlServerCache"/> is <c>true</c>.
|
||||||
|
/// </summary>
|
||||||
|
public SqlCacheOptions? SqlCacheOptions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Calls <c>AddDispatcher<TDbContext>()</c>. Default: <c>true</c>.</summary>
|
||||||
|
public bool AddDispatcher { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>Calls <c>AddMemoryCache()</c>. Default: <c>true</c>.</summary>
|
||||||
|
public bool AddMemoryCache { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>Calls <c>AddUserManager<TDbContext>()</c>. Default: <c>true</c>.</summary>
|
||||||
|
public bool AddUserManager { get; set; } = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Options for <c>AddDistributedSqlServerCache</c>.</summary>
|
||||||
|
public sealed class SqlCacheOptions
|
||||||
|
{
|
||||||
|
/// <summary>SQL Server connection string.</summary>
|
||||||
|
public string ConnectionString { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>Schema name. Default: <c>dbo</c>.</summary>
|
||||||
|
public string SchemaName { get; set; } = "dbo";
|
||||||
|
|
||||||
|
/// <summary>Table name. Default: <c>TBDD_CACHE</c>.</summary>
|
||||||
|
public string TableName { get; set; } = "TBDD_CACHE";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for registering EnvelopeGenerator services into an <see cref="IServiceCollection"/>.
|
||||||
|
/// Use <see cref="AddEnvelopeGenerator{TDbContext}"/> as the single entry-point for projects that need both the
|
||||||
|
/// application layer (MediatR, AutoMapper, CRUD services, configuration sections) and the infrastructure
|
||||||
|
/// layer (repositories, DbContext, SQL executors).
|
||||||
|
/// For projects that do not need a database (e.g. lightweight API gateways or unit-test hosts), use
|
||||||
|
/// <see cref="AddEnvelopeGeneratorCore"/> to register only the application layer.
|
||||||
|
/// </summary>
|
||||||
|
public static class DependencyInjection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Registers the full EnvelopeGenerator stack using <see cref="EGDbContext"/> as the DbContext type.
|
||||||
|
/// </summary>
|
||||||
|
public static IServiceCollection AddEnvelopeGenerator(
|
||||||
|
this IServiceCollection services,
|
||||||
|
IConfiguration configuration,
|
||||||
|
Action<EnvelopeGenerator.Infrastructure.DependencyInjection.Config>? infrastructureOptions = null,
|
||||||
|
Action<EnvelopeGeneratorOptions>? options = null)
|
||||||
|
{
|
||||||
|
var opt = new EnvelopeGeneratorOptions();
|
||||||
|
options?.Invoke(opt);
|
||||||
|
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
// Application layer: CRUD services, MediatR, AutoMapper, configuration sections.
|
||||||
|
services.AddEnvelopeGeneratorServices(configuration);
|
||||||
|
|
||||||
|
// Infrastructure layer: repositories, DbContext, Dapper type maps, SQL executors.
|
||||||
|
services.AddEnvelopeGeneratorInfrastructureServices(cfg =>
|
||||||
|
{
|
||||||
|
infrastructureOptions?.Invoke(cfg);
|
||||||
|
});
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
|
||||||
|
if (opt.AddHttpContextAccessor)
|
||||||
|
services.AddHttpContextAccessor();
|
||||||
|
|
||||||
|
if (opt.AddDistributedSqlServerCache && opt.SqlCacheOptions is { } cacheOpts)
|
||||||
|
services.AddDistributedSqlServerCache(o =>
|
||||||
|
{
|
||||||
|
o.ConnectionString = cacheOpts.ConnectionString;
|
||||||
|
o.SchemaName = cacheOpts.SchemaName;
|
||||||
|
o.TableName = cacheOpts.TableName;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (opt.AddDispatcher)
|
||||||
|
services.AddDispatcher<EGDbContext>();
|
||||||
|
|
||||||
|
if (opt.AddMemoryCache)
|
||||||
|
services.AddMemoryCache();
|
||||||
|
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
if (opt.AddUserManager)
|
||||||
|
services.AddUserManager<EGDbContext>();
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers only the <em>application</em> layer services (MediatR handlers, AutoMapper profiles,
|
||||||
|
/// CRUD services, configuration sections) without any infrastructure / database dependencies.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">Service collection to register services into.</param>
|
||||||
|
/// <param name="configuration">Application configuration used to bind application-level option sections.</param>
|
||||||
|
/// <returns>The updated <see cref="IServiceCollection"/>.</returns>
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
public static IServiceCollection AddEnvelopeGeneratorCore(
|
||||||
|
this IServiceCollection services,
|
||||||
|
IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddEnvelopeGeneratorServices(configuration);
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers <see cref="EnvelopeMailService"/> as the <see cref="IEnvelopeMailService"/> scoped
|
||||||
|
/// implementation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">Service collection to register services into.</param>
|
||||||
|
/// <returns>The updated <see cref="IServiceCollection"/>.</returns>
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
public static IServiceCollection AddEnvelopeMailService(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddScoped<IEnvelopeMailService, EnvelopeMailService>();
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
|
<!-- NuGet package metadata -->
|
||||||
|
<PackageId>EnvelopeGenerator</PackageId>
|
||||||
|
<Authors>Digital Data GmbH</Authors>
|
||||||
|
<Company>Digital Data GmbH</Company>
|
||||||
|
<Product>EnvelopeGenerator</Product>
|
||||||
|
<Description>
|
||||||
|
Envelope Generator ist eine Bibliothek zur Verwaltung und Verarbeitung digitaler Umschläge (Envelopes).
|
||||||
|
Dieses Paket enthält die Dependency-Injection-Erweiterungsmethoden und bündelt die Application-
|
||||||
|
sowie Infrastructure-Schicht in einer einzigen NuGet-Referenz.
|
||||||
|
</Description>
|
||||||
|
<Copyright>Copyright 2024 Digital Data GmbH</Copyright>
|
||||||
|
<RepositoryUrl>http://git.dd:3000/AppStd/EnvelopeGenerator.git</RepositoryUrl>
|
||||||
|
<PackageTags>digital data envelope generator di dependency injection</PackageTags>
|
||||||
|
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||||
|
<Version>1.2.0.3</Version>
|
||||||
|
<AssemblyVersion>1.2.0.3</AssemblyVersion>
|
||||||
|
<FileVersion>1.2.0.3</FileVersion>
|
||||||
|
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- ASP.NET Core shared framework (AddHttpContextAccessor, AddMemoryCache, etc.) -->
|
||||||
|
<ItemGroup>
|
||||||
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
All dependencies are declared here.
|
||||||
|
Because Application and Infrastructure DLLs are bundled via PrivateAssets=all,
|
||||||
|
their PackageReferences have been moved to this project so that the correct
|
||||||
|
dependencies are written to the nuspec and a consuming project works with
|
||||||
|
a single package install.
|
||||||
|
-->
|
||||||
|
<ItemGroup>
|
||||||
|
<!-- DigitalData BaGet packages -->
|
||||||
|
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.6.0" />
|
||||||
|
<PackageReference Include="DigitalData.Core.Application" Version="3.4.0" />
|
||||||
|
<PackageReference Include="DigitalData.Core.Client" Version="2.1.0" />
|
||||||
|
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" />
|
||||||
|
<PackageReference Include="DigitalData.Core.Infrastructure" Version="2.6.1" />
|
||||||
|
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="3.1.1" />
|
||||||
|
<PackageReference Include="UserManager" Version="1.1.3" />
|
||||||
|
|
||||||
|
<!-- ORM / Database -->
|
||||||
|
<PackageReference Include="Dapper" Version="2.1.66" />
|
||||||
|
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.2" />
|
||||||
|
|
||||||
|
<!-- Application services -->
|
||||||
|
<PackageReference Include="MediatR" Version="12.5.0" />
|
||||||
|
<PackageReference Include="QRCoder" Version="1.6.0" />
|
||||||
|
<PackageReference Include="QRCoder-ImageSharp" Version="0.10.0" />
|
||||||
|
<PackageReference Include="QuestPDF" Version="2025.7.1" />
|
||||||
|
<PackageReference Include="Otp.NET" Version="1.4.0" />
|
||||||
|
|
||||||
|
<!-- Security / Identity -->
|
||||||
|
<PackageReference Include="Microsoft.Identity.Client" Version="4.82.1" />
|
||||||
|
|
||||||
|
<!-- Utilities -->
|
||||||
|
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
|
||||||
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
|
||||||
|
<PackageReference Include="System.Formats.Asn1" Version="10.0.3" />
|
||||||
|
<PackageReference Include="System.Security.AccessControl" Version="6.0.1" />
|
||||||
|
|
||||||
|
<!-- DI abstractions -->
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.6" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- TFM-specific packages -->
|
||||||
|
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
|
||||||
|
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||||
|
<PackageReference Include="CommandDotNet" Version="7.0.5" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.20" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="7.0.20" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.20" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.20" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.20" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
|
||||||
|
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||||
|
<PackageReference Include="CommandDotNet" Version="8.1.1" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.17" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="8.0.17" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.17" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.17" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.17" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
|
||||||
|
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||||
|
<PackageReference Include="CommandDotNet" Version="8.1.1" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="9.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.6" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Bundle dependency DLLs into the package lib folder -->
|
||||||
|
<Target Name="IncludeDependencyDlls" BeforeTargets="_GetPackageFiles">
|
||||||
|
<ItemGroup>
|
||||||
|
<_DepDlls Include="
 ..\EnvelopeGenerator.Application\bin\$(Configuration)\%(ProjectReference.TargetFramework)\EnvelopeGenerator.Application.dll;
 ..\EnvelopeGenerator.Domain\bin\$(Configuration)\%(ProjectReference.TargetFramework)\EnvelopeGenerator.Domain.dll;
 ..\EnvelopeGenerator.Infrastructure\bin\$(Configuration)\%(ProjectReference.TargetFramework)\EnvelopeGenerator.Infrastructure.dll" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Rebuild all dependency projects for every TFM before packing so that
|
||||||
|
the DLLs bundled into the package are always up-to-date.
|
||||||
|
Run with: dotnet pack -c Release
|
||||||
|
-->
|
||||||
|
<Target Name="RebuildDependenciesBeforePack" BeforeTargets="GenerateNuspec">
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net7.0" />
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net8.0" />
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net9.0" />
|
||||||
|
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net7.0" />
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net8.0" />
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net9.0" />
|
||||||
|
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net7.0" />
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net8.0" />
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net9.0" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="BundleReferencedDlls" AfterTargets="Build">
|
||||||
|
<ItemGroup>
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Application\bin\$(Configuration)\net7.0\EnvelopeGenerator.Application.dll" TargetFramework="net7.0" />
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Application\bin\$(Configuration)\net8.0\EnvelopeGenerator.Application.dll" TargetFramework="net8.0" />
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Application\bin\$(Configuration)\net9.0\EnvelopeGenerator.Application.dll" TargetFramework="net9.0" />
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Domain\bin\$(Configuration)\net7.0\EnvelopeGenerator.Domain.dll" TargetFramework="net7.0" />
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Domain\bin\$(Configuration)\net8.0\EnvelopeGenerator.Domain.dll" TargetFramework="net8.0" />
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Domain\bin\$(Configuration)\net9.0\EnvelopeGenerator.Domain.dll" TargetFramework="net9.0" />
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Infrastructure\bin\$(Configuration)\net7.0\EnvelopeGenerator.Infrastructure.dll" TargetFramework="net7.0" />
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Infrastructure\bin\$(Configuration)\net8.0\EnvelopeGenerator.Infrastructure.dll" TargetFramework="net8.0" />
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Infrastructure\bin\$(Configuration)\net9.0\EnvelopeGenerator.Infrastructure.dll" TargetFramework="net9.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -47,6 +47,8 @@ public abstract class EGDbContextBase : DbContext
|
|||||||
|
|
||||||
public DbSet<Signature> DocumentReceiverElements { get; set; }
|
public DbSet<Signature> DocumentReceiverElements { get; set; }
|
||||||
|
|
||||||
|
public DbSet<ElementAnnotation> DocumentReceiverElementAnnotations { get; set; }
|
||||||
|
|
||||||
public DbSet<DocumentStatus> DocumentStatus { get; set; }
|
public DbSet<DocumentStatus> DocumentStatus { get; set; }
|
||||||
|
|
||||||
public DbSet<EmailTemplate> EmailTemplate { get; set; }
|
public DbSet<EmailTemplate> EmailTemplate { get; set; }
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ public static class Extensions
|
|||||||
Title = faker.Lorem.Paragraph(faker.Random.Number(1, 2)),
|
Title = faker.Lorem.Paragraph(faker.Random.Number(1, 2)),
|
||||||
Message = faker.Lorem.Paragraph(faker.Random.Number(2, 5)),
|
Message = faker.Lorem.Paragraph(faker.Random.Number(2, 5)),
|
||||||
TfaEnabled = tfaEnabled,
|
TfaEnabled = tfaEnabled,
|
||||||
AddedWhen = DateTime.UtcNow,
|
AddedWhen = DateTime.Now,
|
||||||
CertificationType = (int)CertificationType.AdvancedElectronicSignature,
|
CertificationType = (int)CertificationType.AdvancedElectronicSignature,
|
||||||
UseAccessCode = false,
|
UseAccessCode = false,
|
||||||
ContractType = (int)ContractType.Contract,
|
ContractType = (int)ContractType.Contract,
|
||||||
@@ -273,9 +273,9 @@ public static class Extensions
|
|||||||
EnvelopeId = envelopeId,
|
EnvelopeId = envelopeId,
|
||||||
ReceiverId = receiverId,
|
ReceiverId = receiverId,
|
||||||
Status = ReceiverStatus.Unsigned,
|
Status = ReceiverStatus.Unsigned,
|
||||||
AddedWhen = DateTime.UtcNow,
|
AddedWhen = DateTime.Now,
|
||||||
AccessCode = faker.Random.Number(1000, 9999).ToString(),
|
AccessCode = faker.Random.Number(1000, 9999).ToString(),
|
||||||
ChangedWhen = DateTime.UtcNow,
|
ChangedWhen = DateTime.Now,
|
||||||
CompanyName = faker.Company.CompanyName(),
|
CompanyName = faker.Company.CompanyName(),
|
||||||
JobTitle = faker.Name.JobTitle(),
|
JobTitle = faker.Name.JobTitle(),
|
||||||
Name = faker.Name.FullName(),
|
Name = faker.Name.FullName(),
|
||||||
|
|||||||
@@ -438,4 +438,10 @@ public class EnvelopeController : ViewControllerBase
|
|||||||
return this.ViewDocumentNotFound();
|
return this.ViewDocumentNotFound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("EnvelopeReceiverWithSecretByMediatr")]
|
||||||
|
public async Task<IActionResult> EnvelopeReceiverWithSecretByMediatr([FromQuery] ReadEnvelopeReceiverSecretQuery q, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
return await _mediator.Send(q, cancel) is EnvelopeReceiverSecretDto dto ? Ok(dto) : NotFound();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -12,9 +12,9 @@
|
|||||||
<PackageTags>digital data envelope generator web</PackageTags>
|
<PackageTags>digital data envelope generator web</PackageTags>
|
||||||
<Description>EnvelopeGenerator.Web is an ASP.NET MVC application developed to manage signing processes. It uses Entity Framework Core (EF Core) for database operations. The user interface for signing processes is developed with Razor View Engine (.cshtml files) and JavaScript under wwwroot, integrated with PSPDFKit. This integration allows users to view and sign documents seamlessly.</Description>
|
<Description>EnvelopeGenerator.Web is an ASP.NET MVC application developed to manage signing processes. It uses Entity Framework Core (EF Core) for database operations. The user interface for signing processes is developed with Razor View Engine (.cshtml files) and JavaScript under wwwroot, integrated with PSPDFKit. This integration allows users to view and sign documents seamlessly.</Description>
|
||||||
<ApplicationIcon>Assets\icon.ico</ApplicationIcon>
|
<ApplicationIcon>Assets\icon.ico</ApplicationIcon>
|
||||||
<Version>3.12.2</Version> <!-- NuGet package version -->
|
<Version>3.12.3</Version> <!-- NuGet package version -->
|
||||||
<AssemblyVersion>3.12.2.0</AssemblyVersion> <!-- Assembly version for API compatibility -->
|
<AssemblyVersion>3.12.3.0</AssemblyVersion> <!-- Assembly version for API compatibility -->
|
||||||
<FileVersion>3.12.2.0</FileVersion> <!-- Windows file version -->
|
<FileVersion>3.12.3.0</FileVersion> <!-- Windows file version -->
|
||||||
<Copyright>Copyright © 2025 Digital Data GmbH. All rights reserved.</Copyright>
|
<Copyright>Copyright © 2025 Digital Data GmbH. All rights reserved.</Copyright>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
@@ -2175,6 +2175,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
|
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
|
||||||
|
<ProjectReference Include="..\EnvelopeGenerator.DependencyInjection\EnvelopeGenerator.DependencyInjection.csproj" />
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
|
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.PdfEditor\EnvelopeGenerator.PdfEditor.csproj" />
|
<ProjectReference Include="..\EnvelopeGenerator.PdfEditor\EnvelopeGenerator.PdfEditor.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using EnvelopeGenerator.Application.Services;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NLog;
|
using NLog;
|
||||||
using Quartz;
|
using Quartz;
|
||||||
@@ -9,14 +8,12 @@ using EnvelopeGenerator.Web.Models;
|
|||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using Ganss.Xss;
|
using Ganss.Xss;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using EnvelopeGenerator.Application;
|
|
||||||
using DigitalData.EmailProfilerDispatcher;
|
using DigitalData.EmailProfilerDispatcher;
|
||||||
using EnvelopeGenerator.Infrastructure;
|
using EnvelopeGenerator.Infrastructure;
|
||||||
using EnvelopeGenerator.Web.Sanitizers;
|
using EnvelopeGenerator.Web.Sanitizers;
|
||||||
using EnvelopeGenerator.Web.Models.Annotation;
|
using EnvelopeGenerator.Web.Models.Annotation;
|
||||||
using DigitalData.UserManager.DependencyInjection;
|
|
||||||
using EnvelopeGenerator.Web.Middleware;
|
using EnvelopeGenerator.Web.Middleware;
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
using EnvelopeGenerator.DependencyInjection;
|
||||||
using EnvelopeGenerator.Web;
|
using EnvelopeGenerator.Web;
|
||||||
|
|
||||||
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
||||||
@@ -57,8 +54,6 @@ try
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddHttpContextAccessor();
|
|
||||||
|
|
||||||
builder.ConfigureBySection<TFARegParams>();
|
builder.ConfigureBySection<TFARegParams>();
|
||||||
|
|
||||||
// Add controllers and razor views
|
// Add controllers and razor views
|
||||||
@@ -95,17 +90,9 @@ try
|
|||||||
var connStr = config.GetConnectionString(cnnStrName)
|
var connStr = config.GetConnectionString(cnnStrName)
|
||||||
?? throw new InvalidOperationException($"Connection string '{cnnStrName}' is missing in the application configuration.");
|
?? throw new InvalidOperationException($"Connection string '{cnnStrName}' is missing in the application configuration.");
|
||||||
|
|
||||||
builder.Services.AddDistributedSqlServerCache(options =>
|
|
||||||
{
|
|
||||||
options.ConnectionString = connStr;
|
|
||||||
options.SchemaName = "dbo";
|
|
||||||
options.TableName = "TBDD_CACHE";
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add envelope generator services
|
// Add envelope generator services
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
builder.Services.AddEnvelopeGenerator(config,
|
||||||
builder.Services.AddEnvelopeGeneratorInfrastructureServices(
|
infrastructureOptions: opt =>
|
||||||
opt =>
|
|
||||||
{
|
{
|
||||||
opt.AddDbTriggerParams(config);
|
opt.AddDbTriggerParams(config);
|
||||||
opt.AddDbContext((provider, options) =>
|
opt.AddDbContext((provider, options) =>
|
||||||
@@ -116,12 +103,16 @@ try
|
|||||||
.EnableSensitiveDataLogging()
|
.EnableSensitiveDataLogging()
|
||||||
.EnableDetailedErrors();
|
.EnableDetailedErrors();
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
options: opt =>
|
||||||
|
{
|
||||||
|
opt.SqlCacheOptions = new()
|
||||||
|
{
|
||||||
|
ConnectionString = connStr,
|
||||||
|
SchemaName = "dbo",
|
||||||
|
TableName = "TBDD_CACHE"
|
||||||
|
};
|
||||||
});
|
});
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
|
||||||
|
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
|
||||||
builder.Services.AddEnvelopeGeneratorServices(config);
|
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
|
||||||
|
|
||||||
builder.Services.Configure<CookiePolicyOptions>(options =>
|
builder.Services.Configure<CookiePolicyOptions>(options =>
|
||||||
{
|
{
|
||||||
@@ -169,22 +160,12 @@ try
|
|||||||
builder.Services.AddSingleton(sp => sp.GetRequiredService<IOptions<MultiCulture>>().Value);
|
builder.Services.AddSingleton(sp => sp.GetRequiredService<IOptions<MultiCulture>>().Value);
|
||||||
|
|
||||||
// Register mail services
|
// Register mail services
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
builder.Services.AddEnvelopeMailService();
|
||||||
builder.Services.AddScoped<IEnvelopeMailService, EnvelopeMailService>();
|
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
|
||||||
|
|
||||||
builder.Services.AddDispatcher<EGDbContext>();
|
|
||||||
|
|
||||||
builder.Services.AddMemoryCache();
|
|
||||||
|
|
||||||
builder.ConfigureBySection<CustomImages>();
|
builder.ConfigureBySection<CustomImages>();
|
||||||
|
|
||||||
builder.ConfigureBySection<AnnotationParams>();
|
builder.ConfigureBySection<AnnotationParams>();
|
||||||
|
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
|
||||||
builder.Services.AddUserManager<EGDbContext>();
|
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
app.UseMiddleware<ExceptionHandlingMiddleware>();
|
app.UseMiddleware<ExceptionHandlingMiddleware>();
|
||||||
|
|||||||
2
EnvelopeGenerator.Web/wwwroot/js/util.min.js
vendored
2
EnvelopeGenerator.Web/wwwroot/js/util.min.js
vendored
@@ -1 +1 @@
|
|||||||
function detailedCurrentDate(){return new Intl.DateTimeFormat(getCurrentCulture(),{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit",timeZoneName:"shortOffset"}).format()}function findNearest(e,t,o,n){const r=n.reduce(((n,r)=>{const i=Math.sqrt((t(e)-t(r))**2+(o(e)-o(r))**2);return i<n.dist?{dist:i,dest:r}:n}),{dist:1/0,dest:null});return r.dest}const getCurrentCulture=()=>("undefined"!=typeof localized&&localized.culture)?localized.culture:navigator.language||"en-US";const B64ToBuff=e=>new Uint8Array(Array.from(atob(e),e=>e.charCodeAt(0))).buffer;const getLocaleDateString=e=>new Date().toLocaleDateString(getCurrentCulture());
|
function detailedCurrentDate(){return new Intl.DateTimeFormat(getCurrentCulture(),{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit",timeZoneName:"shortOffset"}).format()}function findNearest(n,t,i,r){const u=r=>Math.sqrt((t(n)-t(r))**2+(i(n)-i(r))**2);return r.reduce((n,t)=>{const i=u(t);return i<n.dist?{dist:i,dest:t}:n},{dist:Infinity,dest:null}).dest}const getCurrentCulture=()=>typeof localized!="undefined"&&localized.culture?localized.culture:navigator.language||"en-US",B64ToBuff=n=>new Uint8Array(Array.from(atob(n),n=>n.charCodeAt(0))).buffer,getLocaleDateString=()=>(new Date).toLocaleDateString(getCurrentCulture());
|
||||||
@@ -35,6 +35,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Tests", "
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.API", "EnvelopeGenerator.API\EnvelopeGenerator.API.csproj", "{EC768913-6270-14F4-1DD3-69C87A659462}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.API", "EnvelopeGenerator.API\EnvelopeGenerator.API.csproj", "{EC768913-6270-14F4-1DD3-69C87A659462}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.DependencyInjection", "EnvelopeGenerator.DependencyInjection\EnvelopeGenerator.DependencyInjection.csproj", "{90FE0312-8C38-4347-9EA2-0A719E255D5C}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -85,6 +87,10 @@ Global
|
|||||||
{EC768913-6270-14F4-1DD3-69C87A659462}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{EC768913-6270-14F4-1DD3-69C87A659462}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{EC768913-6270-14F4-1DD3-69C87A659462}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{EC768913-6270-14F4-1DD3-69C87A659462}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{EC768913-6270-14F4-1DD3-69C87A659462}.Release|Any CPU.Build.0 = Release|Any CPU
|
{EC768913-6270-14F4-1DD3-69C87A659462}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{90FE0312-8C38-4347-9EA2-0A719E255D5C}.Debug|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{90FE0312-8C38-4347-9EA2-0A719E255D5C}.Debug|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{90FE0312-8C38-4347-9EA2-0A719E255D5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{90FE0312-8C38-4347-9EA2-0A719E255D5C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -104,6 +110,7 @@ Global
|
|||||||
{211619F5-AE25-4BA5-A552-BACAFE0632D3} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
|
{211619F5-AE25-4BA5-A552-BACAFE0632D3} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
|
||||||
{224C4845-1CDE-22B7-F3A9-1FF9297F70E8} = {0CBC2432-A561-4440-89BC-671B66A24146}
|
{224C4845-1CDE-22B7-F3A9-1FF9297F70E8} = {0CBC2432-A561-4440-89BC-671B66A24146}
|
||||||
{EC768913-6270-14F4-1DD3-69C87A659462} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
|
{EC768913-6270-14F4-1DD3-69C87A659462} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
|
||||||
|
{90FE0312-8C38-4347-9EA2-0A719E255D5C} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {73E60370-756D-45AD-A19A-C40A02DACCC7}
|
SolutionGuid = {73E60370-756D-45AD-A19A-C40A02DACCC7}
|
||||||
|
|||||||
Reference in New Issue
Block a user