调整Job的执行方式 优化数据存储方式

This commit is contained in:
zwb 2024-07-29 12:03:47 +08:00
parent 009f46879c
commit 16de25c2ca
41 changed files with 759 additions and 1135 deletions

View File

@ -1,7 +1,4 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Security.Cryptography;
using System.Text.Json;
using LoongPanel_Asp.Models;
using LoongPanel_Asp.utils; using LoongPanel_Asp.utils;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
@ -115,7 +112,10 @@ public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options
Name = roleName, Name = roleName,
NormalizedName = roleName.ToUpperInvariant(), NormalizedName = roleName.ToUpperInvariant(),
ApiPermissions = 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","31","32","33","34","35"], [
"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", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"] RouterPermissions = ["1", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"]
}); });
} }
@ -146,12 +146,11 @@ public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options
new RotePermission { Id = 11, Name = "网络连接列表", Router = "/host/networklist" }, new RotePermission { Id = 11, Name = "网络连接列表", Router = "/host/networklist" },
new RotePermission { Id = 12, Name = "巡检记录", Router = "/inspectionrecords" }, new RotePermission { Id = 12, Name = "巡检记录", Router = "/inspectionrecords" },
new RotePermission { Id = 13, Name = "巡检记录", Router = "^/settings/.+$" }, new RotePermission { Id = 13, Name = "巡检记录", Router = "^/settings/.+$" },
new RotePermission { Id = 14, Name = "登录页面修改密码", Router = "^/changepassword/.+$" }, new RotePermission { Id = 14, Name = "登录页面修改密码", Router = "^/changepassword/.+$" }
]; ];
foreach (var permission in rotePermissions) modelBuilder.Entity<RotePermission>().HasData(permission); foreach (var permission in rotePermissions) modelBuilder.Entity<RotePermission>().HasData(permission);
} }
} }
public class ApplicationUser : IdentityUser public class ApplicationUser : IdentityUser
@ -174,9 +173,7 @@ public class ApplicationUser : IdentityUser
[MaxLength(255)] public string? PhysicalAddress { get; set; } [MaxLength(255)] public string? PhysicalAddress { get; set; }
[DataType(DataType.DateTime)] [DataType(DataType.DateTime)] public DateTimeOffset? PasswordExpiredDate { get; set; }
public DateTimeOffset? PasswordExpiredDate { get; set; }
} }
public class ApplicationRole : IdentityRole public class ApplicationRole : IdentityRole
@ -209,13 +206,13 @@ public class ServerMonitoringData
public DateTime? Time { get; set; } public DateTime? Time { get; set; }
public required string ServerId { get; set; } public required string? ServerId { get; set; }
public required string? Data { get; set; } public required string? Data { get; set; }
public required string DataName { get; set; } public required string DataName { get; set; }
public required string? DataType { get; set; } public required string DataType { get; set; }
} }
public class ListOfStringValueComparer() : ValueComparer<List<string>>((c1, c2) => c1!.SequenceEqual(c2!), public class ListOfStringValueComparer() : ValueComparer<List<string>>((c1, c2) => c1!.SequenceEqual(c2!),

View File

@ -1,4 +1,11 @@
[CpuTotalJob] [DataJob]
Group = Data
ValueName = 数据库提交
Description = A simple job that uses the database
JobType = LoongPanel_Asp.Jobs.DataJob, LoongPanel-Asp
CronExpression = 0 0/1 * * * ? *
[CpuTotalJob]
Group = CPU Group = CPU
ValueName = CPU使用率 ValueName = CPU使用率
Description = A simple job that uses the CPU Description = A simple job that uses the CPU
@ -22,6 +29,15 @@ JobType = LoongPanel_Asp.Jobs.CpuSpeedJob, LoongPanel-Asp
Executor = d3YT,xseg Executor = d3YT,xseg
CronExpression = 2/10 * * * * ? * CronExpression = 2/10 * * * * ? *
[SystemTimeIntervalLoadJob]
Group = System
ValueName = 系统间隔负载
Description = A simple job that uses the CPU
JobType = LoongPanel_Asp.Jobs.SystemTimeIntervalLoadJob, LoongPanel-Asp
Executor = d3YT,xseg
CronExpression = 1/5 * * * * ? *
[ProcessTotalJob] [ProcessTotalJob]
Group = Process Group = Process
ValueName = 进程总使用数 ValueName = 进程总使用数

View File

@ -39,14 +39,16 @@ public class AccountController(
Email = model.Email, Email = model.Email,
UserName = model.UserName, UserName = model.UserName,
PhoneNumber = model.Phone, PhoneNumber = model.Phone,
NickName = model.FullName, NickName = model.FullName
}; };
var result = await userManager.CreateAsync(user, model.Password); var result = await userManager.CreateAsync(user, model.Password);
if (!result.Succeeded) return BadRequest("无法创建用户,"+string.Join(",",result.Errors.ToList().Select(e=>e.Description))); if (!result.Succeeded)
return BadRequest("无法创建用户," + string.Join(",", result.Errors.ToList().Select(e => e.Description)));
//添加用户到默认角色 //添加用户到默认角色
result = await userManager.AddToRoleAsync(user, model.Role); result = await userManager.AddToRoleAsync(user, model.Role);
if (!result.Succeeded) return BadRequest("无法创建用户,"+string.Join(",",result.Errors.ToList().Select(e=>e.Description))); if (!result.Succeeded)
return BadRequest("无法创建用户," + string.Join(",", result.Errors.ToList().Select(e => e.Description)));
return Ok("用户创建成功"); return Ok("用户创建成功");
} }
catch (Exception e) catch (Exception e)
@ -69,29 +71,23 @@ public class AccountController(
if (user == null) return BadRequest("用户不存在"); if (user == null) return BadRequest("用户不存在");
var result = await signInManager.CheckPasswordSignInAsync(user, model.Password, true); var result = await signInManager.CheckPasswordSignInAsync(user, model.Password, true);
if (!result.Succeeded) return BadRequest("错误账号或密码"); if (!result.Succeeded) return BadRequest("错误账号或密码");
if (await userManager.IsLockedOutAsync(user)) if (await userManager.IsLockedOutAsync(user)) return BadRequest("账号已锁定,请联系管理员");
{
return BadRequest("账号已锁定,请联系管理员");
}
if (!user.EmailConfirmed && user.UserName != "admin") if (!user.EmailConfirmed && user.UserName != "admin")
{
return Unauthorized(new return Unauthorized(new
{ {
user.Email, user.Email,
user.Id user.Id
}); });
}
//创建修改密码令牌 //创建修改密码令牌
var tokenPassword = await userManager.GeneratePasswordResetTokenAsync(user); var tokenPassword = await userManager.GeneratePasswordResetTokenAsync(user);
if ((user.PasswordExpiredDate == null || user.PasswordExpiredDate < DateTimeOffset.Now)&&user.UserName!="admin") if ((user.PasswordExpiredDate == null || user.PasswordExpiredDate < DateTimeOffset.Now) &&
{ user.UserName != "admin")
//返回402 //返回402
return StatusCode(402, new return StatusCode(402, new
{ {
tokenPassword, tokenPassword,
user.Id, user.Id
}); });
}
var roles = await userManager.GetRolesAsync(user); var roles = await userManager.GetRolesAsync(user);
var roleId = roles.ToList()[0]; // 直接获取角色ID列表 var roleId = roles.ToList()[0]; // 直接获取角色ID列表
var claimsIdentity = new ClaimsIdentity(new[] var claimsIdentity = new ClaimsIdentity(new[]
@ -110,7 +106,8 @@ public class AccountController(
} }
[HttpGet("VerifyEmail")] [HttpGet("VerifyEmail")]
public async Task<IActionResult> VerifyEmail( [FromQuery] string userId,[FromQuery] string email ,[FromQuery] string? code = null) public async Task<IActionResult> VerifyEmail([FromQuery] string userId, [FromQuery] string email,
[FromQuery] string? code = null)
{ {
//如果code 不为空 //如果code 不为空
if (code != null) if (code != null)
@ -126,25 +123,22 @@ public class AccountController(
await userManager.UpdateAsync(user); await userManager.UpdateAsync(user);
return Ok("邮箱验证成功"); return Ok("邮箱验证成功");
} }
await emailService.SendEmailVerifyCodeAsync(userId, email, "尊敬的用户"); await emailService.SendEmailVerifyCodeAsync(userId, email, "尊敬的用户");
return Ok("邮件已发送"); return Ok("邮件已发送");
} }
[HttpGet("ChangePassword")] [HttpGet("ChangePassword")]
public async Task<IActionResult> ChangePassword([FromQuery] string userId, [FromQuery] string token,[FromQuery] string newPassword) public async Task<IActionResult> ChangePassword([FromQuery] string userId, [FromQuery] string token,
[FromQuery] string newPassword)
{ {
// 获取当前经过身份验证的用户 // 获取当前经过身份验证的用户
var authenticatedUser = await userManager.FindByIdAsync(userId); var authenticatedUser = await userManager.FindByIdAsync(userId);
if (authenticatedUser == null) if (authenticatedUser == null) return BadRequest("用户不存在");
{
return BadRequest("用户不存在");
}
// 重置密码 // 重置密码
var result = await userManager.ResetPasswordAsync(authenticatedUser, token, newPassword); var result = await userManager.ResetPasswordAsync(authenticatedUser, token, newPassword);
if (!result.Succeeded) if (!result.Succeeded) return BadRequest("修改密码失败");
{
return BadRequest("修改密码失败");
}
//修改用户的过期时间为1天后 //修改用户的过期时间为1天后
authenticatedUser.PasswordExpiredDate = DateTimeOffset.Now.AddDays(1); authenticatedUser.PasswordExpiredDate = DateTimeOffset.Now.AddDays(1);
await userManager.UpdateAsync(authenticatedUser); await userManager.UpdateAsync(authenticatedUser);
@ -187,6 +181,7 @@ public class AccountController(
await signInManager.SignOutAsync(); await signInManager.SignOutAsync();
return Ok("Logout successful"); return Ok("Logout successful");
} }
private string GenerateRandomPassword(int length) private string GenerateRandomPassword(int length)
{ {
const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%&*"; const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%&*";
@ -195,6 +190,7 @@ public class AccountController(
{ {
rng.GetBytes(byteBuffer); rng.GetBytes(byteBuffer);
} }
return new string(byteBuffer.Select(b => chars[b % chars.Length]).ToArray()); return new string(byteBuffer.Select(b => chars[b % chars.Length]).ToArray());
} }
} }

View File

@ -1,6 +1,5 @@
using System.Security.Claims; using System.Security.Claims;
using IniParser; using IniParser;
using IniParser.Model;
using LoongPanel_Asp.Helpers; using LoongPanel_Asp.Helpers;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -24,6 +23,7 @@ public class ConfigController:ControllerBase
System.IO.File.Copy(Path.Combine("Configs", "alert.ini"), fullPath); System.IO.File.Copy(Path.Combine("Configs", "alert.ini"), fullPath);
JobConfigHelper.ReloadAlerts(); JobConfigHelper.ReloadAlerts();
} }
var data = parser.ReadFile(fullPath); var data = parser.ReadFile(fullPath);
//获得所有section //获得所有section
var sections = data.Sections; var sections = data.Sections;
@ -37,7 +37,7 @@ public class ConfigController:ControllerBase
Notify = x.Keys["Notify"], Notify = x.Keys["Notify"],
Warning = x.Keys["Warning"], Warning = x.Keys["Warning"],
TypeName = x.Keys["ValueName"], TypeName = x.Keys["ValueName"],
Description=x.Keys["Description"], Description = x.Keys["Description"]
}; };
return alertConfiguration; return alertConfiguration;
}); });
@ -52,10 +52,7 @@ public class ConfigController:ControllerBase
var parser = new FileIniDataParser(); var parser = new FileIniDataParser();
var data = parser.ReadFile(fullPath); var data = parser.ReadFile(fullPath);
//判断是否存在 //判断是否存在
if (data.Sections.Contains($"{model.DataType}_{model.ServerId}")) if (data.Sections.Contains($"{model.DataType}_{model.ServerId}")) return BadRequest("配置已存在");
{
return BadRequest("配置已存在");
}
var sectionName = $"{model.DataType}_{model.ServerId}"; var sectionName = $"{model.DataType}_{model.ServerId}";
data[sectionName]["Notify"] = model.Notify; data[sectionName]["Notify"] = model.Notify;
data[sectionName]["Warning"] = model.Warning; data[sectionName]["Warning"] = model.Warning;
@ -65,6 +62,7 @@ public class ConfigController:ControllerBase
JobConfigHelper.ReloadAlerts(); JobConfigHelper.ReloadAlerts();
return Ok("配置已添加"); return Ok("配置已添加");
} }
[HttpDelete("DeleteAlertConfig")] [HttpDelete("DeleteAlertConfig")]
public IActionResult DeleteAlertConfig([FromQuery] string dataType, [FromQuery] string serverId) public IActionResult DeleteAlertConfig([FromQuery] string dataType, [FromQuery] string serverId)
{ {
@ -73,10 +71,7 @@ public class ConfigController:ControllerBase
var parser = new FileIniDataParser(); var parser = new FileIniDataParser();
var data = parser.ReadFile(fullPath); var data = parser.ReadFile(fullPath);
//判断是否存在 //判断是否存在
if (!data.Sections.Contains($"{dataType}_{serverId}")) if (!data.Sections.Contains($"{dataType}_{serverId}")) return BadRequest("配置不存在");
{
return BadRequest("配置不存在");
}
data.Sections.RemoveSection($"{dataType}_{serverId}"); data.Sections.RemoveSection($"{dataType}_{serverId}");
parser.WriteFile(fullPath, data); parser.WriteFile(fullPath, data);
JobConfigHelper.ReloadAlerts(); JobConfigHelper.ReloadAlerts();

View File

@ -18,13 +18,24 @@ public class JobController(ApplicationDbContext dbContext) : ControllerBase
{ "内存总体参数", ["MemoryCache", "MemoryFree", "MemoryTotal", "MemoryTotalUsage", "MemoryUsed"] }, { "内存总体参数", ["MemoryCache", "MemoryFree", "MemoryTotal", "MemoryTotalUsage", "MemoryUsed"] },
{ "内存用户参数", ["MemoryUsage-*"] }, { "内存用户参数", ["MemoryUsage-*"] },
{ "Swap参数", ["SwapFree", "SwapTotal", "SwapTotalUsage", "SwapUsed"] }, { "Swap参数", ["SwapFree", "SwapTotal", "SwapTotalUsage", "SwapUsed"] },
{ "各个磁盘参数", ["DiskTps-*","DiskTps-*","DiskUtil-*","DiskWriteKB-*","DiskWriteKB-*","DiskAwait-*","DiskReadKB-*"] }, {
"各个磁盘参数",
[
"DiskTps-*", "DiskTps-*", "DiskUtil-*", "DiskWriteKB-*", "DiskWriteKB-*", "DiskAwait-*",
"DiskReadKB-*"
]
},
{ "磁盘总体参数", ["DiskTotalUsage"] }, { "磁盘总体参数", ["DiskTotalUsage"] },
{ "网络总体参数", ["InterfaceTotalUtilizationPercentage"] }, { "网络总体参数", ["InterfaceTotalUtilizationPercentage"] },
{ "各个网络接口参数", ["InterfaceUtilizationPercentage-*","InterfaceUtilizationPercentage-*","ReceivedPacketsPerSecond-*","TransmittedPacketsPerSecond-*"] }, {
"各个网络接口参数",
[
"InterfaceUtilizationPercentage-*", "InterfaceUtilizationPercentage-*",
"ReceivedPacketsPerSecond-*", "TransmittedPacketsPerSecond-*"
]
},
{ "进程", ["PhrasePatternCount", "ProcessTotalCount", "ThreadsTotalCount"] }, { "进程", ["PhrasePatternCount", "ProcessTotalCount", "ThreadsTotalCount"] },
{ "用户进程", ["UserProcesses-*"] }, { "用户进程", ["UserProcesses-*"] }
}; };
var filteredData = dbContext.ServerMonitoringData var filteredData = dbContext.ServerMonitoringData
.Where(x => string.IsNullOrEmpty(serverId) || x.ServerId == serverId) .Where(x => string.IsNullOrEmpty(serverId) || x.ServerId == serverId)
@ -36,7 +47,8 @@ public class JobController(ApplicationDbContext dbContext) : ControllerBase
{ {
x.DataName, x.DataName,
x.DataType, x.DataType,
GroupName = wildcardRules.FirstOrDefault(rule => x.DataType != null && IsMatch(x.DataType, rule.Value)).Key ?? "其他" GroupName = wildcardRules.FirstOrDefault(rule => x.DataType != null && IsMatch(x.DataType, rule.Value))
.Key ?? "其他"
}) })
.GroupBy(x => x.GroupName) .GroupBy(x => x.GroupName)
.Select(group => new .Select(group => new
@ -45,12 +57,12 @@ public class JobController(ApplicationDbContext dbContext) : ControllerBase
Items = group.ToList() Items = group.ToList()
}) })
.ToList(); .ToList();
return Ok(groupedData); return Ok(groupedData);
} }
private bool IsMatch(string input, List<string> patterns) private bool IsMatch(string input, List<string> patterns)
{ {
return patterns.Select(pattern => "^" + Regex.Escape(pattern).Replace("\\*", ".*") + "$").Any(regexPattern => Regex.IsMatch(input, regexPattern)); return patterns.Select(pattern => "^" + Regex.Escape(pattern).Replace("\\*", ".*") + "$")
.Any(regexPattern => Regex.IsMatch(input, regexPattern));
} }
} }

