diff --git a/LoongPanel-Asp/ApplicationDbContext.cs b/LoongPanel-Asp/ApplicationDbContext.cs index b73234e..8722cd4 100755 --- a/LoongPanel-Asp/ApplicationDbContext.cs +++ b/LoongPanel-Asp/ApplicationDbContext.cs @@ -115,8 +115,8 @@ public class ApplicationDbContext(DbContextOptions options Name = roleName, NormalizedName = roleName.ToUpperInvariant(), ApiPermissions = - ["1","2","3","4","5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20","21","22","23","24","25","26","27","28","29","30"], - RouterPermissions = ["1", "2", "3", "4","5","6","7","8","9","10","11","12","13","14"] + ["1","2","3","4","5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35"], + RouterPermissions = ["1", "2", "3", "4","5","6","7","8","9","10","11","12","13","14","15","16"] }); } @@ -145,6 +145,8 @@ public class ApplicationDbContext(DbContextOptions options new RotePermission { Id = 10, Name = "进程列表", Router = "/host/process" }, new RotePermission { Id = 11, Name = "网络连接列表", Router = "/host/networklist" }, new RotePermission { Id = 12, Name = "巡检记录", Router = "/inspectionrecords" }, + new RotePermission { Id = 13, Name = "巡检记录", Router = "^/settings/.+$" }, + new RotePermission { Id = 14, Name = "登录页面修改密码", Router = "^/changepassword/.+$" }, ]; foreach (var permission in rotePermissions) modelBuilder.Entity().HasData(permission); @@ -172,6 +174,9 @@ public class ApplicationUser : IdentityUser [MaxLength(255)] public string? PhysicalAddress { get; set; } + [DataType(DataType.DateTime)] + public DateTimeOffset? PasswordExpiredDate { get; set; } + } public class ApplicationRole : IdentityRole diff --git a/LoongPanel-Asp/Configs/Alerts/4503e3c7-94c6-4ea7-acfc-d503031504fa.ini b/LoongPanel-Asp/Configs/Alerts/4503e3c7-94c6-4ea7-acfc-d503031504fa.ini deleted file mode 100644 index 6a7ffec..0000000 --- a/LoongPanel-Asp/Configs/Alerts/4503e3c7-94c6-4ea7-acfc-d503031504fa.ini +++ /dev/null @@ -1,3 +0,0 @@ -[CpuTotalUsage_d3YT] -Notify=20 -Warning=30 \ No newline at end of file diff --git a/LoongPanel-Asp/Configs/alert.ini b/LoongPanel-Asp/Configs/alert.ini new file mode 100644 index 0000000..87e6542 --- /dev/null +++ b/LoongPanel-Asp/Configs/alert.ini @@ -0,0 +1,5 @@ +[CpuTotalUsage_d3YT] +ValueName=Cpu总使用率 +Description=Cpu总使用率超过20%通知,超过30%警告 +Notify=20 +Warning=30 \ No newline at end of file diff --git a/LoongPanel-Asp/Configs/jobs.ini b/LoongPanel-Asp/Configs/jobs.ini index 06ba629..e7c5d3a 100755 --- a/LoongPanel-Asp/Configs/jobs.ini +++ b/LoongPanel-Asp/Configs/jobs.ini @@ -36,7 +36,7 @@ ValueName = 句柄总使用数 Description = A simple job that uses the Process JobType = LoongPanel_Asp.Jobs.PhrasePatternJob, LoongPanel-Asp Executor = d3YT,xseg -CronExpression = 6/30 * * * * ? * +CronExpression = 6/15 * * * * ? * [MemoryTotalJob] Group = Memory diff --git a/LoongPanel-Asp/Controllers/AccountController.cs b/LoongPanel-Asp/Controllers/AccountController.cs index 73d5dda..a9296e6 100755 --- a/LoongPanel-Asp/Controllers/AccountController.cs +++ b/LoongPanel-Asp/Controllers/AccountController.cs @@ -1,8 +1,9 @@ using System.Security.Claims; +using System.Security.Cryptography; using LiteDB; using LoongPanel_Asp.Helpers; using LoongPanel_Asp.Models; -using LoongPanel_Asp.Types; +using LoongPanel_Asp.Servers; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; @@ -13,7 +14,7 @@ namespace LoongPanel_Asp.Controllers; public class AccountController( UserManager userManager, SignInManager signInManager, - EmailHelper emailHelper, + EmailService emailService, TokenHelper tokenHelper, ILiteDatabase db) : ControllerBase @@ -59,47 +60,157 @@ public class AccountController( [HttpPost("Login")] public async Task Login([FromBody] LoginModel model) { - if (!ModelState.IsValid) return BadRequest(new ApiResponse(ApiResponseState.Error, "Invalid request")); - - try + if (!ModelState.IsValid) return BadRequest("错误的内容"); + ApplicationUser? user; + if (model.EmailOrUserName.Contains('@')) + user = await userManager.FindByEmailAsync(model.EmailOrUserName); + else + user = await userManager.FindByNameAsync(model.EmailOrUserName); + if (user == null) return BadRequest("用户不存在"); + var result = await signInManager.CheckPasswordSignInAsync(user, model.Password,true); + if (!result.Succeeded) return BadRequest("错误账号或密码"); + if (await userManager.IsLockedOutAsync(user)) { - ApplicationUser? user = null; - // 判断字符串是否包含@ - if (model.EmailOrUserName.Contains('@')) - user = await userManager.FindByEmailAsync(model.EmailOrUserName); - else - user = await userManager.FindByNameAsync(model.EmailOrUserName); - if (user == null) return Ok(new ApiResponse(ApiResponseState.Error, "Invalid email or username")); - Console.WriteLine(user.UserName); - var result = await signInManager.CheckPasswordSignInAsync(user, model.Password, model.RememberMe); - if (!result.Succeeded) return Ok(new ApiResponse(ApiResponseState.Error, "Invalid password")); - - var roles = await userManager.GetRolesAsync(user); - var roleId = roles.ToList()[0]; // 直接获取角色ID列表 - var claimsIdentity = new ClaimsIdentity(new[] + return BadRequest("账号已锁定,请联系管理员"); + } + if (!user.EmailConfirmed&&user.UserName!="admin") + { + return Unauthorized(new { - // userId - new Claim(ClaimTypes.NameIdentifier, user.Id), - // email - new Claim(ClaimTypes.Email, user.Email!), - // role - new Claim(ClaimTypes.Role, roleId.ToLower()) // 将角色ID列表转换为逗号分隔的字符串 + user.Email, + user.Id }); - var token = tokenHelper.GenerateToken(claimsIdentity); - - return Ok(new ApiResponse(ApiResponseState.Success, "Login successful", new - { - user.UserName, - Token = token - })); - ; } - catch (Exception ex) + var roles = await userManager.GetRolesAsync(user); + var roleId = roles.ToList()[0]; // 直接获取角色ID列表 + var claimsIdentity = new ClaimsIdentity(new[] { - // Log the exception - // logger.LogError(ex, "An error occurred while processing the login request."); - return StatusCode(StatusCodes.Status500InternalServerError, - new ApiResponse(ApiResponseState.Error, ex.Message)); + new Claim(ClaimTypes.NameIdentifier, user.Id), + new Claim(ClaimTypes.Email, user.Email!), + new Claim(ClaimTypes.Role, roleId.ToLower()) // 将角色ID列表转换为逗号分隔的字符串 + }); + var token = tokenHelper.GenerateToken(claimsIdentity); + if (user.PasswordExpiredDate == null || user.PasswordExpiredDate < DateTimeOffset.Now) + { + //返回402 + return StatusCode(402,new + { + token, + user.Id, + }); } + + return Ok(new + { + user.NickName, + Token=token + }); } -} \ No newline at end of file + + [HttpGet("VerifyEmail")] + public async Task VerifyEmail( [FromQuery] string userId,[FromQuery] string email ,[FromQuery] string? code = null) + { + //如果code 不为空 + if (code != null) + { + var r= emailService.VerifyEmailVerifyCode(userId,code); + if (!r) return BadRequest("验证码错误"); + //验证成功 + //更新用户信息 + var user = await userManager.FindByIdAsync(userId); + if (user == null) return BadRequest("用户不存在"); + user.EmailConfirmed = true; + user.Email = email; + await userManager.UpdateAsync(user); + return Ok("邮箱验证成功"); + } + await emailService.SendEmailVerifyCodeAsync( userId,email, "尊敬的用户"); + return Ok("邮件已发送"); + } + [HttpGet("ChangePassword")] + public async Task ChangePassword([FromQuery] string currentPassword,[FromQuery] string newPassword) + { + // 获取当前经过身份验证的用户 + var authenticatedUser = await userManager.GetUserAsync(HttpContext.User); + + if (authenticatedUser == null) + { + return BadRequest("用户未登录"); + } + + // 检查当前密码是否正确 + var isCurrentPasswordValid = await userManager.CheckPasswordAsync(authenticatedUser, currentPassword); + if (!isCurrentPasswordValid) + { + return BadRequest("当前密码不正确"); + } + + // 检查新密码是否与旧密码相同 + if (currentPassword == newPassword) + { + return BadRequest("新密码不能与旧密码相同"); + } + + // 生成密码重置令牌 + var token = await userManager.GeneratePasswordResetTokenAsync(authenticatedUser); + + // 重置密码 + var result = await userManager.ResetPasswordAsync(authenticatedUser, token, newPassword); + + if (!result.Succeeded) + { + return BadRequest("修改密码失败"); + } + //修改用户的过期时间为1天后 + authenticatedUser.PasswordExpiredDate = DateTimeOffset.Now.AddDays(1); + await userManager.UpdateAsync(authenticatedUser); + return Ok("修改密码成功"); + } + + [HttpGet("ForgotPassword")] + public async Task ForgotPassword([FromQuery] string email) + { + var user = await userManager.FindByEmailAsync(email); + if (user == null) return BadRequest("用户不存在"); + var token = await userManager.GeneratePasswordResetTokenAsync(user); + await emailService.SendEmailAsync(email, "尊敬的用户", "重置密码", + $"请点击这里重置密码"); + return Ok("邮件已发送"); + } + + [HttpGet("ResetPassword")] + public async Task ResetPassword([FromQuery] string email, [FromQuery] string token) + { + var user = await userManager.FindByEmailAsync(email); + if (user == null) return BadRequest("用户不存在"); + //随机生成密码12位 特殊字符大小写数字 英文 + token= token.Replace(" ", "+"); + var newPassword = GenerateRandomPassword(32); + var result = await userManager.ResetPasswordAsync(user, token, newPassword); + if (!result.Succeeded) return BadRequest(result.Errors.FirstOrDefault()?.Description); + user.PasswordExpiredDate = DateTimeOffset.Now.AddDays(1); + await userManager.UpdateAsync(user); + await emailService.SendEmailAsync(user.Email ?? throw new InvalidOperationException(), + user.UserName ?? throw new InvalidOperationException(), "新密码", + $"

您的新密码是:{newPassword}

"); + return Ok("密码重置成功,新密码已生成并发送给用户邮箱。"); + } + + //登出 + [HttpGet("Logout")] + public async Task Logout() + { + await signInManager.SignOutAsync(); + return Ok("Logout successful"); + } + private string GenerateRandomPassword(int length) + { + const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%&*"; + var byteBuffer = new byte[length]; + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(byteBuffer); + } + return new string(byteBuffer.Select(b => chars[b % chars.Length]).ToArray()); + } +} diff --git a/LoongPanel-Asp/Controllers/ConfigController.cs b/LoongPanel-Asp/Controllers/ConfigController.cs new file mode 100644 index 0000000..d004647 --- /dev/null +++ b/LoongPanel-Asp/Controllers/ConfigController.cs @@ -0,0 +1,96 @@ +using System.Security.Claims; +using IniParser; +using IniParser.Model; +using LoongPanel_Asp.Helpers; +using Microsoft.AspNetCore.Mvc; + +namespace LoongPanel_Asp.Controllers; + +[ApiController] +[Route("Api/[controller]")] +public class ConfigController:ControllerBase +{ + [HttpGet("GetAlertConfig")] + public IActionResult GetAlertConfig() + { + var userId = HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)!.Value; + //从Configs/Alerts/userId.ini + var parser=new FileIniDataParser(); + var fullPath=Path.Combine("Configs","Alerts",$"{userId}.ini"); + //判断文件是否存在 + if (!System.IO.File.Exists(fullPath)) + { + //复制一份默认配置文件 + System.IO.File.Copy(Path.Combine("Configs","alert.ini"),fullPath); + JobConfigHelper.ReloadAlerts(); + } + var data = parser.ReadFile(fullPath); + //获得所有section + var sections = data.Sections; + //遍历 + var alertsConfigs = sections.Select(x => + { + var alertConfiguration = new + { + AlertType = x.SectionName.Split("_")[0], + ServerId = x.SectionName.Split("_")[1], + Notify= x.Keys["Notify"], + Warning=x.Keys["Warning"], + TypeName=x.Keys["ValueName"], + Description=x.Keys["Description"], + }; + return alertConfiguration; + }); + return Ok(alertsConfigs); + } + + [HttpPost("AddAlertConfig")] + public IActionResult AddAlertConfig([FromBody] AlertModel model) + { + var userId = HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)!.Value; + var fullPath=Path.Combine("Configs","Alerts",$"{userId}.ini"); + var parser=new FileIniDataParser(); + var data = parser.ReadFile(fullPath); + //判断是否存在 + if (data.Sections.Contains($"{model.DataType}_{model.ServerId}")) + { + return BadRequest("配置已存在"); + } + var sectionName=$"{model.DataType}_{model.ServerId}"; + data[sectionName]["Notify"]=model.Notify; + data[sectionName]["Warning"]=model.Warning; + data[sectionName]["ValueName"]=model.DataName; + data[sectionName]["Description"]=model.Description; + parser.WriteFile(fullPath,data); + JobConfigHelper.ReloadAlerts(); + return Ok("配置已添加"); + } + [HttpDelete("DeleteAlertConfig")] + public IActionResult DeleteAlertConfig([FromQuery] string dataType,[FromQuery] string serverId) + { + var userId = HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)!.Value; + var fullPath=Path.Combine("Configs","Alerts",$"{userId}.ini"); + var parser=new FileIniDataParser(); + var data = parser.ReadFile(fullPath); + //判断是否存在 + if (!data.Sections.Contains($"{dataType}_{serverId}")) + { + return BadRequest("配置不存在"); + } + data.Sections.RemoveSection($"{dataType}_{serverId}"); + parser.WriteFile(fullPath,data); + JobConfigHelper.ReloadAlerts(); + return Ok("配置已删除"); + } +} + +public class AlertModel +{ + public required string ServerId {get;init;} + public required string DataType {get;init;} + public required string Description {get;init;} + public required string Notify {get;init;} + public required string Warning {get;init;} + + public required string DataName {get;init;} +} \ No newline at end of file diff --git a/LoongPanel-Asp/Controllers/JobController.cs b/LoongPanel-Asp/Controllers/JobController.cs index d21d192..4825c62 100755 --- a/LoongPanel-Asp/Controllers/JobController.cs +++ b/LoongPanel-Asp/Controllers/JobController.cs @@ -7,18 +7,18 @@ namespace LoongPanel_Asp.Controllers; public class JobController(ApplicationDbContext dbContext) : ControllerBase { [HttpGet("GetJobList")] - public IActionResult GetJobList(string serverId) + public IActionResult GetJobList([FromQuery]string? serverId=null) { var serverMonitoringData = dbContext.ServerMonitoringData; - //获得所有DataName类型 var dataNameTypesWithDataType = serverMonitoringData - .GroupBy(x => x.DataName) // 假设DataName是ServerMonitoringData实体中的一个属性 + .Where(x=>!string.IsNullOrEmpty(serverId) || x.ServerId==serverId) + .GroupBy(x => x.DataName) .Select(group => new { - DataName = group.Key, group.First().DataType // 假设DataType是ServerMonitoringData实体中的一个属性 + DataName = group.Key, group.First().DataType ,group.First().ServerId }) .ToList(); - return Ok(dataNameTypesWithDataType); // 返回所有唯一的DataName类型及其对应的DataType + return Ok(dataNameTypesWithDataType); } } \ No newline at end of file diff --git a/LoongPanel-Asp/Controllers/RoteController.cs b/LoongPanel-Asp/Controllers/RoteController.cs index 553f10c..6c9e2f8 100755 --- a/LoongPanel-Asp/Controllers/RoteController.cs +++ b/LoongPanel-Asp/Controllers/RoteController.cs @@ -17,11 +17,10 @@ public class RoteController( { var roleId = HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Role)!.Value; //获得role - Console.WriteLine(roleId); var role = await roleManager.FindByNameAsync(roleId); var rotes = role!.RouterPermissions.ToList(); //获取路由列表 - var apiPermissions = dbContext.RotePermissions.ToList().Select(x => x.Router); + var apiPermissions = dbContext.RotePermissions.ToList().Where(x => rotes.Any(y => y == x.Id.ToString())).Select(x=>x.Router).ToList(); //将path全部小写 path = path.ToLower(); //使用正则匹配 diff --git a/LoongPanel-Asp/Helpers/DataHelper.cs b/LoongPanel-Asp/Helpers/DataHelper.cs index 0ad7783..fd94039 100755 --- a/LoongPanel-Asp/Helpers/DataHelper.cs +++ b/LoongPanel-Asp/Helpers/DataHelper.cs @@ -45,6 +45,7 @@ public class DataHelper(ApplicationDbContext dbContext,IHubContext c foreach (var item in matchingValues) { await context.Clients.Group(item).SendAsync("ReceiveWaring", value, valueName); + var key=$"{serverId}_{valueType}+{item}"; } return; } diff --git a/LoongPanel-Asp/Helpers/EmailHelper.cs b/LoongPanel-Asp/Helpers/EmailHelper.cs deleted file mode 100755 index bdcf598..0000000 --- a/LoongPanel-Asp/Helpers/EmailHelper.cs +++ /dev/null @@ -1,44 +0,0 @@ -using LiteDB; -using MailKit.Net.Smtp; -using MailKit.Security; -using MimeKit; -using MimeKit.Text; - -namespace LoongPanel_Asp.Helpers; - -public class EmailHelper( - ILiteDatabase? database, - string? smtpHost, - int smtpPort, - string smtpUsername, - string smtpPassword) -{ - private readonly string? _smtpHost = smtpHost; - private readonly string _smtpPassword = smtpPassword; - private readonly int _smtpPort = smtpPort; - private readonly string _smtpUsername = smtpUsername; - private ILiteDatabase? _database = database; - - public async Task SendVerificationEmailAsync(string toEmail, string code) - { - var emailMessage = new MimeMessage(); - emailMessage.From.Add(new MailboxAddress("龙腾云御", _smtpUsername)); - emailMessage.To.Add(new MailboxAddress("", toEmail)); - var body = $"

请验证您的邮箱

这是你的验证码

{code}

"; - emailMessage.Body = new TextPart(TextFormat.Html) - { - Text = body - }; - - using var client = new SmtpClient(); - await client.ConnectAsync(_smtpHost, _smtpPort, SecureSocketOptions.SslOnConnect); - await client.AuthenticateAsync(_smtpUsername, _smtpPassword); - await client.SendAsync(emailMessage); - await client.DisconnectAsync(true); - } - - public string GenerateVerificationCode() - { - return Guid.NewGuid().ToString()[..6]; - } -} \ No newline at end of file diff --git a/LoongPanel-Asp/Helpers/JobConfigHelper.cs b/LoongPanel-Asp/Helpers/JobConfigHelper.cs index 878c4ae..41da3d0 100755 --- a/LoongPanel-Asp/Helpers/JobConfigHelper.cs +++ b/LoongPanel-Asp/Helpers/JobConfigHelper.cs @@ -164,6 +164,18 @@ public static class JobConfigHelper _alertsConfigs =alertsConfigs; return alertsConfigs; } + + public static void ReloadAlerts() + { + _alertsConfigs = null; + GetAlerts(); + } + + public static void ReloadServers() + { + _serverConfigs = null; + GetServers(); + } } public class JobConfiguration diff --git a/LoongPanel-Asp/Jobs/CpuJob.cs b/LoongPanel-Asp/Jobs/CpuJob.cs index 53f3534..b8861f5 100755 --- a/LoongPanel-Asp/Jobs/CpuJob.cs +++ b/LoongPanel-Asp/Jobs/CpuJob.cs @@ -113,12 +113,15 @@ public class CpuSpeedJob( } cpuDataListAll.AddRange(cpuDataList); - cpuDataList.ForEach(async data => + + cpuDataList.ForEach(Action); + continue; + + async void Action(ServerMonitoringData data) { await hubContext.Clients.All.SendAsync("ReceiveData", data.ServerId, data.DataType, data.Data); await dataHelper.CheckData(server.Id, data.DataType ?? "", data.Data ?? "", data.DataName); - }); - + } } _count++; @@ -162,11 +165,14 @@ public class CpuSingleUsageJob( cpuDataList.Add(singleUsage); } - cpuDataList.ForEach(async data => + cpuDataList.ForEach(Action); + continue; + + async void Action(ServerMonitoringData data) { - await hubContext.Clients.All.SendAsync("ReceiveData", data.ServerId, data.DataType, data.Data); + await hubContext.Clients.All.SendAsync("ReceiveData", data.ServerId, data.DataType, data.Data); await dataHelper.CheckData(server.Id, data.DataType ?? "", data.Data ?? "", data.DataName); - }); + } } _count++; diff --git a/LoongPanel-Asp/Jobs/DiskJob.cs b/LoongPanel-Asp/Jobs/DiskJob.cs index 6d99585..831fc8e 100755 --- a/LoongPanel-Asp/Jobs/DiskJob.cs +++ b/LoongPanel-Asp/Jobs/DiskJob.cs @@ -37,6 +37,7 @@ public class DiskTotalJob( }; await hubContext.Clients.All.SendAsync("ReceiveData", diskTotalUsage.ServerId, diskTotalUsage.DataType, diskTotalUsage.Data); + await dataHelper.CheckData(server.Id, diskTotalUsage.DataType ?? "", diskTotalUsage.Data ?? "", "磁盘总使用率"); diskDataList.Add(diskTotalUsage); diskDataListAll.AddRange(diskDataList); } @@ -116,10 +117,15 @@ public class DiskUseJob( DataType = $"DiskUtil-{dev}" }; diskDataList.Add(diskUtil); - diskDataList.ForEach(data => + + diskDataList.ForEach(Action); + continue; + + async void Action(ServerMonitoringData data) { - hubContext.Clients.All.SendAsync("ReceiveData", data.ServerId, data.DataType, data.Data); - }); + await hubContext.Clients.All.SendAsync("ReceiveData", data.ServerId, data.DataType, data.Data); + await dataHelper.CheckData(server.Id, data.DataType ?? "", data.Data ?? "", data.DataName); + } } diskDataListAll.AddRange(diskDataList); } diff --git a/LoongPanel-Asp/Jobs/MemoryJob.cs b/LoongPanel-Asp/Jobs/MemoryJob.cs index 6d12fa7..e3e6972 100755 --- a/LoongPanel-Asp/Jobs/MemoryJob.cs +++ b/LoongPanel-Asp/Jobs/MemoryJob.cs @@ -79,11 +79,16 @@ public class MemoryTotalJob( DataType = "SwapTotalUsage" }; serverDataList.Add(swapData); - serverDataList.ForEach(data => - { - hubContext.Clients.All.SendAsync("ReceiveData", data.ServerId, data.DataType, data.Data); - }); + + serverDataList.ForEach(Action); serverDataListAll.AddRange(serverDataList); + continue; + + async void Action(ServerMonitoringData data) + { + await hubContext.Clients.All.SendAsync("ReceiveData", data.ServerId, data.DataType, data.Data); + await dataHelper.CheckData(server.Id, data.DataType ?? "", data.Data ?? "", data.DataName); + } } _count++; diff --git a/LoongPanel-Asp/Jobs/NetworkJob.cs b/LoongPanel-Asp/Jobs/NetworkJob.cs index 65f5e11..51d1314 100755 --- a/LoongPanel-Asp/Jobs/NetworkJob.cs +++ b/LoongPanel-Asp/Jobs/NetworkJob.cs @@ -65,10 +65,15 @@ public class NetworkTotalJob( }; netWorkDataList.Add(d); netWorkDataListAll.AddRange(netWorkDataList); - netWorkDataList.ForEach(data => + + netWorkDataList.ForEach( Action); + continue; + + async void Action(ServerMonitoringData data) { - hubContext.Clients.All.SendAsync("ReceiveData", data.ServerId, data.DataType, data.Data); - }); + await hubContext.Clients.All.SendAsync("ReceiveData", data.ServerId, data.DataType, data.Data); + await dataHelper.CheckData(server.Id, data.DataType ?? "", data.Data ?? "", data.DataName); + } } _count++; diff --git a/LoongPanel-Asp/Jobs/ProcessJob.cs b/LoongPanel-Asp/Jobs/ProcessJob.cs index 975a6dd..a0971b0 100644 --- a/LoongPanel-Asp/Jobs/ProcessJob.cs +++ b/LoongPanel-Asp/Jobs/ProcessJob.cs @@ -46,11 +46,16 @@ public class ProcessTotalJob( DataType = "ThreadsTotalCount" }; processDataList.Add(threadsTotalCount); - processDataList.ForEach(data => - { - hubContext.Clients.All.SendAsync("ReceiveData", data.ServerId, data.DataType, data.Data); - }); + + processDataList.ForEach(Action); processDataListAll.AddRange(processDataList); + continue; + + async void Action(ServerMonitoringData data) + { + await hubContext.Clients.All.SendAsync("ReceiveData", data.ServerId, data.DataType, data.Data); + await dataHelper.CheckData(server.Id, data.DataType ?? "", data.Data ?? "", data.DataName); + } } _count++; @@ -89,6 +94,8 @@ public class PhrasePatternJob( processDataListAll.Add(phrasePatternCount); await hubContext.Clients.All.SendAsync("ReceiveData", server.Id, phrasePatternCount.DataType, phrasePatternCount.Data); + await dataHelper.CheckData(server.Id, phrasePatternCount.DataType ?? "", phrasePatternCount.Data ?? "", + "句柄数"); } _count++; diff --git a/LoongPanel-Asp/Jobs/UserJob.cs b/LoongPanel-Asp/Jobs/UserJob.cs index d461026..4ff5341 100644 --- a/LoongPanel-Asp/Jobs/UserJob.cs +++ b/LoongPanel-Asp/Jobs/UserJob.cs @@ -59,13 +59,18 @@ public class UserTotalJob(IHubContext hubContext, userDataListAll.Add(data); } } - userDataListAll.ForEach(data => - { - hubContext.Clients.All.SendAsync("ReceiveData", data.ServerId, data.DataType, data.Data); - }); + + userDataListAll.ForEach(Action); _count++; if (_count <= 10) return; _count = 0; await dataHelper.SaveData(userDataListAll); + return; + + async void Action(ServerMonitoringData data) + { + await hubContext.Clients.All.SendAsync("ReceiveData", data.ServerId, data.DataType, data.Data); + await dataHelper.CheckData(data.ServerId, data.DataType ?? "", data.Data ?? "", data.DataName); + } } } \ No newline at end of file diff --git a/LoongPanel-Asp/LoongPanel-Asp.csproj b/LoongPanel-Asp/LoongPanel-Asp.csproj index 9eefd98..52adf0a 100755 --- a/LoongPanel-Asp/LoongPanel-Asp.csproj +++ b/LoongPanel-Asp/LoongPanel-Asp.csproj @@ -46,6 +46,7 @@ - + + diff --git a/LoongPanel-Asp/Middlewares/ApiPermissionMiddleware.cs b/LoongPanel-Asp/Middlewares/ApiPermissionMiddleware.cs index 0e088dd..838906b 100755 --- a/LoongPanel-Asp/Middlewares/ApiPermissionMiddleware.cs +++ b/LoongPanel-Asp/Middlewares/ApiPermissionMiddleware.cs @@ -16,9 +16,8 @@ public class ApiPermissionMiddleware( // 获取配置中定义的公开API列表 var publicApis = configuration["PublicApi"]?.Split(";", StringSplitOptions.RemoveEmptyEntries) ?? []; - - // 如果请求路径在公开API列表中,则直接调用下一个中间件 - if (publicApis.Any(api => api == context.Request.Path.Value)) + + if (publicApis.Any(api => context.Request.Path.Value == api)) { await next(context); return; diff --git a/LoongPanel-Asp/Middlewares/PermissionMiddleware.cs b/LoongPanel-Asp/Middlewares/PermissionMiddleware.cs index 36627c7..4973464 100755 --- a/LoongPanel-Asp/Middlewares/PermissionMiddleware.cs +++ b/LoongPanel-Asp/Middlewares/PermissionMiddleware.cs @@ -13,7 +13,7 @@ public class PermissionMiddleware( var publicApis = configuration["PublicApi"]?.Split(";") ?? []; // 如果请求路径在公开API列表中,则直接调用下一个中间件 - if (publicApis.Any(api => api == context.Request.Path.Value)) + if (publicApis.Any(api => context.Request.Path.Value == api)) { await next(context); return; diff --git a/LoongPanel-Asp/Models/OrderType.cs b/LoongPanel-Asp/Models/OrderType.cs index 745da03..0a759df 100755 --- a/LoongPanel-Asp/Models/OrderType.cs +++ b/LoongPanel-Asp/Models/OrderType.cs @@ -18,3 +18,12 @@ public class ServerModel public required string Username { get; set; } public required bool Http { get; set; } } + + +public class EmailSettings +{ + public required string Host {get;init;} + public required string Port {get;init;} + public required string Username {get;init;} + public required string Password {get;init;} +} \ No newline at end of file diff --git a/LoongPanel-Asp/Program.cs b/LoongPanel-Asp/Program.cs index 15d0885..b6562e9 100755 --- a/LoongPanel-Asp/Program.cs +++ b/LoongPanel-Asp/Program.cs @@ -3,8 +3,10 @@ using LoongPanel_Asp; using LoongPanel_Asp.Helpers; using LoongPanel_Asp.Hubs; using LoongPanel_Asp.Middlewares; +using LoongPanel_Asp.Models; using LoongPanel_Asp.Servers; using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.EntityFrameworkCore; using Quartz; using Quartz.AspNetCore; @@ -39,7 +41,7 @@ builder.Services.Configure(options => // User settings. options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+"; - options.User.RequireUniqueEmail = false; + options.User.RequireUniqueEmail = true; }); builder.Services.AddSignalR(); @@ -61,14 +63,8 @@ builder.Services.AddSingleton(builder.Configuration); builder.Services.AddSingleton(); var emailSettings = builder.Configuration.GetSection("EmailSettings"); -builder.Services.AddSingleton(sp => - new EmailHelper( - sp.GetService(), - emailSettings["Host"] ?? throw new InvalidOperationException(), - int.Parse(emailSettings["Port"] ?? throw new InvalidOperationException()), - emailSettings["Username"] ?? throw new InvalidOperationException(), - emailSettings["Password"] ?? throw new InvalidOperationException() - )); +builder.Services.AddSingleton(provider => new EmailService(emailSettings)); + builder.Services.AddSingleton( sp => new LiteDatabase("Filename=temp.db;Connection=shared;")); diff --git a/LoongPanel-Asp/Properties/launchSettings.json b/LoongPanel-Asp/Properties/launchSettings.json index be89b84..d43adb8 100755 --- a/LoongPanel-Asp/Properties/launchSettings.json +++ b/LoongPanel-Asp/Properties/launchSettings.json @@ -4,7 +4,7 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://127.0.0.1:58826", + "applicationUrl": "http://192.168.0.13:58826", "sslPort": 44304 } }, @@ -14,7 +14,7 @@ "dotnetRunMessages": true, "launchBrowser": false, "launchUrl": "swagger", - "applicationUrl": "http://127.0.0.1:5253", + "applicationUrl": "http://192.168.0.13:5253", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -24,7 +24,7 @@ "dotnetRunMessages": true, "launchBrowser": true, "launchUrl": "swagger", - "applicationUrl": "https://127.0.0.1:7233;http://127.0.0.1:5253", + "applicationUrl": "https://192.168.0.13:7233;http://192.168.0.13:5253", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/LoongPanel-Asp/Servers/EmailService.cs b/LoongPanel-Asp/Servers/EmailService.cs new file mode 100644 index 0000000..a9fa79c --- /dev/null +++ b/LoongPanel-Asp/Servers/EmailService.cs @@ -0,0 +1,74 @@ +using System.Collections.Concurrent; +using System.Net; +using LoongPanel_Asp.Models; +using MimeKit; +using SmtpClient = MailKit.Net.Smtp.SmtpClient; + +namespace LoongPanel_Asp.Servers; + +public class EmailService:IDisposable +{ + + private readonly SmtpClient _smtpClient; + private readonly IConfigurationSection _emailSettings; + //创建一个验证码缓存字典 + private readonly ConcurrentDictionary _verifyCodeCache = new(); + public EmailService(IConfigurationSection emailSettings) + { + _emailSettings = emailSettings; + // 初始化SmtpClient + _smtpClient = new SmtpClient(); + _smtpClient.Connect(_emailSettings["Host"], int.Parse(_emailSettings["Port"] ?? throw new InvalidOperationException()), true); + _smtpClient.Authenticate(_emailSettings["Username"], _emailSettings["Password"]); + } + + + //发送邮件 + public async Task SendEmailAsync(string toAddress,string toName, string subject, string body) + { + using var mailMessage = new MimeMessage(); + mailMessage.From.Add(new MailboxAddress("龙盾云御",_emailSettings["Username"])); + mailMessage.To.Add(new MailboxAddress(toName, toAddress)); + mailMessage.Subject = subject; + var bodyBuilder = new BodyBuilder + { + HtmlBody = body, + }; + mailMessage.Body = bodyBuilder.ToMessageBody(); + await _smtpClient.SendAsync(mailMessage); + } + + + //发送邮箱验证码 + public async Task SendEmailVerifyCodeAsync( string userId,string toAddress, string toName) + { + const string subject = "龙盾云御邮箱验证码"; + var code=GenerateVerifyCode(); + var body = $"

