V1.0.5
新增用户重置密码和修改密码
This commit is contained in:
parent
57402e0dda
commit
a23984c9f5
78
LMS.Common/Password/RandomNumberGenerator.cs
Normal file
78
LMS.Common/Password/RandomNumberGenerator.cs
Normal file
@ -0,0 +1,78 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
namespace LMS.Common.Password
|
||||
{
|
||||
public static class PasswordGenerator
|
||||
{
|
||||
private const string LowercaseChars = "abcdefghijklmnopqrstuvwxyz";
|
||||
private const string UppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
private const string NumberChars = "0123456789";
|
||||
private const string SpecialChars = "@$!%*?.&";
|
||||
private const string AllChars = LowercaseChars + UppercaseChars + NumberChars + SpecialChars;
|
||||
|
||||
/// <summary>
|
||||
/// 生成指定长度的随机密码
|
||||
/// </summary>
|
||||
/// <param name="length">密码长度</param>
|
||||
/// <returns>随机生成的密码</returns>
|
||||
public static string GeneratePassword(int length)
|
||||
{
|
||||
if (length < 4)
|
||||
{
|
||||
throw new ArgumentException("密码长度必须至少为4个字符", nameof(length));
|
||||
}
|
||||
|
||||
// 使用加密安全的随机数生成器
|
||||
using var rng = RandomNumberGenerator.Create();
|
||||
// 确保密码包含至少一个小写字母、一个大写字母、一个数字和一个特殊字符
|
||||
var password = new StringBuilder();
|
||||
|
||||
// 添加每种类型的至少一个字符
|
||||
password.Append(GetRandomChar(LowercaseChars, rng));
|
||||
password.Append(GetRandomChar(UppercaseChars, rng));
|
||||
password.Append(GetRandomChar(NumberChars, rng));
|
||||
password.Append(GetRandomChar(SpecialChars, rng));
|
||||
|
||||
// 添加剩余的随机字符
|
||||
for (int i = 4; i < length; i++)
|
||||
{
|
||||
password.Append(GetRandomChar(AllChars, rng));
|
||||
}
|
||||
|
||||
// 打乱字符顺序
|
||||
return ShuffleString(password.ToString(), rng);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定字符集中获取一个随机字符
|
||||
/// </summary>
|
||||
private static char GetRandomChar(string chars, RandomNumberGenerator rng)
|
||||
{
|
||||
byte[] data = new byte[1];
|
||||
rng.GetBytes(data);
|
||||
return chars[data[0] % chars.Length];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打乱字符串中字符的顺序
|
||||
/// </summary>
|
||||
private static string ShuffleString(string input, RandomNumberGenerator rng)
|
||||
{
|
||||
char[] array = input.ToCharArray();
|
||||
int n = array.Length;
|
||||
|
||||
while (n > 1)
|
||||
{
|
||||
byte[] box = new byte[1];
|
||||
rng.GetBytes(box);
|
||||
int k = box[0] % n;
|
||||
n--;
|
||||
char temp = array[n];
|
||||
array[n] = array[k];
|
||||
array[k] = temp;
|
||||
}
|
||||
|
||||
return new string(array);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,14 +9,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>LMS Registration Code</h1>
|
||||
<p>Hi there,</p>
|
||||
<p>Your code is <strong>{RegisterCode}</strong></p>
|
||||
<p>The code is valid for 10 minutes, if it is not you, please ignore it.</p>
|
||||
<h1>LMS注册验证码</h1>
|
||||
<p>您好,</p>
|
||||
<p>您的验证码是 <strong>{RegisterCode}</strong></p>
|
||||
<p>该验证码有效期为 10 分钟,如果不是您本人操作,请忽略此邮件。</p>
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
|
||||
public const string ResetPasswordHtmlTemplates = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>LMS重置密码验证码</h1>
|
||||
<p>您好,</p>
|
||||
<p>您的验证码是 <strong>{ResetPasswordCode}</strong></p>
|
||||
<p>该验证码有效期为 10 分钟,如果不是您本人操作,请忽略此邮件。</p>
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
|
||||
public const string ResetpasswordSuccessMail = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>LMS重置密码成功</h1>
|
||||
<p>您好,</p>
|
||||
<p>您的重置密码操作已经成功!</p>
|
||||
<p>您的新密码是 <strong>{NewPassword}</strong></p>
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
|
||||
/// <summary>
|
||||
/// 替换模板的占位符
|
||||
|
||||
@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Net.Mail;
|
||||
using static LMS.Common.Enums.ResponseCodeEnum;
|
||||
|
||||
namespace LMS.service.Controllers
|
||||
@ -142,6 +143,49 @@ namespace LMS.service.Controllers
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region 获取重置密码验证码
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<APIResponseModel<string>>> SendResetPasswordCode([FromBody] EmailVerificationService.SendVerificationCodeDto model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.ParameterError);
|
||||
|
||||
return await _loginService.SendResetPasswordCode(model);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 用户自行重置密码
|
||||
|
||||
[HttpPost("{mail}/{code}")]
|
||||
public async Task<ActionResult<APIResponseModel<string>>> ResetPassword(string mail, string code)
|
||||
{
|
||||
// 校验邮箱是不是有效
|
||||
if (string.IsNullOrWhiteSpace(mail))
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.ParameterError, "无效的邮箱");
|
||||
|
||||
try
|
||||
{
|
||||
// 尝试创建MailAddress实例,如果成功则表示邮箱格式正确
|
||||
MailAddress mailAddress = new(mail);
|
||||
bool isMail = mailAddress.Address == mail;
|
||||
if (!isMail)
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.ParameterError, "无效的邮箱");
|
||||
return await _loginService.UserResetPassword(mail, code);
|
||||
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
// 创建失败表示邮箱格式不正确
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.ParameterError, "无效的邮箱");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 刷新token
|
||||
|
||||
[HttpPost]
|
||||
@ -256,7 +300,7 @@ namespace LMS.service.Controllers
|
||||
|
||||
#endregion
|
||||
|
||||
#region 管理员重置用户密码
|
||||
#region 重置用户密码
|
||||
|
||||
[HttpPost("{id}")]
|
||||
[Authorize]
|
||||
|
||||
@ -23,7 +23,13 @@ public class EmailVerificationService
|
||||
public string Email { get; set; }
|
||||
}
|
||||
|
||||
// 生成并发送验证码
|
||||
#region 注册验证码
|
||||
|
||||
/// <summary>
|
||||
/// 发送注册验证码
|
||||
/// </summary>
|
||||
/// <param name="email"></param>
|
||||
/// <returns></returns>
|
||||
public async Task SendVerificationCodeAsync(string email)
|
||||
{
|
||||
// 生成6位数验证码
|
||||
@ -46,7 +52,12 @@ public class EmailVerificationService
|
||||
await _emailService.SendEmailAsync(email, "LMS注册验证码", emailBody);
|
||||
}
|
||||
|
||||
// 验证用户提交的验证码
|
||||
/// <summary>
|
||||
/// 验证用户提交的验证码
|
||||
/// </summary>
|
||||
/// <param name="email"></param>
|
||||
/// <param name="code"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> VerifyCodeAsync(string email, string code)
|
||||
{
|
||||
var storedCode = await _cache.GetStringAsync($"EmailVerification_{email}");
|
||||
@ -64,8 +75,84 @@ public class EmailVerificationService
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个6位数的验证码
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
|
||||
private string GenerateVerificationCode()
|
||||
{
|
||||
return _random.Next(100000, 999999).ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 重置密码验证码
|
||||
|
||||
/// <summary>
|
||||
/// 发送重置密码验证码
|
||||
/// </summary>
|
||||
/// <param name="email"></param>
|
||||
/// <returns></returns>
|
||||
public async Task SendResetPasswordCodeAsync(string email)
|
||||
{
|
||||
// 生成6位数验证码
|
||||
string resetPasswordCode = GenerateVerificationCode();
|
||||
// 将验证码保存到分布式缓存,设置10分钟过期
|
||||
var options = new DistributedCacheEntryOptions
|
||||
{
|
||||
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10)
|
||||
};
|
||||
await _cache.SetStringAsync($"ResetPassword_{email}", resetPasswordCode, options);
|
||||
var emailBody = EmailTemplateService.ReplaceTemplate(EmailTemplateService.ResetPasswordHtmlTemplates, new Dictionary<string, string>
|
||||
{
|
||||
{ "ResetPasswordCode", resetPasswordCode }
|
||||
});
|
||||
// 发送验证码邮件
|
||||
await _emailService.SendEmailAsync(email, "LMS重置密码验证码", emailBody);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 严重用户提交成的重置密码验证码
|
||||
/// </summary>
|
||||
/// <param name="email"></param>
|
||||
/// <param name="code"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> VerifyResetPasswordAsync(string email, string code)
|
||||
{
|
||||
var storedCode = await _cache.GetStringAsync($"ResetPassword_{email}");
|
||||
|
||||
if (string.IsNullOrEmpty(storedCode))
|
||||
return false;
|
||||
|
||||
// 使用完就删除验证码
|
||||
if (storedCode == code)
|
||||
{
|
||||
await _cache.RemoveAsync($"ResetPassword_{email}");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region 重置密码成功邮件
|
||||
|
||||
/// <summary>
|
||||
/// 发送重置密码验证码
|
||||
/// </summary>
|
||||
/// <param name="email"></param>
|
||||
/// <returns></returns>
|
||||
public async Task SendResetpasswordSuccessMail(string email, string newPassword)
|
||||
{
|
||||
var emailBody = EmailTemplateService.ReplaceTemplate(EmailTemplateService.ResetpasswordSuccessMail, new Dictionary<string, string>
|
||||
{
|
||||
{ "NewPassword", newPassword }
|
||||
});
|
||||
// 发送验证码邮件
|
||||
await _emailService.SendEmailAsync(email, "LMS重置密码成功", emailBody);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -391,8 +391,11 @@ namespace LMS.service.Service
|
||||
query = query.Where(x => filteredOwnerIds.Contains(x.UserID));
|
||||
}
|
||||
|
||||
if (isSuperAdmin)
|
||||
{
|
||||
|
||||
if (isAdmin && !isSuperAdmin)
|
||||
}
|
||||
else if (isAdmin)
|
||||
{
|
||||
// 除了超级管理员的代理 其他都能看到
|
||||
IList<User> superUsers = await _userManager.GetUsersInRoleAsync("Super Admin");
|
||||
@ -408,7 +411,7 @@ namespace LMS.service.Service
|
||||
query = query.Where(x => !filteredParentIds.Contains(x.UserID) || x.UserID == requestUserId);
|
||||
}
|
||||
}
|
||||
else if (isAgent && !isSuperAdmin)
|
||||
else if (isAgent)
|
||||
{
|
||||
// 代理只能看到自己下面的用户
|
||||
var list = await userLookupQuery
|
||||
@ -417,10 +420,18 @@ namespace LMS.service.Service
|
||||
.ToListAsync();
|
||||
|
||||
HashSet<long> filteredParentIds = new(list);
|
||||
if (filteredParentIds?.Count > 0)
|
||||
if (filteredParentIds != null)
|
||||
{
|
||||
query = query.Where(x => filteredParentIds.Contains(x.UserID) || x.UserID == requestUserId);
|
||||
}
|
||||
else
|
||||
{
|
||||
query = query.Where(x => x.UserID == user.Id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
query = query.Where(x => x.UserID == user.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -291,7 +291,11 @@ namespace LMS.service.Service.SoftwareService
|
||||
// 做筛选权限
|
||||
// 获取相关用户ID
|
||||
var userLookupQuery = _userManager.Users.AsNoTracking();
|
||||
if (isAdmin && !isSuperAdmin)
|
||||
if (isSuperAdmin)
|
||||
{
|
||||
|
||||
}
|
||||
else if (isAdmin)
|
||||
{
|
||||
// 除了超级管理员的代理 其他都能看到
|
||||
IList<User> superUsers = await _userManager.GetUsersInRoleAsync("Super Admin");
|
||||
@ -307,7 +311,7 @@ namespace LMS.service.Service.SoftwareService
|
||||
query = query.Where(x => !filteredParentIds.Contains(x.UserId) || x.UserId == requestUserId);
|
||||
}
|
||||
}
|
||||
else if (isAgent && !isSuperAdmin)
|
||||
else if (isAgent)
|
||||
{
|
||||
// 代理只能看到自己下面的用户
|
||||
var list = await userLookupQuery
|
||||
@ -316,12 +320,16 @@ namespace LMS.service.Service.SoftwareService
|
||||
.ToListAsync();
|
||||
|
||||
HashSet<long> filteredParentIds = new(list);
|
||||
if (filteredParentIds?.Count > 0)
|
||||
if (filteredParentIds != null)
|
||||
{
|
||||
query = query.Where(x => filteredParentIds.Contains(x.UserId));
|
||||
}
|
||||
else
|
||||
{
|
||||
query = query.Where(x => x.UserId == requestUserId);
|
||||
}
|
||||
else if (!isSuperAdmin)
|
||||
}
|
||||
else
|
||||
{
|
||||
// 普通用户只能看到自己的
|
||||
query = query.Where(x => x.UserId == requestUserId);
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
|
||||
using LMS.Common.Enum;
|
||||
using LMS.Common.Password;
|
||||
using LMS.Common.RSAKey;
|
||||
using LMS.DAO;
|
||||
using LMS.DAO.UserDAO;
|
||||
@ -15,16 +17,18 @@ using System.Collections.Concurrent;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using static Betalgo.Ranul.OpenAI.ObjectModels.SharedModels.IOpenAIModels;
|
||||
using static LMS.Common.Enums.ResponseCodeEnum;
|
||||
|
||||
namespace LMS.service.Service.UserService
|
||||
{
|
||||
public class LoginService(UserManager<User> userManager, ApplicationDbContext context, SecurityService securityService, UserBasicDao userBasicDao)
|
||||
public class LoginService(UserManager<User> userManager, ApplicationDbContext context, SecurityService securityService, UserBasicDao userBasicDao, EmailVerificationService emailVerificationService)
|
||||
{
|
||||
private readonly UserManager<User> _userManager = userManager;
|
||||
private readonly ApplicationDbContext _context = context;
|
||||
private readonly SecurityService _securityService = securityService;
|
||||
private readonly UserBasicDao _userBasicDao = userBasicDao;
|
||||
private readonly EmailVerificationService _verificationService = emailVerificationService;
|
||||
|
||||
#region 生成JWT
|
||||
/// <summary>
|
||||
@ -417,7 +421,8 @@ namespace LMS.service.Service.UserService
|
||||
}
|
||||
// 检查当前用户是不是超级管理员
|
||||
bool isSuperAdmin = await _userBasicDao.CheckUserIsSuperAdmin(requestUserId);
|
||||
if (!isSuperAdmin)
|
||||
// 不是超级管理员,不能重置其他用户的密码
|
||||
if (!isSuperAdmin && id != requestUserId)
|
||||
{
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.NotPermissionAction);
|
||||
}
|
||||
@ -458,5 +463,97 @@ namespace LMS.service.Service.UserService
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 发送重置密码验证码
|
||||
internal async Task<ActionResult<APIResponseModel<string>>> SendResetPasswordCode(EmailVerificationService.SendVerificationCodeDto model)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查邮箱是否已被使用
|
||||
var existingUser = await _userManager.FindByEmailAsync(model.Email);
|
||||
if (existingUser == null)
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.ParameterError, "当前邮箱未注册,请先注册!");
|
||||
|
||||
Options? enaleMailService = await _context.Options.FirstOrDefaultAsync(x => x.Key == OptionKeyName.EnableMailService);
|
||||
if (enaleMailService != null)
|
||||
{
|
||||
_ = bool.TryParse(enaleMailService.Value, out bool enableMail);
|
||||
if (!enableMail)
|
||||
{
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.SystemError, "邮件服务未开启,无法重置密码!");
|
||||
}
|
||||
}
|
||||
|
||||
// 发送验证码
|
||||
await _verificationService.SendResetPasswordCodeAsync(model.Email);
|
||||
return APIResponseModel<string>.CreateSuccessResponseModel(ResponseCode.Success, "验证码发送成功,请在邮箱中查收!");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.SystemError, e.Message);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region 用户自行重置密码
|
||||
public async Task<ActionResult<APIResponseModel<string>>> UserResetPassword(string mail, string code)
|
||||
{
|
||||
using var transaction = await _context.Database.BeginTransactionAsync();
|
||||
try
|
||||
{
|
||||
// 检查邮箱是否已被使用
|
||||
var existingUser = await _userManager.FindByEmailAsync(mail);
|
||||
if (existingUser == null)
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.ParameterError, "当前邮箱未注册,请先注册!");
|
||||
// 校验验证码
|
||||
Options? enaleMailService = await _context.Options.FirstOrDefaultAsync(x => x.Key == OptionKeyName.EnableMailService);
|
||||
if (enaleMailService != null)
|
||||
{
|
||||
_ = bool.TryParse(enaleMailService.Value, out bool enableMail);
|
||||
if (!enableMail)
|
||||
{
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.SystemError, "邮件服务未开启,无法重置密码!");
|
||||
}
|
||||
}
|
||||
|
||||
var isCodeValid = await _verificationService.VerifyResetPasswordAsync(mail, code);
|
||||
if (!isCodeValid)
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.ParameterError, "验证码无效或已过期");
|
||||
|
||||
// 生成一个密码
|
||||
string randomPassword = PasswordGenerator.GeneratePassword(12);
|
||||
|
||||
// 移除用户当前密码(如果用户没有密码,则需要跳过此步骤)
|
||||
var hasPassword = await _userManager.HasPasswordAsync(existingUser);
|
||||
if (hasPassword)
|
||||
{
|
||||
var removePasswordResult = await _userManager.RemovePasswordAsync(existingUser);
|
||||
if (!removePasswordResult.Succeeded)
|
||||
{
|
||||
var errors = string.Join("; ", removePasswordResult.Errors.Select(e => e.Description));
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.SystemError, $"移除旧密码失败:{errors}");
|
||||
}
|
||||
}
|
||||
|
||||
// 为用户设置新密码
|
||||
var addPasswordResult = await _userManager.AddPasswordAsync(existingUser, randomPassword);
|
||||
if (!addPasswordResult.Succeeded)
|
||||
{
|
||||
var errors = string.Join("; ", addPasswordResult.Errors.Select(e => e.Description));
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.SystemError, $"重置密码失败:{errors}");
|
||||
}
|
||||
await transaction.CommitAsync();
|
||||
|
||||
// 重置密码成功,把新密码发送到邮箱
|
||||
await _verificationService.SendResetpasswordSuccessMail(mail, randomPassword);
|
||||
|
||||
// 重置密码
|
||||
return APIResponseModel<string>.CreateSuccessResponseModel(ResponseCode.Success, "密码重置成功,请去邮箱查看重置密码!");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.SystemError, e.Message);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,7 +139,11 @@ namespace LMS.service.Service.UserService
|
||||
|
||||
// 获取相关用户ID
|
||||
var userLookupQuery = _userManager.Users.AsNoTracking();
|
||||
if (isAdmin && !isSuperAdmin)
|
||||
if (isSuperAdmin)
|
||||
{
|
||||
|
||||
}
|
||||
else if (isAdmin)
|
||||
{
|
||||
// 除了超级管理员的代理 其他都能看到
|
||||
IList<User> superUsers = await _userManager.GetUsersInRoleAsync("Super Admin");
|
||||
@ -155,7 +159,7 @@ namespace LMS.service.Service.UserService
|
||||
query = query.Where(x => !filteredParentIds.Contains(x.Id));
|
||||
}
|
||||
}
|
||||
else if (isAgent && !isSuperAdmin)
|
||||
else if (isAgent)
|
||||
{
|
||||
// 代理只能看到自己下面的用户
|
||||
var list = await userLookupQuery
|
||||
@ -164,10 +168,18 @@ namespace LMS.service.Service.UserService
|
||||
.ToListAsync();
|
||||
|
||||
HashSet<long> filteredParentIds = new(list);
|
||||
if (filteredParentIds?.Count > 0)
|
||||
if (filteredParentIds != null)
|
||||
{
|
||||
query = query.Where(x => filteredParentIds.Contains(x.Id));
|
||||
}
|
||||
else
|
||||
{
|
||||
query = query.Where(x => x.Id == reuqertUserId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
query = query.Where(x => x.Id == reuqertUserId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -26,6 +26,6 @@
|
||||
],
|
||||
"Enrich": [ "FromLogContext" ]
|
||||
},
|
||||
"Version": "1.0.4",
|
||||
"Version": "1.0.5",
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user