View File

@ -1,5 +1,4 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
namespace LoongPanel_Asp.Controllers; namespace LoongPanel_Asp.Controllers;
@ -10,22 +9,13 @@ public class PublicFileController(IWebHostEnvironment webHostEnvironment) : Cont
[HttpPost("UploadImage")] [HttpPost("UploadImage")]
public async Task<IActionResult> UploadImage(IFormFile? file) public async Task<IActionResult> UploadImage(IFormFile? file)
{ {
if (file == null || file.Length == 0) if (file == null || file.Length == 0) return BadRequest("文件不能为空");
{
return BadRequest("文件不能为空");
}
var allowedExtensions = new[] { ".jpg", ".jpeg", ".png", ".gif" }; var allowedExtensions = new[] { ".jpg", ".jpeg", ".png", ".gif" };
var extension = Path.GetExtension(file.FileName).ToLowerInvariant(); var extension = Path.GetExtension(file.FileName).ToLowerInvariant();
Console.WriteLine(extension); Console.WriteLine(extension);
if (!allowedExtensions.Contains(extension)) if (!allowedExtensions.Contains(extension)) return BadRequest("不支持的文件类型");
{
return BadRequest("不支持的文件类型");
}
var uploadsFolderPath = Path.Combine(webHostEnvironment.WebRootPath, "public/image"); var uploadsFolderPath = Path.Combine(webHostEnvironment.WebRootPath, "public/image");
if (!Directory.Exists(uploadsFolderPath)) if (!Directory.Exists(uploadsFolderPath)) Directory.CreateDirectory(uploadsFolderPath);
{
Directory.CreateDirectory(uploadsFolderPath);
}
var uniqueFileName = $"{Guid.NewGuid()}{extension}"; var uniqueFileName = $"{Guid.NewGuid()}{extension}";
var filePath = Path.Combine(uploadsFolderPath, uniqueFileName); var filePath = Path.Combine(uploadsFolderPath, uniqueFileName);

View File

@ -1,6 +1,5 @@
using System.Security.Claims; using System.Security.Claims;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using LoongPanel_Asp.Helpers;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -20,12 +19,10 @@ public class RoteController(
var role = await roleManager.FindByNameAsync(roleId); var role = await roleManager.FindByNameAsync(roleId);
var rotes = role!.RouterPermissions.ToList(); var rotes = role!.RouterPermissions.ToList();
//获取路由列表 //获取路由列表
var apiPermissions = dbContext.RotePermissions.ToList().Where(x => rotes.Any(y => y == x.Id.ToString())).Select(x=>x.Router).ToList(); var apiPermissions = dbContext.RotePermissions.ToList().Where(x => rotes.Any(y => y == x.Id.ToString()))
.Select(x => x.Router).ToList();
//如果rotes 中包括* //如果rotes 中包括*
if (rotes.Contains("*")) if (rotes.Contains("*")) return Ok("权限验证通过");
{
return Ok("权限验证通过");
}
//将path全部小写 //将path全部小写
path = path.ToLower(); path = path.ToLower();
//使用正则匹配 //使用正则匹配

View File

@ -1,17 +1,12 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Specialized;
using System.Dynamic; using System.Dynamic;
using System.Globalization; using System.Globalization;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using LoongPanel_Asp.Helpers; using LoongPanel_Asp.Helpers;
using LoongPanel_Asp.Models;
using LoongPanel_Asp.Servers; using LoongPanel_Asp.Servers;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
namespace LoongPanel_Asp.Controllers; namespace LoongPanel_Asp.Controllers;
@ -177,7 +172,8 @@ public class ServerController(IServiceProvider serviceProvider, ApplicationDbCon
var serverConfigs = JobConfigHelper.GetServers().ToList(); var serverConfigs = JobConfigHelper.GetServers().ToList();
var server = serverConfigs.Find(x => x.Id == serverId); var server = serverConfigs.Find(x => x.Id == serverId);
if (server == null) return BadRequest(); if (server == null) return BadRequest();
var output = await sshClient?.ExecuteCommandAsync(false, serverId,"echo",$"'{server.Password}'","|","sudo","-S","lshw","-class","memory","-json")!; var output = await sshClient?.ExecuteCommandAsync(false, serverId, "echo", $"'{server.Password}'", "|", "sudo",
"-S", "lshw", "-class", "memory", "-json")!;
if (string.IsNullOrEmpty(output)) return BadRequest(); if (string.IsNullOrEmpty(output)) return BadRequest();
return Ok(output); return Ok(output);
} }
@ -190,10 +186,12 @@ public class ServerController(IServiceProvider serviceProvider, ApplicationDbCon
var serverConfigs = JobConfigHelper.GetServers().ToList(); var serverConfigs = JobConfigHelper.GetServers().ToList();
var server = serverConfigs.Find(x => x.Id == serverId); var server = serverConfigs.Find(x => x.Id == serverId);
if (server == null) return BadRequest(); if (server == null) return BadRequest();
var output = await sshClient?.ExecuteCommandAsync(serverId,$"echo {server.Password}","|","sudo -S /usr/sbin/fdisk -l","|","grep 'Disk /'","|","awk '{print $2,$3}'")!; var output = await sshClient?.ExecuteCommandAsync(serverId, $"echo {server.Password}", "|",
"sudo -S /usr/sbin/fdisk -l", "|", "grep 'Disk /'", "|", "awk '{print $2,$3}'")!;
if (string.IsNullOrEmpty(output)) return BadRequest(output); if (string.IsNullOrEmpty(output)) return BadRequest(output);
var diskList = output.Split("\n", StringSplitOptions.RemoveEmptyEntries); var diskList = output.Split("\n", StringSplitOptions.RemoveEmptyEntries);
var outList = diskList.Select(disk => disk.Split(":", StringSplitOptions.RemoveEmptyEntries)).Select(info => new { name = info[0], size = info[1] }).ToList(); var outList = diskList.Select(disk => disk.Split(":", StringSplitOptions.RemoveEmptyEntries))
.Select(info => new { name = info[0], size = info[1] }).ToList();
return Ok(outList); return Ok(outList);
} }
@ -204,13 +202,16 @@ public class ServerController(IServiceProvider serviceProvider, ApplicationDbCon
var serverConfigs = JobConfigHelper.GetServers().ToList(); var serverConfigs = JobConfigHelper.GetServers().ToList();
var server = serverConfigs.Find(x => x.Id == serverId); var server = serverConfigs.Find(x => x.Id == serverId);
if (server == null) return BadRequest(); if (server == null) return BadRequest();
var output = await sshClient?.ExecuteCommandAsync(serverId,"ip link show" ,"|","grep '^[0-9]'","|","awk -F': ' '{print $2}'")!; var output = await sshClient?.ExecuteCommandAsync(serverId, "ip link show", "|", "grep '^[0-9]'", "|",
"awk -F': ' '{print $2}'")!;
if (string.IsNullOrEmpty(output)) return BadRequest(); if (string.IsNullOrEmpty(output)) return BadRequest();
var data = output.Split('\n', StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList(); var data = output.Split('\n', StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList();
return Ok(data); return Ok(data);
} }
[HttpGet("GetServerNetworkEquipmentInfo")] [HttpGet("GetServerNetworkEquipmentInfo")]
public async Task<IActionResult> GetServerNetworkEquipmentInfo([FromQuery] string serverId,[FromQuery] string networkId) public async Task<IActionResult> GetServerNetworkEquipmentInfo([FromQuery] string serverId,
[FromQuery] string networkId)
{ {
var sshClient = serviceProvider.GetService<SshService>(); var sshClient = serviceProvider.GetService<SshService>();
var serverConfigs = JobConfigHelper.GetServers().ToList(); var serverConfigs = JobConfigHelper.GetServers().ToList();
@ -222,6 +223,7 @@ public class ServerController(IServiceProvider serviceProvider, ApplicationDbCon
var data = output.Split(" ", StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).Skip(1).ToList(); var data = output.Split(" ", StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).Skip(1).ToList();
return Ok(data); return Ok(data);
} }
[HttpGet("GetServerGpuList")] [HttpGet("GetServerGpuList")]
public async Task<IActionResult> GetServerGpuList([FromQuery] string serverId) public async Task<IActionResult> GetServerGpuList([FromQuery] string serverId)
{ {
@ -248,13 +250,13 @@ public class ServerController(IServiceProvider serviceProvider, ApplicationDbCon
else if (diskId.StartsWith("sd")) type = "ata"; else if (diskId.StartsWith("sd")) type = "ata";
else if (diskId.StartsWith("hd")) type = "ata"; else if (diskId.StartsWith("hd")) type = "ata";
diskId = $"/dev/{diskId}"; diskId = $"/dev/{diskId}";
var output = await sshClient?.ExecuteCommandAsync(serverId,$"echo '{server.Password}'","|","sudo -S","/usr/sbin/smartctl -i",diskId,$"-d {type}","-T permissive","2>/dev/null","|","awk 'NR>4'")!; var output = await sshClient?.ExecuteCommandAsync(serverId, $"echo '{server.Password}'", "|", "sudo -S",
"/usr/sbin/smartctl -i", diskId, $"-d {type}", "-T permissive", "2>/dev/null", "|", "awk 'NR>4'")!;
if (string.IsNullOrEmpty(output)) return BadRequest(output); if (string.IsNullOrEmpty(output)) return BadRequest(output);
if (output.Contains("=== START OF INFORMATION SECTION ===")) if (output.Contains("=== START OF INFORMATION SECTION ==="))
{
//截断 //截断
output = output.Substring(output.IndexOf("=== START OF INFORMATION SECTION ===", StringComparison.Ordinal) + "=== START OF INFORMATION SECTION ===".Length); output = output.Substring(output.IndexOf("=== START OF INFORMATION SECTION ===", StringComparison.Ordinal) +
} "=== START OF INFORMATION SECTION ===".Length);
var diskInfo = output var diskInfo = output
.Split('\n', StringSplitOptions.RemoveEmptyEntries) .Split('\n', StringSplitOptions.RemoveEmptyEntries)
.Select(line => line.Trim().Split(':')) .Select(line => line.Trim().Split(':'))
@ -273,12 +275,16 @@ public class ServerController(IServiceProvider serviceProvider, ApplicationDbCon
var serverConfigs = JobConfigHelper.GetServers().ToList(); var serverConfigs = JobConfigHelper.GetServers().ToList();
var server = serverConfigs.Find(x => x.Id == serverId); var server = serverConfigs.Find(x => x.Id == serverId);
if (server == null) return BadRequest(); if (server == null) return BadRequest();
var output = await sshClient?.ExecuteCommandAsync(serverId,"awk -F: '$1 != \"nobody\" &&$1 != \"build\" {print $1 \":\"$3}' /etc/passwd | sort -t: -k2nr")!; var output = await sshClient?.ExecuteCommandAsync(serverId,
"awk -F: '$1 != \"nobody\" &&$1 != \"build\" {print $1 \":\"$3}' /etc/passwd | sort -t: -k2nr")!;
if (string.IsNullOrEmpty(output)) return BadRequest("无法获得用户树"); if (string.IsNullOrEmpty(output)) return BadRequest("无法获得用户树");
var data = output.Split('\n', StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList(); var data = output.Split('\n', StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList();
output = await sshClient?.ExecuteCommandAsync(serverId,"w -husf","|"," awk '$2 !~ /^tty/ {print$1, $2}'"," |"," sort ","|"," uniq")!; output = await sshClient?.ExecuteCommandAsync(serverId, "w -husf", "|", " awk '$2 !~ /^tty/ {print$1, $2}'",
" |", " sort ", "|", " uniq")!;
var onlineList = output.Split('\n', StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList(); var onlineList = output.Split('\n', StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList();
output = await sshClient?.ExecuteCommandAsync(serverId,"lastlog | awk 'NR > 1 { if ($2 ~ /^**Never/) {print $1, \"-\",\"NULL\"} else {print $1,$2, substr($0,index($0,$3))}}' ")!; output = await sshClient?.ExecuteCommandAsync(serverId,
"lastlog | awk 'NR > 1 { if ($2 ~ /^**Never/) {print $1, \"-\",\"NULL\"} else {print $1,$2, substr($0,index($0,$3))}}' ")
!;
if (string.IsNullOrEmpty(output)) return BadRequest("无法获得用户登录记录"); if (string.IsNullOrEmpty(output)) return BadRequest("无法获得用户登录记录");
var loginList = output.Split('\n', StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList(); var loginList = output.Split('\n', StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList();
var serverUserList = new List<ServerUserInfo>(); var serverUserList = new List<ServerUserInfo>();
@ -321,10 +327,12 @@ public class ServerController(IServiceProvider serviceProvider, ApplicationDbCon
out var dateTimeOffset); out var dateTimeOffset);
time = dateTimeOffset.ToString("G"); time = dateTimeOffset.ToString("G");
} }
d.LastLoginTime = time; d.LastLoginTime = time;
d.Port = port; d.Port = port;
}); });
serverUserList = serverUserList.OrderByDescending(x => x.IsOnline).ThenByDescending(x => x.LastLoginTime).ToList(); serverUserList = serverUserList.OrderByDescending(x => x.IsOnline).ThenByDescending(x => x.LastLoginTime)
.ToList();
return Ok(serverUserList); return Ok(serverUserList);
} }
@ -356,7 +364,8 @@ public class ServerController(IServiceProvider serviceProvider, ApplicationDbCon
} }
[HttpGet("GetServerProcessesKill")] [HttpGet("GetServerProcessesKill")]
public async Task<IActionResult> GetServerProcessesKill([FromQuery] string serverId, [FromQuery] string pid,[FromQuery] bool force=false) public async Task<IActionResult> GetServerProcessesKill([FromQuery] string serverId, [FromQuery] string pid,
[FromQuery] bool force = false)
{ {
var sshClient = serviceProvider.GetService<SshService>(); var sshClient = serviceProvider.GetService<SshService>();
var serverConfigs = JobConfigHelper.GetServers().ToList(); var serverConfigs = JobConfigHelper.GetServers().ToList();
@ -366,6 +375,7 @@ public class ServerController(IServiceProvider serviceProvider, ApplicationDbCon
$"echo {server.Password}", "|", "sudo -SS", "kill", force ? "-9" : "-15", pid)!; $"echo {server.Password}", "|", "sudo -SS", "kill", force ? "-9" : "-15", pid)!;
return Ok($"关闭信号已发送,{output}"); return Ok($"关闭信号已发送,{output}");
} }
[HttpGet("GetServerNetworkList")] [HttpGet("GetServerNetworkList")]
public async Task<IActionResult> GetServerNetworkList([FromQuery] string serverId, [FromQuery] string? userName) public async Task<IActionResult> GetServerNetworkList([FromQuery] string serverId, [FromQuery] string? userName)
{ {
@ -419,10 +429,9 @@ public class ServerController(IServiceProvider serviceProvider, ApplicationDbCon
var directoryPath = Path.GetDirectoryName(path); var directoryPath = Path.GetDirectoryName(path);
if (!Directory.Exists(directoryPath)) if (!Directory.Exists(directoryPath))
{
// 不存在则创建目录 // 不存在则创建目录
if (directoryPath != null) Directory.CreateDirectory(directoryPath); if (directoryPath != null)
} Directory.CreateDirectory(directoryPath);
// 检查文件是否存在 // 检查文件是否存在
if (System.IO.File.Exists(path)) if (System.IO.File.Exists(path))
@ -430,7 +439,8 @@ public class ServerController(IServiceProvider serviceProvider, ApplicationDbCon
// 读取现有JSON文件 // 读取现有JSON文件
var json = await System.IO.File.ReadAllTextAsync(path); var json = await System.IO.File.ReadAllTextAsync(path);
// 反序列化JSON为动态对象 // 反序列化JSON为动态对象
dynamic existingWord = Newtonsoft.Json.JsonConvert.DeserializeObject(json) ?? new ExpandoObject();; dynamic existingWord = JsonConvert.DeserializeObject(json) ?? new ExpandoObject();
;
// 使用内部的createAt更新createAt // 使用内部的createAt更新createAt
createAt = existingWord.createAt; createAt = existingWord.createAt;
@ -448,7 +458,7 @@ public class ServerController(IServiceProvider serviceProvider, ApplicationDbCon
LastModifyAt = lastModifyAt LastModifyAt = lastModifyAt
}; };
// 将新的配置对象序列化为JSON // 将新的配置对象序列化为JSON
var newJson = Newtonsoft.Json.JsonConvert.SerializeObject(newWord); var newJson = JsonConvert.SerializeObject(newWord);
// 覆盖写入新的JSON配置 // 覆盖写入新的JSON配置
await System.IO.File.WriteAllTextAsync(path, newJson); await System.IO.File.WriteAllTextAsync(path, newJson);
@ -461,10 +471,7 @@ public class ServerController(IServiceProvider serviceProvider, ApplicationDbCon
public async Task<IActionResult> GetWordList([FromQuery] string serverId) public async Task<IActionResult> GetWordList([FromQuery] string serverId)
{ {
var path = Path.Combine(AppContext.BaseDirectory, "markdowns", serverId); var path = Path.Combine(AppContext.BaseDirectory, "markdowns", serverId);
if (!Directory.Exists(path)) if (!Directory.Exists(path)) Directory.CreateDirectory(path);
{
Directory.CreateDirectory(path);
}
var files = Directory.GetFiles(path); var files = Directory.GetFiles(path);
var wordList = new List<WordFileModel>(); var wordList = new List<WordFileModel>();
@ -472,7 +479,7 @@ public class ServerController(IServiceProvider serviceProvider, ApplicationDbCon
{ {
var fileInfo = new FileInfo(file); var fileInfo = new FileInfo(file);
var json = await System.IO.File.ReadAllTextAsync(file); var json = await System.IO.File.ReadAllTextAsync(file);
var word = Newtonsoft.Json.JsonConvert.DeserializeObject<WordFileModel>(json); var word = JsonConvert.DeserializeObject<WordFileModel>(json);
//去除content //去除content
if (word == null) continue; if (word == null) continue;
word.Content = null; word.Content = null;
@ -480,6 +487,7 @@ public class ServerController(IServiceProvider serviceProvider, ApplicationDbCon
word.FileSize = fileSize.ToString(); word.FileSize = fileSize.ToString();
wordList.Add(word); wordList.Add(word);
} }
return Ok(wordList); return Ok(wordList);
} }
@ -488,10 +496,10 @@ public class ServerController(IServiceProvider serviceProvider, ApplicationDbCon
{ {
var path = Path.Combine(AppContext.BaseDirectory, "markdowns", serverId, wordId + ".json"); var path = Path.Combine(AppContext.BaseDirectory, "markdowns", serverId, wordId + ".json");
var json = await System.IO.File.ReadAllTextAsync(path); var json = await System.IO.File.ReadAllTextAsync(path);
var word = Newtonsoft.Json.JsonConvert.DeserializeObject<WordFileModel>(json); var word = JsonConvert.DeserializeObject<WordFileModel>(json);
return Ok(word); return Ok(word);
} }
[HttpGet("GetWordTemplates")] [HttpGet("GetWordTemplates")]
public async Task<IActionResult> GetWordTemplates() public async Task<IActionResult> GetWordTemplates()
{ {

View File

@ -36,7 +36,7 @@ public class UserController(UserManager<ApplicationUser> userManager) : Controll
user.Avatar, user.Avatar,
user.Desc, user.Desc,
user.Posts, user.Posts,
Role = roles[0], Role = roles[0]
}; };
// 返回用户信息 // 返回用户信息

View File

@ -1,63 +1,45 @@
using LoongPanel_Asp.Hubs; namespace LoongPanel_Asp.Helpers;
using Microsoft.AspNetCore.SignalR;
namespace LoongPanel_Asp.Helpers; public static class DataHelper
public class DataHelper(ApplicationDbContext dbContext,IHubContext<SessionHub> context)
{ {
public async Task SaveData(ServerMonitoringData data) public static void AddMonitoringData(List<ServerMonitoringData> res, string data, string dataName, string dataType)
{ {
// 保存数据到数据库 res.Add(new ServerMonitoringData
var dataDb = dbContext.ServerMonitoringData;
//获取当前时间
var time = DateTime.UtcNow;
data.Time = time;
dataDb.Add(data);
//提交
await dbContext.SaveChangesAsync();
}
//批量添加
public async Task SaveData(List<ServerMonitoringData> data)
{ {
var dataDb = dbContext.ServerMonitoringData; ServerId = null,
var time = DateTime.UtcNow; Data = data,
foreach (var i in data) DataName = dataName,
{ DataType = dataType
i.Time = time; });
dataDb.Add(i);
}
await dbContext.SaveChangesAsync();
}
public async Task CheckData(string serverId,string valueType,string value,string valueName)
{
var alertConfigs = JobConfigHelper.GetAlerts();
if (!alertConfigs.TryGetValue(serverId, out var serverAlert)) return;
serverAlert.Notify.TryGetValue(valueType, out var notifyValuePairs);
serverAlert.Warning.TryGetValue(valueType, out var warningValuePairs);
var matchingValues = warningValuePairs?.Where(pair => double.Parse(value) >= double.Parse(pair.Key))
.SelectMany(pair => pair.Value).Distinct().ToList();
if (matchingValues?.Count > 0)
{
foreach (var item in matchingValues)
{
await context.Clients.Group(item).SendAsync("ReceiveWaring", value, valueName);
var key=$"{serverId}_{valueType}+{item}";
}
return;
}
matchingValues = notifyValuePairs?.Where(pair => double.Parse(value) >= double.Parse(pair.Key))
.SelectMany(pair => pair.Value).Distinct().ToList();
if (matchingValues?.Count > 0)
{
foreach (var item in matchingValues)
{
await context.Clients.Group(item).SendAsync("ReceiveNotify", value, valueName);
}
}
} }
// public async Task CheckData(string serverId,string valueType,string value,string valueName)
// {
//
// var alertConfigs = JobConfigHelper.GetAlerts();
// if (!alertConfigs.TryGetValue(serverId, out var serverAlert)) return;
// serverAlert.Notify.TryGetValue(valueType, out var notifyValuePairs);
// serverAlert.Warning.TryGetValue(valueType, out var warningValuePairs);
// var matchingValues = warningValuePairs?.Where(pair => double.Parse(value) >= double.Parse(pair.Key))
// .SelectMany(pair => pair.Value).Distinct().ToList();
//
// if (matchingValues?.Count > 0)
// {
// foreach (var item in matchingValues)
// {
// await context.Clients.Group(item).SendAsync("ReceiveWaring", value, valueName);
// var key=$"{serverId}_{valueType}+{item}";
// }
// return;
// }
//
// matchingValues = notifyValuePairs?.Where(pair => double.Parse(value) >= double.Parse(pair.Key))
// .SelectMany(pair => pair.Value).Distinct().ToList();
// if (matchingValues?.Count > 0)
// {
// foreach (var item in matchingValues)
// {
// await context.Clients.Group(item).SendAsync("ReceiveNotify", value, valueName);
// }
// }
// }
} }

View File

@ -33,7 +33,7 @@ public static class JobConfigHelper
Description = section.Keys["Description"], Description = section.Keys["Description"],
CronExpression = section.Keys["CronExpression"], CronExpression = section.Keys["CronExpression"],
ValueName = section.Keys["ValueName"], ValueName = section.Keys["ValueName"],
Executor = section.Keys["Executor"].Split(',').Select(executor => Executor = section.Keys["Executor"]?.Split(',').Select(executor =>
{ {
// 查找serverConfigs 中id匹配 未找到则跳过 // 查找serverConfigs 中id匹配 未找到则跳过
var server = serverConfigs.FirstOrDefault(server => server.Id == executor); var server = serverConfigs.FirstOrDefault(server => server.Id == executor);
@ -111,6 +111,7 @@ public static class JobConfigHelper
warningTypes = []; warningTypes = [];
warningUsersMap.Add(serverId, warningTypes); warningUsersMap.Add(serverId, warningTypes);
} }
if (!notifyTypes.TryGetValue(type, out var notifyValues)) if (!notifyTypes.TryGetValue(type, out var notifyValues))
{ {
notifyValues = []; notifyValues = [];
@ -128,24 +129,29 @@ public static class JobConfigHelper
notifyUsersList = []; notifyUsersList = [];
notifyValues.Add(notifyValue, notifyUsersList); notifyValues.Add(notifyValue, notifyUsersList);
} }
if (!warningValues.TryGetValue(warningValue, out var warningUsersList)) if (!warningValues.TryGetValue(warningValue, out var warningUsersList))
{ {
warningUsersList = []; warningUsersList = [];
warningValues.Add(warningValue, warningUsersList); warningValues.Add(warningValue, warningUsersList);
} }
notifyUsersList.Add(userId); notifyUsersList.Add(userId);
warningUsersList.Add(userId); warningUsersList.Add(userId);
} }
} }
//遍历 notifyUsersMap //遍历 notifyUsersMap
foreach (var (serverId, notifyUsersList) in notifyUsersMap) foreach (var (serverId, notifyUsersList) in notifyUsersMap)
{ {
//获得key 和 value //获得key 和 value
if(!alertsConfigs.TryGetValue(serverId, out var alertsConfig)){ if (!alertsConfigs.TryGetValue(serverId, out var alertsConfig))
{
//创建新的 //创建新的
alertsConfig = new AlertConfiguration(); alertsConfig = new AlertConfiguration();
alertsConfigs.Add(serverId, alertsConfig); alertsConfigs.Add(serverId, alertsConfig);
} }
alertsConfig.Notify = notifyUsersList; alertsConfig.Notify = notifyUsersList;
} }
@ -158,6 +164,7 @@ public static class JobConfigHelper
alertsConfig = new AlertConfiguration(); alertsConfig = new AlertConfiguration();
alertsConfigs.Add(serverId, alertsConfig); alertsConfigs.Add(serverId, alertsConfig);
} }
alertsConfig.Warning = emailUsersList; alertsConfig.Warning = emailUsersList;
} }
@ -190,7 +197,7 @@ public class JobConfiguration
public string? Description { get; init; } public string? Description { get; init; }
//执行者 //执行者
public required List<ServerModel?> Executor { get; init; } public List<ServerModel?>? Executor { get; init; }
//任务类型 //任务类型
public required string JobType { get; init; } public required string JobType { get; init; }
@ -202,7 +209,8 @@ public class JobConfiguration
public string? ValueName { get; init; } public string? ValueName { get; init; }
} }
public class AlertConfiguration(Dictionary<string, Dictionary<string, List<string>>>? notify=null, public class AlertConfiguration(
Dictionary<string, Dictionary<string, List<string>>>? notify = null,
Dictionary<string, Dictionary<string, List<string>>>? warning = null) Dictionary<string, Dictionary<string, List<string>>>? warning = null)
{ {
public Dictionary<string, Dictionary<string, List<string>>> Notify { get; set; } = notify ?? []; public Dictionary<string, Dictionary<string, List<string>>> Notify { get; set; } = notify ?? [];

View File

@ -31,7 +31,7 @@ public class SessionHub(UserManager<ApplicationUser> userManager, ILiteDatabase
NickName = user.NickName!, NickName = user.NickName!,
Avatar = user.Avatar, Avatar = user.Avatar,
Role = role.ToList(), Role = role.ToList(),
Posts = user.Posts, Posts = user.Posts
}; };
@ -69,7 +69,6 @@ public class SessionHub(UserManager<ApplicationUser> userManager, ILiteDatabase
Console.WriteLine("12312312312{0},{1}", userId, message); Console.WriteLine("12312312312{0},{1}", userId, message);
await Clients.Group(receiver).SendAsync("sendMessage", userId, message); await Clients.Group(receiver).SendAsync("sendMessage", userId, message);
} }
} }
//定义类型UserInfo //定义类型UserInfo