您的验证码是:{code}

"; + await SendEmailAsync(toAddress, toName, subject, body); + //存入缓存 + _verifyCodeCache.AddOrUpdate(userId, code, (key, oldValue) => code); + } + //验证邮箱验证码 + public bool VerifyEmailVerifyCode(string userId, string code) + { + if (!_verifyCodeCache.TryGetValue(userId, out var verifyCode)) return false; + if (verifyCode != code) return false; + _verifyCodeCache.TryRemove(userId, out _); + return true; + } + + + //生成6位随机验证码 + private static string GenerateVerifyCode() + { + var random = new Random(); + var verifyCode = random.Next(100000, 999999).ToString(); + return verifyCode; + } + + public void Dispose() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/LoongPanel-Asp/appsettings.json b/LoongPanel-Asp/appsettings.json index 5db4b83..eb177b1 100755 --- a/LoongPanel-Asp/appsettings.json +++ b/LoongPanel-Asp/appsettings.json @@ -20,5 +20,5 @@ "Secret": "p4Qzf/+GPP/XNLalZGCzwlelOl6skiFZscj6iZ6rZZE=", "Issuer": "LoongPanel", "Audience": "LoongPanel", - "PubLicApi": "/Api/Account/Login" + "PubLicApi": "/Api/Account/Login;/Api/Account/VerifyEmail;/Api/Account/ForgotPassword;/Api/Account/ResetPassword;" } diff --git a/LoongPanel-Asp/markdowns/templates/值班记录.md b/LoongPanel-Asp/markdowns/templates/值班记录.md new file mode 100644 index 0000000..7957a64 --- /dev/null +++ b/LoongPanel-Asp/markdowns/templates/值班记录.md @@ -0,0 +1,26 @@ +| ֵ¼ | col | col | col | col | col | +| -------- | ------- | ------- | ------- | ------- | ------- | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | +| content | content | content | content | content | content | \ No newline at end of file diff --git a/LoongPanel-Asp/my.pfx b/LoongPanel-Asp/my.pfx deleted file mode 100644 index 344a793..0000000 Binary files a/LoongPanel-Asp/my.pfx and /dev/null differ diff --git a/web/.env.development b/web/.env.development index d561ac2..54d6dd1 100644 --- a/web/.env.development +++ b/web/.env.development @@ -1 +1 @@ -NUXT_API_URL="http://127.0.0.1:5253" \ No newline at end of file +NUXT_API_URL="http://192.168.0.13:5253" \ No newline at end of file diff --git a/web/assets/diagonal1.cur b/web/assets/diagonal1.cur deleted file mode 100755 index 20857fb..0000000 Binary files a/web/assets/diagonal1.cur and /dev/null differ diff --git a/web/assets/link.cur b/web/assets/link.cur deleted file mode 100755 index 23a8022..0000000 Binary files a/web/assets/link.cur and /dev/null differ diff --git a/web/assets/min.scss b/web/assets/min.scss index 2f89066..4d258df 100755 --- a/web/assets/min.scss +++ b/web/assets/min.scss @@ -4,7 +4,6 @@ margin: 0; box-sizing: border-box; transition: color .2s, background-color .2s, border-color .2s, box-shadow .2s, stork .2s, fill .2s, opacity .2s; - cursor: url("assets/normal.cur"), auto; } body { diff --git a/web/assets/normal.cur b/web/assets/normal.cur deleted file mode 100755 index 2ef5163..0000000 Binary files a/web/assets/normal.cur and /dev/null differ diff --git a/web/assets/normalshowel.cur b/web/assets/normalshowel.cur deleted file mode 100755 index e568830..0000000 Binary files a/web/assets/normalshowel.cur and /dev/null differ diff --git a/web/bun.lockb b/web/bun.lockb index 8168b6e..7492ec1 100644 Binary files a/web/bun.lockb and b/web/bun.lockb differ diff --git a/web/components/ColorSwitch.vue b/web/components/ColorSwitch.vue index ca9c21d..93dafcd 100755 --- a/web/components/ColorSwitch.vue +++ b/web/components/ColorSwitch.vue @@ -3,79 +3,108 @@ diff --git a/web/components/Grid/MainGrid.vue b/web/components/Grid/MainGrid.vue index a046962..1e0ea47 100644 --- a/web/components/Grid/MainGrid.vue +++ b/web/components/Grid/MainGrid.vue @@ -43,27 +43,6 @@ const items = ref([ cardVisible.value = true } }, - { - label: 'Delete', - icon: 'pi pi-trash', - command: () => { - toast.add({severity: 'error', summary: 'Delete', detail: 'Data Deleted'}); - } - }, - { - label: 'Upload', - icon: 'pi pi-upload', - command: () => { - router.push('/fileupload'); - } - }, - { - label: 'Vue Website', - icon: 'pi pi-external-link', - command: () => { - window.location.href = 'https://vuejs.org/' - } - } ]) const layoutChangedEvent = _.throttle((newLayout: IGridItem[]) => { @@ -124,15 +103,7 @@ watchDebounced(