View File

@ -6,18 +6,19 @@ namespace LoongPanel_Asp.Hubs;
public class TerminalHub(SshStreamService sshStreamService) : Hub public class TerminalHub(SshStreamService sshStreamService) : Hub
{ {
public override async Task OnConnectedAsync() public override async Task OnConnectedAsync()
{ {
var userId = Context.User!.Claims.First(x => x.Type == ClaimTypes.NameIdentifier).Value; var userId = Context.User!.Claims.First(x => x.Type == ClaimTypes.NameIdentifier).Value;
await Groups.AddToGroupAsync(Context.ConnectionId, userId); await Groups.AddToGroupAsync(Context.ConnectionId, userId);
} }
//create a terminal //create a terminal
public void CreateTerminal(string serverId) public void CreateTerminal(string serverId)
{ {
var userId = Context.User!.Claims.First(x => x.Type == ClaimTypes.NameIdentifier).Value; var userId = Context.User!.Claims.First(x => x.Type == ClaimTypes.NameIdentifier).Value;
sshStreamService.Connect(userId, serverId); sshStreamService.Connect(userId, serverId);
} }
//send a message to the terminal //send a message to the terminal
public void SendMessage(string message) public void SendMessage(string message)
{ {

View File

@ -1,14 +1,7 @@
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Threading;
using System.Threading.Tasks;
using LoongPanel_Asp.Helpers;
namespace LoongPanel_Asp namespace LoongPanel_Asp;
{
public class Init : IHostedService public class Init : IHostedService
{ {
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
@ -36,7 +29,7 @@ namespace LoongPanel_Asp
Email = "admin@admin.com", Email = "admin@admin.com",
UserName = "admin", UserName = "admin",
PhoneNumber = "999999999", PhoneNumber = "999999999",
NickName = "默认管理员", NickName = "默认管理员"
}; };
var result = await userManager.CreateAsync(adminUser, "Qwertyuiop123!@#"); var result = await userManager.CreateAsync(adminUser, "Qwertyuiop123!@#");
@ -47,11 +40,9 @@ namespace LoongPanel_Asp
Console.WriteLine("管理员创建成功,账号:{0},密码:{1}", "admin", "Qwertyuiop123!@#"); Console.WriteLine("管理员创建成功,账号:{0},密码:{1}", "admin", "Qwertyuiop123!@#");
else else
foreach (var error in result.Errors) foreach (var error in result.Errors)
{
Console.WriteLine(error.Description); Console.WriteLine(error.Description);
} }
} }
}
public Task StopAsync(CancellationToken cancellationToken) public Task StopAsync(CancellationToken cancellationToken)
{ {
@ -59,4 +50,3 @@ namespace LoongPanel_Asp
return Task.CompletedTask; return Task.CompletedTask;
} }
} }
}

View File

@ -1,183 +1,53 @@
using System.Globalization; using System.Globalization;
using LoongPanel_Asp.Helpers; using LoongPanel_Asp.Helpers;
using LoongPanel_Asp.Hubs;
using LoongPanel_Asp.Models; using LoongPanel_Asp.Models;
using LoongPanel_Asp.Servers; using LoongPanel_Asp.Servers;
using Microsoft.AspNetCore.SignalR;
using Quartz; using Quartz;
using Property = LoongPanel_Asp.Models.Property;
namespace LoongPanel_Asp.Jobs; namespace LoongPanel_Asp.Jobs;
public class CpuTotalJob( public class CpuTotalJob(DataService dataService, SshService sshService) : IiJob(dataService, sshService)
IHubContext<SessionHub> hubContext,
IServiceProvider serviceProvider,
ApplicationDbContext dbContext) : IJob
{ {
private static int _count; protected override async Task ExecuteInternal(IJobExecutionContext context, ServerModel server,
SshService sshService, List<ServerMonitoringData> res)
public async Task Execute(IJobExecutionContext context)
{ {
var dataMap = context.JobDetail.JobDataMap; var output = await sshService.ExecuteCommandAsync(server.Id, "sar", "-u", "3 1", "|", "grep", "Average");
var dataHelper = new DataHelper(dbContext,hubContext); if (string.IsNullOrEmpty(output)) return;
var serverList = (List<ServerModel>)dataMap["executor"]; var values = output.Split(' ', StringSplitOptions.RemoveEmptyEntries).Skip(2).ToList();
var cpuDataListAll = new List<ServerMonitoringData>(); DataHelper.AddMonitoringData(res, values[0], "CPU用户使用率", "CpuUserUsage");
var sshClient = serviceProvider.GetService<SshService>(); DataHelper.AddMonitoringData(res, values[2], "CPU系统使用率", "CpuSystemUsage");
foreach (var server in serverList) DataHelper.AddMonitoringData(res, values[3], "CPUIO等待率", "CpuIOWaitUsage");
{ DataHelper.AddMonitoringData(res, (100 - double.Parse(values[5])).ToString(CultureInfo.InvariantCulture),
var output = await sshClient?.ExecuteCommandAsync(server.Id, "sar", "-u", "3 1", "|", "grep", "Average")!; "CPU总使用率", "CpuTotalUsage");
if (string.IsNullOrEmpty(output)) continue;
output = output.Replace("Average:", "").Replace("all", "").TrimStart();
var cpuList = output.Split(' ', StringSplitOptions.RemoveEmptyEntries);
var cpuProperties = new List<Property>
{
new() { Name = "CpuUserUsage", DisplayName = "CPU用户使用率", Order = 0 },
new() { Name = "CpuSystemUsage", DisplayName = "CPU系统使用率", Order = 1 },
new() { Name = "CpuIOWaitUsage", DisplayName = "CPUIO等待使用率", Order = 2 }
};
var cpuDataList = cpuProperties.Select(property => new ServerMonitoringData { ServerId = server.Id, Data = cpuList[(int)property.Order!], DataName = property.DisplayName, DataType = property.Name }).ToList();
// Calculate CpuTotalUsage separately
var idleUsage = double.Parse(cpuList[5]);
var totalUsage = new ServerMonitoringData
{
ServerId = server.Id,
Data = (100 - idleUsage).ToString(CultureInfo.InvariantCulture),
DataName = "CPU总使用率",
DataType = "CpuTotalUsage"
};
cpuDataList.Add(totalUsage);
cpuDataList.ForEach(async data =>
{
await hubContext.Clients.All.SendAsync("ReceiveData", data.ServerId, data.DataType, data.Data);
await dataHelper.CheckData(server.Id, data.DataType??"", data.Data??"", data.DataName);
});
cpuDataListAll.AddRange(cpuDataList);
}
_count++;
if (_count <= 10) return;
_count = 0;
// Add to database
await dataHelper.SaveData(cpuDataListAll);
} }
} }
public class CpuSpeedJob( public class CpuSpeedJob(DataService dataService, SshService sshService) : IiJob(dataService, sshService)
IHubContext<SessionHub> hubContext,
IServiceProvider serviceProvider,
ApplicationDbContext dbContext) : IJob
{ {
private static int _count; protected override async Task ExecuteInternal(IJobExecutionContext context, ServerModel server,
SshService sshService, List<ServerMonitoringData> res)
public async Task Execute(IJobExecutionContext context)
{ {
var dataMap = context.JobDetail.JobDataMap; var output = await sshService.ExecuteCommandAsync(server.Id, "cat", "/proc/cpuinfo", "|", "grep", "'cpu MHz'");
var dataHelper = new DataHelper(dbContext,hubContext); if (string.IsNullOrEmpty(output)) return;
var serverList = (List<ServerModel>)dataMap["executor"]; var cpuSpeeds = output.Split('\n', StringSplitOptions.RemoveEmptyEntries)
var cpuDataListAll = new List<ServerMonitoringData>(); .Select(line => line.Split(':', 2)[1].Trim())
var sshClient = serviceProvider.GetService<SshService>(); .Where(speed => !string.IsNullOrEmpty(speed)).ToList();
foreach (var server in serverList) DataHelper.AddMonitoringData(res, cpuSpeeds[0], "CPU总频率", "CpuTotalSpeed");
{ cpuSpeeds.Skip(1).Select((speed, i) => (speed, i)).ToList().ForEach(x =>
var output = DataHelper.AddMonitoringData(res, x.speed, $"CPU{x.i + 1}频率", $"CpuSingleSpeed-{x.i + 1}"));
await sshClient?.ExecuteCommandAsync(server.Id, "cat", "/proc/cpuinfo", "|", "grep", "'cpu MHz'")!;
if (string.IsNullOrEmpty(output)) continue;
//切分每行
var cpuSpeedList = output.Split('\n', StringSplitOptions.RemoveEmptyEntries);
//获取第一行
var cpuTotalSpeed = cpuSpeedList[0].Split(':', StringSplitOptions.RemoveEmptyEntries)[1].Trim();
var cpuDataList = new List<ServerMonitoringData>();
var totalSpeed = new ServerMonitoringData
{
ServerId = server.Id,
Data = cpuTotalSpeed,
DataName = "CPU总速度",
DataType = "CpuTotalSpeed"
};
cpuDataList.Add(totalSpeed);
//遍历剩下的行
foreach (var (cpuSpeed, index) in cpuSpeedList.Skip(1).Select((x, index) => (x, index)))
{
var speed = cpuSpeed.Split(':', StringSplitOptions.RemoveEmptyEntries)[1].Trim();
var singleSpeed = new ServerMonitoringData
{
ServerId = server.Id,
Data = speed,
DataName = $"CPU单核速度-{index}",
DataType = $"CpuSingleSpeed-{index}"
};
cpuDataList.Add(singleSpeed);
}
cpuDataListAll.AddRange(cpuDataList);
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++; public class CpuSingleUsageJob(DataService dataService, SshService sshService) : IiJob(dataService, sshService)
if (_count <= 10) return;
_count = 0;
await dataHelper.SaveData(cpuDataListAll);
}
}
public class CpuSingleUsageJob(
IHubContext<SessionHub> hubContext,
IServiceProvider serviceProvider,
ApplicationDbContext dbContext) : IJob
{ {
private static int _count; protected override async Task ExecuteInternal(IJobExecutionContext context, ServerModel server,
SshService sshService, List<ServerMonitoringData> res)
public async Task Execute(IJobExecutionContext context)
{ {
var dataMap = context.JobDetail.JobDataMap; var output = await sshService.ExecuteCommandAsync(server.Id, "sar -P ALL 3 1", "|", "grep Average", "|",
var dataHelper = new DataHelper(dbContext,hubContext); "awk 'NR>2 {print 100-$NF}'");
var serverList = (List<ServerModel>)dataMap["executor"]; if (string.IsNullOrEmpty(output)) return;
var cpuDataListAll = new List<ServerMonitoringData>(); var values = output.Split('\n', StringSplitOptions.RemoveEmptyEntries);
foreach (var server in serverList) values.Select((value, i) => (value, i)).ToList().ForEach(x =>
{ DataHelper.AddMonitoringData(res, x.value, $"CPU{x.i}使用率", $"CpuSingleUsage-{x.i}"));
var sshClient = serviceProvider.GetService<SshService>();
var output =
await sshClient?.ExecuteCommandAsync(server.Id, "sar -P ALL 3 1", "|", "grep Average", "|",
"awk 'NR>2 {print 100-$NF}'")!;
if (string.IsNullOrEmpty(output)) continue;
var cpuDataList = new List<ServerMonitoringData>();
foreach (var (cpuUsage, index) in output.Split("\n", StringSplitOptions.RemoveEmptyEntries)
.Select((x, index) => (x, index)))
{
var singleUsage = new ServerMonitoringData
{
ServerId = server.Id,
Data = cpuUsage,
DataName = $"CPU单核使用率-{index}",
DataType = $"CpuSingleUsage-{index}"
};
cpuDataList.Add(singleUsage);
}
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++;
if (_count <= 10) return;
_count = 0;
await dataHelper.SaveData(cpuDataListAll);
} }
} }

View File

@ -1,137 +1,39 @@
using LoongPanel_Asp.Helpers; using LoongPanel_Asp.Helpers;
using LoongPanel_Asp.Hubs;
using LoongPanel_Asp.Models; using LoongPanel_Asp.Models;
using LoongPanel_Asp.Servers; using LoongPanel_Asp.Servers;
using Microsoft.AspNetCore.SignalR;
using Quartz; using Quartz;
namespace LoongPanel_Asp.Jobs; namespace LoongPanel_Asp.Jobs;
public class DiskTotalJob( public class DiskTotalJob(DataService dataService, SshService sshService) : IiJob(dataService, sshService)
IHubContext<SessionHub> hubContext,
IServiceProvider serviceProvider,
ApplicationDbContext dbContext) : IJob
{ {
private static int _count; protected override async Task ExecuteInternal(IJobExecutionContext context, ServerModel server,
SshService sshService, List<ServerMonitoringData> res)
public async Task Execute(IJobExecutionContext context)
{ {
var dataMap = context.JobDetail.JobDataMap; var output = await sshService.ExecuteCommandAsync(server.Id, "df", "--total", "|", "grep", "total");
var dataHelper = new DataHelper(dbContext,hubContext);
var serverList = (List<ServerModel>)dataMap["executor"];
//获得cpu信息
var diskDataListAll = new List<ServerMonitoringData>();
foreach (var server in serverList)
{
var sshClient = serviceProvider.GetService<SshService>();
var diskDataList = new List<ServerMonitoringData>();
var output = await sshClient?.ExecuteCommandAsync(server.Id, "df", "--total", "|", "grep", "total")!;
if (string.IsNullOrEmpty(output)) return; if (string.IsNullOrEmpty(output)) return;
var disk = output.Split(" ", StringSplitOptions.RemoveEmptyEntries); var diskTotal = output.Split(" ", StringSplitOptions.RemoveEmptyEntries)[4].Replace("%", "");
var diskTotalUsage = new ServerMonitoringData DataHelper.AddMonitoringData(res, diskTotal, "磁盘总使用率", "DiskTotalUsage");
{
ServerId = server.Id,
Data = disk[4].Replace("%", ""),
DataName = "磁盘总使用率",
DataType = "DiskTotalUsage"
};
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);
}
_count++;
if (_count <= 10) return;
_count = 0;
await dataHelper.SaveData(diskDataListAll);
} }
} }
public class DiskUseJob( public class DiskUseJob(DataService dataService, SshService sshService) : IiJob(dataService, sshService)
IHubContext<SessionHub> hubContext,
IServiceProvider serviceProvider,
ApplicationDbContext dbContext) : IJob
{ {
private static int _count; protected override async Task ExecuteInternal(IJobExecutionContext context, ServerModel server,
SshService sshService, List<ServerMonitoringData> res)
public async Task Execute(IJobExecutionContext context)
{ {
var dataMap = context.JobDetail.JobDataMap; var output = await sshService.ExecuteCommandAsync(server.Id,
var dataHelper = new DataHelper(dbContext,hubContext); "sar -d 3 1 | grep Average: | awk 'NR>1' | awk '{$1=\"\";print$0}'");
var serverList = (List<ServerModel>)dataMap["executor"];
//获得cpu信息
var diskDataListAll = new List<ServerMonitoringData>();
foreach (var server in serverList)
{
var sshClient = serviceProvider.GetService<SshService>();
var diskDataList = new List<ServerMonitoringData>();
var output =
await sshClient?.ExecuteCommandAsync(server.Id, "sar -d 3 1", "|", "grep Average:", "|", "awk 'NR>1'","|","awk '{$1=\"\";print $0}'")!;
if (string.IsNullOrEmpty(output)) return; if (string.IsNullOrEmpty(output)) return;
var lines = output.Split("\n", StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList();
foreach (var line in lines) output.Split("\n").Select(x => x.Trim()).Where(line => !string.IsNullOrEmpty(line)).ToList().ForEach(line =>
{ {
var disk = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); var diskValues = line.Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList();
var dev = disk[0]; var diskName = diskValues[0];
// 每秒传输数 DataHelper.AddMonitoringData(res, diskValues[1], $"磁盘每秒传输数-{diskName}", $"DiskTps-{diskName}");
var diskTps = new ServerMonitoringData DataHelper.AddMonitoringData(res, diskValues[2], $"磁盘每秒读数-{diskName}", $"DiskReadKB-{diskName}");
{ DataHelper.AddMonitoringData(res, diskValues[3], $"磁盘每秒写数-{diskName}", $"DiskWriteKB-{diskName}");
ServerId = server.Id, DataHelper.AddMonitoringData(res, diskValues[8], $"磁盘利用率-{diskName}", $"DiskUtil-{diskName}");
Data = disk[1], });
DataName = $"磁盘每秒传输数-{dev}" ,
DataType = $"DiskTps-{dev}"
};
diskDataList.Add(diskTps);
var diskReadKb = new ServerMonitoringData
{
ServerId = server.Id,
Data = disk[2],
DataName = $"磁盘每秒读取数据量-{dev}" ,
DataType = $"DiskReadKB-{dev}"
};
diskDataList.Add(diskReadKb);
var diskWriteKb = new ServerMonitoringData
{
ServerId = server.Id,
Data = disk[3],
DataName = $"磁盘每秒写入数据量-{dev}" ,
DataType = $"DiskWriteKB-{dev}"
};
diskDataList.Add(diskWriteKb);
var diskAwait = new ServerMonitoringData
{
ServerId = server.Id,
Data = disk[7],
DataName = $"磁盘平均等待时间-{dev}" ,
DataType = $"DiskAwait-{dev}"
};
diskDataList.Add(diskAwait);
var diskUtil = new ServerMonitoringData
{
ServerId = server.Id,
Data = disk[8],
DataName = $"磁盘利用率-{dev}" ,
DataType = $"DiskUtil-{dev}"
};
diskDataList.Add(diskUtil);
diskDataList.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);
}
}
diskDataListAll.AddRange(diskDataList);
}
_count++;
if (_count <= 10) return;
_count = 0;
await dataHelper.SaveData(diskDataListAll);
} }
} }

View File

@ -0,0 +1,40 @@
using LoongPanel_Asp.Models;
using LoongPanel_Asp.Servers;
using Quartz;
namespace LoongPanel_Asp.Jobs;
public abstract class IiJob(
DataService dataService,
SshService sshService)
: IJob
{
public async Task Execute(IJobExecutionContext context)
{
var dataMap = context.JobDetail.JobDataMap;
var serverList = (List<ServerModel>)dataMap["executor"];
var res = new List<ServerMonitoringData>();
foreach (var server in serverList)
{
var r = new List<ServerMonitoringData>();
await ExecuteInternal(context, server, sshService, r);
r.ForEach(x => x.ServerId = server.Id);
res.AddRange(r);
}
await dataService.Save(res);
}
protected abstract Task ExecuteInternal(IJobExecutionContext context, ServerModel server, SshService sshService,
List<ServerMonitoringData> res);
}
//DataService Submit
public class DataJob(DataService service) : IJob
{
public Task Execute(IJobExecutionContext context)
{
_ = service.Submit();
return Task.CompletedTask;
}
}

View File

@ -1,99 +1,35 @@
using System.Globalization; using System.Globalization;
using LoongPanel_Asp.Helpers; using LoongPanel_Asp.Helpers;
using LoongPanel_Asp.Hubs;
using LoongPanel_Asp.Models; using LoongPanel_Asp.Models;
using LoongPanel_Asp.Servers; using LoongPanel_Asp.Servers;
using Microsoft.AspNetCore.SignalR;
using Quartz; using Quartz;
namespace LoongPanel_Asp.Jobs; namespace LoongPanel_Asp.Jobs;
public class MemoryTotalJob( public class MemoryTotalJob(DataService dataService, SshService sshService) : IiJob(dataService, sshService)
IHubContext<SessionHub> hubContext,
IServiceProvider serviceProvider,
ApplicationDbContext dbContext) : IJob
{ {
private static int _count; protected override async Task ExecuteInternal(IJobExecutionContext context, ServerModel server,
SshService sshService, List<ServerMonitoringData> res)
{
var output =
await sshService.ExecuteCommandAsync(server.Id, "free -w | awk 'NR>1' | awk '{$1=\"\";print$0}' | xargs");
var values = output.Split(" ", StringSplitOptions.RemoveEmptyEntries);
var memoryTotal = values[0];
var memoryUsed = values[1];
var memoryFree = values[2];
var swapTotal = values[6];
var swapUsed = values[7];
DataHelper.AddMonitoringData(res, memoryUsed, "内存使用量", "MemoryUsed");
DataHelper.AddMonitoringData(res, memoryFree, "内存空闲量", "MemoryFree");
var memoryUsedPercent = Math.Round(double.Parse(memoryUsed) / double.Parse(memoryTotal) * 100, 2)
.ToString(CultureInfo.InvariantCulture);
DataHelper.AddMonitoringData(res, memoryUsedPercent, "内存总使用率", "MemoryTotalUsage");
public async Task Execute(IJobExecutionContext context) if (double.Parse(swapTotal) > 0)
{ {
var dataMap = context.JobDetail.JobDataMap; var swapUsedPercent = Math.Round(double.Parse(swapUsed) / double.Parse(swapTotal) * 100, 2)
var dataHelper = new DataHelper(dbContext,hubContext); .ToString(CultureInfo.InvariantCulture);
// 从JobDataMap中获取参数 DataHelper.AddMonitoringData(res, swapUsedPercent, "Swap总使用率", "SwapTotalUsage");
var serverList = (List<ServerModel>)dataMap["executor"];
var serverDataListAll = new List<ServerMonitoringData>();
foreach (var server in serverList)
{
var sshClient = serviceProvider.GetService<SshService>();
var output = await sshClient?.ExecuteCommandAsync(server.Id, "free -w", "|", "awk 'NR>1'", "|",
"awk '{$1=\"\";print $0}'", "|", "xargs")!;
if (string.IsNullOrEmpty(output)) continue;
var serverDataList = new List<ServerMonitoringData>();
var dataList = output.Split(" ", StringSplitOptions.RemoveEmptyEntries);
var memoryProperties = new List<Property>
{
new() { Name = "MemoryTotal", DisplayName = "内存总量", Order = 0 },
new() { Name = "MemoryUsed", DisplayName = "内存使用量", Order = 1 },
new() { Name = "MemoryFree", DisplayName = "内存空闲量", Order = 2 },
new() { Name = "MemoryCache", DisplayName = "内存缓存量", Order = 5 },
new() { Name = "SwapTotal", DisplayName = "Swap总量", Order = 7 },
new() { Name = "SwapUsed", DisplayName = "Swap使用量", Order = 8 },
new() { Name = "SwapFree", DisplayName = "Swap空闲量", Order = 9 }
};
memoryProperties.ForEach(data =>
{
var d = new ServerMonitoringData
{
ServerId = server.Id,
Data = double.Parse(dataList[(int)data.Order!]).ToString(CultureInfo.CurrentCulture),
DataName = data.DisplayName,
DataType = data.Name
};
serverDataList.Add(d);
});
//计算内存使用率
var memoryUsed = double.Parse(dataList[1]);
var memoryTotal = double.Parse(dataList[0]);
if (memoryTotal <= 0) memoryTotal = 1;
var memoryUsedRate = memoryUsed / memoryTotal * 100;
var memoryData = new ServerMonitoringData
{
ServerId = server.Id,
Data = memoryUsedRate.ToString(CultureInfo.InvariantCulture),
DataName = "内存总使用率",
DataType = "MemoryTotalUsage"
};
serverDataList.Add(memoryData);
//计算交换使用率
var swapUsed = double.Parse(dataList[8]);
var swapTotal = double.Parse(dataList[7]);
if (swapTotal <= 0) swapTotal = 1;
var swapUsedRate = swapUsed / swapTotal * 100;
var swapData = new ServerMonitoringData
{
ServerId = server.Id,
Data = swapUsedRate.ToString(CultureInfo.InvariantCulture),
DataName = "Swap使用率",
DataType = "SwapTotalUsage"
};
serverDataList.Add(swapData);
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++;
if (_count <= 10) return;
_count = 0;
await dataHelper.SaveData(serverDataListAll);
}
} }

View File

@ -1,84 +1,33 @@
using System.Globalization; using System.Globalization;
using LoongPanel_Asp.Helpers; using LoongPanel_Asp.Helpers;
using LoongPanel_Asp.Hubs;
using LoongPanel_Asp.Models; using LoongPanel_Asp.Models;
using LoongPanel_Asp.Servers; using LoongPanel_Asp.Servers;
using Microsoft.AspNetCore.SignalR;
using Quartz; using Quartz;
namespace LoongPanel_Asp.Jobs; namespace LoongPanel_Asp.Jobs;
public class NetworkTotalJob( public class NetworkTotalJob(DataService dataService, SshService sshService) : IiJob(dataService, sshService)
IHubContext<SessionHub> hubContext,
IServiceProvider serviceProvider,
ApplicationDbContext dbContext) : IJob
{ {
private static int _count; protected override async Task ExecuteInternal(IJobExecutionContext context, ServerModel server,
SshService sshService, List<ServerMonitoringData> res)
public async Task Execute(IJobExecutionContext context)
{ {
var dataMap = context.JobDetail.JobDataMap; var output = await sshService.ExecuteCommandAsync(server.Id, "sar", "-n", "DEV", "3 1", "|", "grep", "Average:",
var dataHelper = new DataHelper(dbContext,hubContext); "|", "awk 'NR>1'", "|", "awk '{$1=\"\";print $0}'");
var serverList = (List<ServerModel>)dataMap["executor"]; if (string.IsNullOrEmpty(output)) return;
var netWorkDataListAll = new List<ServerMonitoringData>(); var lines = output.Split("\n", StringSplitOptions.RemoveEmptyEntries);
foreach (var server in serverList)
{
var sshClient = serviceProvider.GetService<SshService>();
var netWorkDataList = new List<ServerMonitoringData>();
var output = await sshClient?.ExecuteCommandAsync(server.Id, "sar", "-n", "DEV", "3 1", "|", "grep",
"Average:", "|", "awk 'NR>1'", "|", "awk '{$1=\"\";print $0}'")!;
if (string.IsNullOrEmpty(output)) continue;
var lines = output.Split("\n");
var totalUsage = 0.0; var totalUsage = 0.0;
foreach (var line in lines) foreach (var line in lines)
{ {
if (string.IsNullOrEmpty(line)) continue; if (string.IsNullOrEmpty(line)) continue;
var data = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); var values = line.Split(" ", StringSplitOptions.RemoveEmptyEntries);
var iFace = data[0]; var iFace = values[0];
var dataNum = data.Skip(1).Select(double.Parse).ToList(); DataHelper.AddMonitoringData(res, values[1], $"每秒钟接收到的数据包数量-{iFace}", $"NetWorkReceive-{iFace}");
var netWorkProperties = new List<Property> DataHelper.AddMonitoringData(res, values[2], $"每秒钟发送的数据包数量-{iFace}", $"NetWorkSend-{iFace}");
{ DataHelper.AddMonitoringData(res, values[8], $"网络接口的使用率-{iFace}", $"NetWorkUsage-{iFace}");
new() { Name = "ReceivedPacketsPerSecond", DisplayName = "每秒钟接收到的数据包数量", Order = 0 }, totalUsage += double.Parse(values[8]);
new() { Name = "TransmittedPacketsPerSecond", DisplayName = "每秒钟发送的数据包数量", Order = 1 },
new() { Name = "InterfaceUtilizationPercentage", DisplayName = "网络接口的使用率", Order = 7 }
};
netWorkProperties.ForEach(property =>
{
var d = new ServerMonitoringData
{
ServerId = server.Id,
Data = dataNum[(int)property.Order!].ToString(CultureInfo.InvariantCulture),
DataName = $"{property.DisplayName}-{iFace}",
DataType = $"{property.Name}-{iFace}"
};
netWorkDataList.Add(d);
});
totalUsage += dataNum[7];
} }
var d = new ServerMonitoringData DataHelper.AddMonitoringData(res, totalUsage.ToString(CultureInfo.InvariantCulture), "网络接口的使用率-总",
{ "NetWorkUsage");
ServerId = server.Id,
Data = totalUsage.ToString(CultureInfo.InvariantCulture),
DataName = "网络接口总体使用率",
DataType = "InterfaceTotalUtilizationPercentage"
};
netWorkDataList.Add(d);
netWorkDataListAll.AddRange(netWorkDataList);
netWorkDataList.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++;
if (_count <= 10) return;
_count = 0;
await dataHelper.SaveData(netWorkDataListAll);
} }
} }

View File

@ -1,106 +1,34 @@
using LoongPanel_Asp.Helpers; using LoongPanel_Asp.Helpers;
using LoongPanel_Asp.Hubs;
using LoongPanel_Asp.Models; using LoongPanel_Asp.Models;
using LoongPanel_Asp.Servers; using LoongPanel_Asp.Servers;
using Microsoft.AspNetCore.SignalR;
using Quartz; using Quartz;
namespace LoongPanel_Asp.Jobs; namespace LoongPanel_Asp.Jobs;
public class ProcessTotalJob( public class ProcessTotalJob(DataService dataService, SshService sshService) : IiJob(dataService, sshService)
IHubContext<SessionHub> hubContext,
IServiceProvider serviceProvider,
ApplicationDbContext dbContext) : IJob
{ {
private static int _count; protected override async Task ExecuteInternal(IJobExecutionContext context, ServerModel server,
SshService sshService, List<ServerMonitoringData> res)
public async Task Execute(IJobExecutionContext context)
{ {
var dataMap = context.JobDetail.JobDataMap; var output = await sshService.ExecuteCommandAsync(server.Id, "ps", "-e", "|", "wc", "-l");
var dataHelper = new DataHelper(dbContext,hubContext); if (string.IsNullOrEmpty(output)) return;
var serverList = (List<ServerModel>)dataMap["executor"]; var value = int.Parse(output).ToString();
var processDataListAll = new List<ServerMonitoringData>(); DataHelper.AddMonitoringData(res, value, "进程总数", "ProcessTotalCount");
var sshClient = serviceProvider.GetService<SshService>(); output = await sshService.ExecuteCommandAsync(server.Id, "ps", "-eLf", "|", "wc", "-l");
foreach (var server in serverList) if (string.IsNullOrEmpty(output)) return;
{ value = int.Parse(output).ToString();
var output = await sshClient?.ExecuteCommandAsync(server.Id, "ps", "-e", "|", "wc", "-l")!; DataHelper.AddMonitoringData(res, value, "线程总数", "ThreadTotalCount");
if (string.IsNullOrEmpty(output)) continue;
var processDataList = new List<ServerMonitoringData>();
var count = int.Parse(output);
var processTotalCount = new ServerMonitoringData
{
ServerId = server.Id,
Data = count.ToString(),
DataName = "进程总数",
DataType = "ProcessTotalCount"
};
processDataList.Add(processTotalCount);
output = await sshClient?.ExecuteCommandAsync(server.Id, "ps", "-eLf", "|", "wc", "-l")!;
if (string.IsNullOrEmpty(output)) continue;
count = int.Parse(output);
var threadsTotalCount = new ServerMonitoringData
{
ServerId = server.Id,
Data = count.ToString(),
DataName = "线程总数",
DataType = "ThreadsTotalCount"
};
processDataList.Add(threadsTotalCount);
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++; public class PhrasePatternJob(DataService dataService, SshService sshService) : IiJob(dataService, sshService)
if (_count <= 10) return;
_count = 0;
await dataHelper.SaveData(processDataListAll);
}
}
public class PhrasePatternJob(
IHubContext<SessionHub> hubContext,
IServiceProvider serviceProvider,
ApplicationDbContext dbContext) : IJob
{ {
private static int _count; protected override async Task ExecuteInternal(IJobExecutionContext context, ServerModel server,
SshService sshService, List<ServerMonitoringData> res)
public async Task Execute(IJobExecutionContext context)
{ {
var dataMap = context.JobDetail.JobDataMap; var output = await sshService.ExecuteCommandAsync(server.Id, "lsof", "|", "wc", "-l");
var dataHelper = new DataHelper(dbContext,hubContext); if (string.IsNullOrEmpty(output)) return;
var serverList = (List<ServerModel>)dataMap["executor"]; var value = int.Parse(output).ToString();
var processDataListAll = new List<ServerMonitoringData>(); DataHelper.AddMonitoringData(res, value, "句柄", "PhrasePatternCount");
var sshClient = serviceProvider.GetService<SshService>();
foreach (var server in serverList)
{
var output = await sshClient?.ExecuteCommandAsync(server.Id, "lsof", "|", "wc", "-l")!;
if (string.IsNullOrEmpty(output)) continue;
var count = int.Parse(output);
var phrasePatternCount = new ServerMonitoringData
{
ServerId = server.Id,
Data = count.ToString(),
DataName = "句柄",
DataType = "PhrasePatternCount"
};
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++;
if (_count <= 10) return;
_count = 0;
await dataHelper.SaveData(processDataListAll);
} }
} }

View File

@ -0,0 +1,18 @@
namespace LoongPanel_Asp.Jobs;
// public class SystemTimeIntervalLoadJob(DataService dataService, SshService sshService) : IiJob(dataService, sshService)
// {
// protected override async Task<List<ServerMonitoringData>?> ExecuteInternal(IJobExecutionContext context, ServerModel server, SshService sshService)
// {
// var output = await sshService?.ExecuteCommandAsync(server.Id, "sar", "-u", "3 1", "|", "grep", "Average")!;
// if (string.IsNullOrEmpty(output)) return null;
// var totalUsage = new ServerMonitoringData
// {
// ServerId = server.Id,
// Data = "20",
// DataName = "CPU总使用率",
// DataType = "CpuTotalUsage"
// };
// return new List<ServerMonitoringData> { totalUsage };
// }
// }

View File

@ -1,76 +1,27 @@
using LoongPanel_Asp.Helpers; using LoongPanel_Asp.Helpers;
using LoongPanel_Asp.Hubs;
using LoongPanel_Asp.Models; using LoongPanel_Asp.Models;
using LoongPanel_Asp.Servers; using LoongPanel_Asp.Servers;
using Microsoft.AspNetCore.SignalR;
using Quartz; using Quartz;
namespace LoongPanel_Asp.Jobs; namespace LoongPanel_Asp.Jobs;
public class UserTotalJob(IHubContext<SessionHub> hubContext, public class UserTotalJob(DataService dataService, SshService sshService) : IiJob(dataService, sshService)
IServiceProvider serviceProvider,
ApplicationDbContext dbContext): IJob
{ {
private static int _count; protected override async Task ExecuteInternal(IJobExecutionContext context, ServerModel server,
public async Task Execute(IJobExecutionContext context) SshService sshService, List<ServerMonitoringData> res)
{ {
// 执行用户统计任务 var output = await sshService.ExecuteCommandAsync(server.Id,
var dataMap = context.JobDetail.JobDataMap; "ps -o ruser=userForLongName -eo user,pcpu,pmem,comm --sort=-pcpu | awk 'NR>1 && $1 !~ /^systemd/ {user[$1]+=$2; mem[$1]+=$3; count[$1]++; total[$1]=$2+$3} END {for (u in user) print u, user[u], mem[u]/count[u], count[u]}' | sort -k1,1r -k2,2nr");
var dataHelper = new DataHelper(dbContext,hubContext); if (string.IsNullOrEmpty(output)) return;
var serverList = (List<ServerModel>)dataMap["executor"]; var users = output.Split("\n", StringSplitOptions.RemoveEmptyEntries);
var userDataListAll = new List<ServerMonitoringData>();
var sshClient = serviceProvider.GetService<SshService>();
foreach (var server in serverList)
{
var output = await sshClient?.ExecuteCommandAsync(server.Id, "ps -o ruser=userForLongName -eo user,pcpu,pmem,comm --sort=-pcpu | awk 'NR>1 && $1 !~ /^systemd/ {user[$1]+=$2; mem[$1]+=$3; count[$1]++; total[$1]=$2+$3} END {for (u in user) print u, user[u], mem[u]/count[u], count[u]}' | sort -k1,1r -k2,2nr")!;
if (string.IsNullOrEmpty(output)) continue;
var lines = output.Split("\n",StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines) foreach (var user in users)
{ {
var d = line.Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList(); var values = user.Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList();
var name = d[0]; var name = values[0];
var cpu = d[1]; DataHelper.AddMonitoringData(res, values[1], $"CPU使用率-{name}", $"CpuUsage-{name}");
var mem = d[2]; DataHelper.AddMonitoringData(res, values[2], $"内存使用率-{name}", $"MemUsage-{name}");
var command=d[3]; DataHelper.AddMonitoringData(res, values[3], $"用户进程数-{name}", $"UserProcessCount-{name}");
var data = new ServerMonitoringData
{
ServerId = server.Id,
Data = cpu,
DataName = $"CPU使用率-{name}",
DataType = $"CpuUsage-{name}"
};
userDataListAll.Add(data);
data = new ServerMonitoringData
{
ServerId = server.Id,
Data = mem,
DataName = $"内存使用率-{name}",
DataType = $"MemoryUsage-{name}"
};
userDataListAll.Add(data);
data = new ServerMonitoringData
{
ServerId = server.Id,
Data = command,
DataName = $"用户进程数-{name}",
DataType = $"UserProcesses-{name}"
};
userDataListAll.Add(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);
} }
} }
} }

View File

@ -40,9 +40,6 @@
<None Update="markdowns\templates\巡检模板1.md"> <None Update="markdowns\templates\巡检模板1.md">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="app.db">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -84,15 +84,11 @@ public class ApiPermissionMiddleware(
// 判断请求是否拥有权限 // 判断请求是否拥有权限
if (!apiPermissions.Any(x => if (!apiPermissions.Any(x =>
context.Request.Path.Value != null && context.Request.Path.Value.StartsWith(x))) context.Request.Path.Value != null && context.Request.Path.Value.StartsWith(x)))
{
await ForbiddenResponse(context); await ForbiddenResponse(context);
}
else else
{
// 请求拥有权限,调用下一个中间件 // 请求拥有权限,调用下一个中间件
await next(context); await next(context);
} }
}
catch (Exception ex) catch (Exception ex)
{ {
// 记录异常信息 // 记录异常信息

View File

@ -18,6 +18,7 @@ public class PermissionMiddleware(
await next(context); await next(context);
return; return;
} }
Console.WriteLine(context.Request.Path.Value!); Console.WriteLine(context.Request.Path.Value!);
//如果访问 /public/* //如果访问 /public/*
if (context.Request.Path.Value!.StartsWith("/public")) if (context.Request.Path.Value!.StartsWith("/public"))

View File

@ -5,7 +5,6 @@ public class EmailModel
public required string Email { get; set; } public required string Email { get; set; }
} }
public class RegisterModel : EmailModel public class RegisterModel : EmailModel
{ {
public required string UserName { get; set; } public required string UserName { get; set; }

View File

@ -7,7 +7,6 @@ public class Property
public int? Order { get; set; } public int? Order { get; set; }
} }
public class ServerModel public class ServerModel
{ {
public required string Id { get; init; } public required string Id { get; init; }
@ -19,7 +18,6 @@ public class ServerModel
public required bool Http { get; set; } public required bool Http { get; set; }
} }
public class EmailSettings public class EmailSettings
{ {
public required string Host { get; init; } public required string Host { get; init; }

View File

@ -3,10 +3,8 @@ using LoongPanel_Asp;
using LoongPanel_Asp.Helpers; using LoongPanel_Asp.Helpers;
using LoongPanel_Asp.Hubs; using LoongPanel_Asp.Hubs;
using LoongPanel_Asp.Middlewares; using LoongPanel_Asp.Middlewares;
using LoongPanel_Asp.Models;
using LoongPanel_Asp.Servers; using LoongPanel_Asp.Servers;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Quartz; using Quartz;
using Quartz.AspNetCore; using Quartz.AspNetCore;
@ -77,7 +75,8 @@ builder.Services.AddCors(options =>
{ {
//允许全部 //允许全部
policy.WithOrigins("http://localhost:3001", "http://192.168.1.22:3001", "http://192.168.1.22:3001", policy.WithOrigins("http://localhost:3001", "http://192.168.1.22:3001", "http://192.168.1.22:3001",
"https://192.168.0.22:3000","https://loongpanel.xn--7p0a.site").AllowAnyHeader().AllowAnyMethod().AllowCredentials(); "https://192.168.0.22:3000", "https://loongpanel.xn--7p0a.site").AllowAnyHeader().AllowAnyMethod()
.AllowCredentials();
}); });
}); });
@ -104,13 +103,11 @@ builder.Services.AddQuartzServer(options =>
builder.Services.AddScoped<SshService>(); builder.Services.AddScoped<SshService>();
builder.Services.AddSingleton<SshStreamService>(); builder.Services.AddSingleton<SshStreamService>();
builder.Services.AddSingleton<DataService>();
builder.Services.AddHostedService<Init>(); builder.Services.AddHostedService<Init>();
var app = builder.Build(); var app = builder.Build();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.

View File

@ -0,0 +1,46 @@
using LoongPanel_Asp.Hubs;
using Microsoft.AspNetCore.SignalR;
namespace LoongPanel_Asp.Servers;
public class DataService(IServiceProvider serviceProvider, IHubContext<SessionHub> context) : IDisposable
{
//创建一个存储
private static readonly Dictionary<string, ServerMonitoringData> ServerMonitoringData = new();
public void Dispose()
{
Submit().Wait();
}
public async Task Save(ServerMonitoringData data)
{
data.Time = DateTime.UtcNow;
ServerMonitoringData[$"{data.DataType}-{data.ServerId}"] = data;
await context.Clients.All.SendAsync("ReceiveData", data.ServerId, data.DataType, data.Data);
}
public async Task Save(List<ServerMonitoringData> datas)
{
foreach (var data in datas)
{
data.Time = DateTime.UtcNow;
ServerMonitoringData[$"{data.DataType}-{data.ServerId}"] = data;
await context.Clients.All.SendAsync("ReceiveData", data.ServerId, data.DataType, data.Data);
}
}
//提交
public async Task Submit()
{
var dbContext = serviceProvider.GetRequiredService<ApplicationDbContext>();
var dataDb = dbContext.ServerMonitoringData;
await dataDb.AddRangeAsync(ServerMonitoringData.Values);
await dbContext.SaveChangesAsync();
ServerMonitoringData.Clear();
}
private async Task CheckData()
{
}
}

View File

@ -1,6 +1,4 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Net;
using LoongPanel_Asp.Models;
using MimeKit; using MimeKit;
using SmtpClient = MailKit.Net.Smtp.SmtpClient; using SmtpClient = MailKit.Net.Smtp.SmtpClient;
@ -8,20 +6,28 @@ namespace LoongPanel_Asp.Servers;
public class EmailService : IDisposable public class EmailService : IDisposable
{ {
private readonly IConfigurationSection _emailSettings;
private readonly SmtpClient _smtpClient; private readonly SmtpClient _smtpClient;
private readonly IConfigurationSection _emailSettings;
//创建一个验证码缓存字典 //创建一个验证码缓存字典
private readonly ConcurrentDictionary<string, string> _verifyCodeCache = new(); private readonly ConcurrentDictionary<string, string> _verifyCodeCache = new();
public EmailService(IConfigurationSection emailSettings) public EmailService(IConfigurationSection emailSettings)
{ {
_emailSettings = emailSettings; _emailSettings = emailSettings;
// 初始化SmtpClient // 初始化SmtpClient
_smtpClient = new SmtpClient(); _smtpClient = new SmtpClient();
_smtpClient.Connect(_emailSettings["Host"], int.Parse(_emailSettings["Port"] ?? throw new InvalidOperationException()), true); _smtpClient.Connect(_emailSettings["Host"],
int.Parse(_emailSettings["Port"] ?? throw new InvalidOperationException()), true);
_smtpClient.Authenticate(_emailSettings["Username"], _emailSettings["Password"]); _smtpClient.Authenticate(_emailSettings["Username"], _emailSettings["Password"]);
} }
public void Dispose()
{
_smtpClient.Dispose();
}
//发送邮件 //发送邮件
public async Task SendEmailAsync(string toAddress, string toName, string subject, string body) public async Task SendEmailAsync(string toAddress, string toName, string subject, string body)
@ -32,7 +38,7 @@ public class EmailService:IDisposable
mailMessage.Subject = subject; mailMessage.Subject = subject;
var bodyBuilder = new BodyBuilder var bodyBuilder = new BodyBuilder
{ {
HtmlBody = body, HtmlBody = body
}; };
mailMessage.Body = bodyBuilder.ToMessageBody(); mailMessage.Body = bodyBuilder.ToMessageBody();
await _smtpClient.SendAsync(mailMessage); await _smtpClient.SendAsync(mailMessage);
@ -49,6 +55,7 @@ public class EmailService:IDisposable
//存入缓存 //存入缓存
_verifyCodeCache.AddOrUpdate(userId, code, (key, oldValue) => code); _verifyCodeCache.AddOrUpdate(userId, code, (key, oldValue) => code);
} }
//验证邮箱验证码 //验证邮箱验证码
public bool VerifyEmailVerifyCode(string userId, string code) public bool VerifyEmailVerifyCode(string userId, string code)
{ {
@ -66,9 +73,4 @@ public class EmailService:IDisposable
var verifyCode = random.Next(100000, 999999).ToString(); var verifyCode = random.Next(100000, 999999).ToString();
return verifyCode; return verifyCode;
} }
public void Dispose()
{
throw new NotImplementedException();
}
} }

View File

@ -1,5 +1,4 @@
using LoongPanel_Asp.Helpers; using LoongPanel_Asp.Helpers;
using LoongPanel_Asp.Models;
using Renci.SshNet; using Renci.SshNet;
using ConnectionInfo = Renci.SshNet.ConnectionInfo; using ConnectionInfo = Renci.SshNet.ConnectionInfo;
@ -35,7 +34,8 @@ public class SshService : IDisposable
foreach (var serverConfiguration in serverConfigurations) foreach (var serverConfiguration in serverConfigurations)
{ {
var connectionInfo = new ConnectionInfo(serverConfiguration.Address, serverConfiguration.Port, var connectionInfo = new ConnectionInfo(serverConfiguration.Address, serverConfiguration.Port,
serverConfiguration.Username, new PasswordAuthenticationMethod(serverConfiguration.Username, serverConfiguration.Password)); serverConfiguration.Username,
new PasswordAuthenticationMethod(serverConfiguration.Username, serverConfiguration.Password));
_serverConnectionInfos[serverConfiguration.Id] = connectionInfo; _serverConnectionInfos[serverConfiguration.Id] = connectionInfo;
var sshClient = new SshClient(connectionInfo); var sshClient = new SshClient(connectionInfo);
// 设置超时时间 // 设置超时时间

View File

@ -1,20 +1,18 @@
using Renci.SshNet; using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using System.IO;
using LoongPanel_Asp.Helpers; using LoongPanel_Asp.Helpers;
using LoongPanel_Asp.Hubs; using LoongPanel_Asp.Hubs;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using Renci.SshNet;
using ConnectionInfo = Renci.SshNet.ConnectionInfo; using ConnectionInfo = Renci.SshNet.ConnectionInfo;
namespace LoongPanel_Asp.Servers; namespace LoongPanel_Asp.Servers;
public class SshStreamService : IDisposable public class SshStreamService : IDisposable
{ {
private readonly ConcurrentDictionary<string, (SshClient Client, ShellStream Stream)> _sshStreams = new();
private readonly IHubContext<TerminalHub> _hubContext; private readonly IHubContext<TerminalHub> _hubContext;
private CancellationTokenSource _cancellationTokenSource; private readonly ConcurrentDictionary<string, (SshClient Client, ShellStream Stream)> _sshStreams = new();
private Task _readingTask; private readonly CancellationTokenSource _cancellationTokenSource;
private readonly Task _readingTask;
public SshStreamService(IHubContext<TerminalHub> hubContext) public SshStreamService(IHubContext<TerminalHub> hubContext)
{ {
@ -22,6 +20,31 @@ public class SshStreamService : IDisposable
_cancellationTokenSource = new CancellationTokenSource(); _cancellationTokenSource = new CancellationTokenSource();
_readingTask = Task.Run(() => StartReadingAsync(_cancellationTokenSource.Token)); _readingTask = Task.Run(() => StartReadingAsync(_cancellationTokenSource.Token));
} }
public void Dispose()
{
_cancellationTokenSource.Cancel();
try
{
_readingTask.Wait();
}
catch (AggregateException ae)
{
ae.Handle(e => e is OperationCanceledException);
}
foreach (var sshStreamInfo in _sshStreams.Values)
{
var (sshClient, shellStream) = sshStreamInfo;
shellStream?.Close();
shellStream?.Dispose();
sshClient.Disconnect();
sshClient.Dispose();
}
_sshStreams.Clear();
}
public void Connect(string userId, string serverId) public void Connect(string userId, string serverId)
{ {
// 假设 JobConfigHelper.GetServers() 返回一个包含服务器配置的列表 // 假设 JobConfigHelper.GetServers() 返回一个包含服务器配置的列表
@ -34,7 +57,8 @@ public class SshStreamService : IDisposable
var username = serverConfig.Username; var username = serverConfig.Username;
var password = serverConfig.Password; var password = serverConfig.Password;
var connectionInfo = new ConnectionInfo(host, port, username, new PasswordAuthenticationMethod(username, password)); var connectionInfo =
new ConnectionInfo(host, port, username, new PasswordAuthenticationMethod(username, password));
var sshClient = new SshClient(connectionInfo); var sshClient = new SshClient(connectionInfo);
sshClient.Connect(); sshClient.Connect();
var shellStream = sshClient.CreateShellStream("xterm", 100, 40, 800, 600, 1024); var shellStream = sshClient.CreateShellStream("xterm", 100, 40, 800, 600, 1024);
@ -91,29 +115,6 @@ public class SshStreamService : IDisposable
} }
} }
public void Dispose()
{
_cancellationTokenSource.Cancel();
try
{
_readingTask.Wait();
}
catch (AggregateException ae)
{
ae.Handle(e => e is OperationCanceledException);
}
foreach (var sshStreamInfo in _sshStreams.Values)
{
var (sshClient, shellStream) = sshStreamInfo;
shellStream?.Close();
shellStream?.Dispose();
sshClient.Disconnect();
sshClient.Dispose();
}
_sshStreams.Clear();
}
private async Task StartReadingAsync(CancellationToken cancellationToken) private async Task StartReadingAsync(CancellationToken cancellationToken)
{ {
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)

View File

@ -1,5 +1,5 @@
| Öµ°à¼Ç¼ | col | col | col | col | col | | ֵ<EFBFBD><EFBFBD><EFBFBD>¼ | 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 |

View File

@ -9,7 +9,7 @@
## 第二部分:巡检项目 ## 第二部分:巡检项目
| 序号 | 巡检项目名称 | 巡检要点 | 巡检内容 | | 序号 | 巡检项目名称 | 巡检要点 | 巡检内容 |
| ---- | ------------ | -------------------------------------------------- | -------------------------------------------------------------- | |-----|--------|---------------------------|---------------------------------|
| 1 | 设备运行状况 | 检查设备运行是否平稳,有无异常振动或噪音。 | 详细记录设备运行参数,比较历史数据,分析是否存在异常。 | | 1 | 设备运行状况 | 检查设备运行是否平稳,有无异常振动或噪音。 | 详细记录设备运行参数,比较历史数据,分析是否存在异常。 |
| 2 | 安全防护措施 | 检查安全防护设施是否完好,如安全栅栏、警示标志等。 | 确认所有安全设施无损坏,位置正确,且工作人员了解如何正确使用。 | | 2 | 安全防护措施 | 检查安全防护设施是否完好,如安全栅栏、警示标志等。 | 确认所有安全设施无损坏,位置正确,且工作人员了解如何正确使用。 |
| 3 | 环境卫生状况 | 检查工作区域是否清洁,有无垃圾或障碍物。 | 清理工作区域,确保无杂物,保持环境整洁。 | | 3 | 环境卫生状况 | 检查工作区域是否清洁,有无垃圾或障碍物。 | 清理工作区域,确保无杂物,保持环境整洁。 |
@ -18,7 +18,7 @@
## 第三部分:检查记录 ## 第三部分:检查记录
| 序号 | 检查项目 | 检查结果 | 异常说明 | 处理措施 | 反馈意见 | | 序号 | 检查项目 | 检查结果 | 异常说明 | 处理措施 | 反馈意见 |
| ---- | -------- | -------- | ---------------- | ------------------------------ | ---------------------- | |-----|-------|------|----------|-----------------|-------------|
| 1 | 外壳 | 正常 | 无 | 无 | 无 | | 1 | 外壳 | 正常 | 无 | 无 | 无 |
| 2 | 电源 | 异常 | 设备有轻微振动。 | 已联系维修人员,计划明日检修。 | 建议增加设备维护频率。 | | 2 | 电源 | 异常 | 设备有轻微振动。 | 已联系维修人员,计划明日检修。 | 建议增加设备维护频率。 |
| 3 | 主机 | 正常 | 无 | 无 | 无 | | 3 | 主机 | 正常 | 无 | 无 | 无 |

View File

@ -70,9 +70,9 @@ dataStore.$subscribe((_, state) => {
</div> </div>
</div> </div>
</template> </template>
<n-tag type="info" v-for="i in info"> <slot>
{{ i }} 这里什么都没有
</n-tag> </slot>
</n-popover> </n-popover>
</template> </template>

View File

@ -108,6 +108,9 @@ watchDebounced(
<n-modal <n-modal
v-model:show="visible" v-model:show="visible"
style="width: auto;" style="width: auto;"
preset="card"
title="添加图表"
> >
<SettingCard /> <SettingCard />
</n-modal> </n-modal>

View File

@ -64,9 +64,9 @@ onMounted(async () => {
.then(() => { .then(() => {
console.log("Terminal created") console.log("Terminal created")
}) })
setTimeout(()=>{ // setTimeout(()=>{
connection.invoke("SendMessage","neofetch\n") // connection.invoke("SendMessage","neofetch\n")
},200) // },200)
}) })
const resize=()=>{ const resize=()=>{
@ -125,9 +125,9 @@ const resize=()=>{
height: 500px; height: 500px;
max-width: 80vw; max-width: 80vw;
max-height: 80vh; max-height: 80vh;
border-radius: $radius; border-radius: $radius*2;
background: rgba(0,0,0,.5); backdrop-filter: blur(20px);
backdrop-filter: blur(20px) border: 2px solid #fff;
} }
#terminal { #terminal {

View File

@ -25,7 +25,6 @@ onMounted(() => {
width: 100%; width: 100%;
padding: 32px; padding: 32px;
gap: 32px; gap: 32px;
background: unset;
display: grid; display: grid;
grid-template-columns: 1fr minmax(800px,1fr); grid-template-columns: 1fr minmax(800px,1fr);
grid-template-rows: 1fr; grid-template-rows: 1fr;

View File

@ -199,7 +199,6 @@ onKeyStroke('Shift', (e) => {
<SideBar/> <SideBar/>
<TitleBar/> <TitleBar/>
<n-modal v-model:show.lazy="visible" auto-focus > <n-modal v-model:show.lazy="visible" auto-focus >
<Term/> <Term/>
</n-modal> </n-modal>
<div class="main-box"> <div class="main-box">

View File

@ -68,7 +68,8 @@ export default defineNuxtConfig({
css: ['assets/min.scss', 'vue-toastification/dist/index.css'], css: ['assets/min.scss', 'vue-toastification/dist/index.css'],
devServer: { devServer: {
port: 3001, host: '0.0.0.0', port: 3001,
// host: '0.0.0.0',
// https: { // https: {
// key: "./localhost+3-key.pem", // key: "./localhost+3-key.pem",
// cert: "./localhost+3.pem", // cert: "./localhost+3.pem",