using System.IdentityModel.Tokens.Jwt; using System.Net; using System.Security.Claims; using System.Text; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; namespace server.Controllers; /// /// 数据控制器 /// [ApiController] [Route("api/[controller]")] public class DataController : ControllerBase { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private readonly Database.UserManager _userManager; // 固定的实验板IP,端口,MAC地址 private const string BOARD_IP = "169.254.109.0"; public DataController(Database.UserManager userManager) { _userManager = userManager; } /// /// 获取本机IP地址(优先选择与实验板同网段的IP) /// /// 本机IP地址 private IPAddress GetLocalIPAddress() { try { var boardIpSegments = BOARD_IP.Split('.').Take(3).ToArray(); // 优先选择与实验板IP前三段相同的IP var sameSegmentIP = System.Net.NetworkInformation.NetworkInterface .GetAllNetworkInterfaces() .Where(nic => nic.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up && nic.NetworkInterfaceType != System.Net.NetworkInformation.NetworkInterfaceType.Loopback) .SelectMany(nic => nic.GetIPProperties().UnicastAddresses) .Where(addr => addr.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) .Select(addr => addr.Address) .FirstOrDefault(addr => { var segments = addr.ToString().Split('.'); return segments.Length == 4 && segments[0] == boardIpSegments[0] && segments[1] == boardIpSegments[1] && segments[2] == boardIpSegments[2]; }); if (sameSegmentIP != null) return sameSegmentIP; // 如果没有找到同网段的IP,返回第一个可用的IP return System.Net.NetworkInformation.NetworkInterface .GetAllNetworkInterfaces() .Where(nic => nic.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up && nic.NetworkInterfaceType != System.Net.NetworkInformation.NetworkInterfaceType.Loopback) .SelectMany(nic => nic.GetIPProperties().UnicastAddresses) .Where(addr => addr.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) .Select(addr => addr.Address) .FirstOrDefault() ?? IPAddress.Loopback; } catch (Exception ex) { logger.Error(ex, "获取本机IP地址失败"); return IPAddress.Loopback; } } /// /// 用户登录,获取 JWT 令牌 /// /// 用户名 /// 用户密码 /// JWT 令牌字符串 [HttpPost("Login")] [EnableCors("Users")] [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public IActionResult Login(string name, string password) { // 验证用户密码 var ret = _userManager.CheckUserPassword(name, password); if (!ret.IsSuccessful) return StatusCode(StatusCodes.Status500InternalServerError, "数据库操作失败"); if (!ret.Value.HasValue) return BadRequest("用户名或密码错误"); var user = ret.Value.Value; // 生成 JWT var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.ASCII.GetBytes("my secret key 1234567890my secret key 1234567890"); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new Claim[] { new Claim(ClaimTypes.Name, user.Name), new Claim(ClaimTypes.Email, user.EMail), new Claim(ClaimTypes.Role, user.Permission.ToString()) }), Expires = DateTime.UtcNow.AddHours(1), SigningCredentials = new SigningCredentials( new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature), Audience = "dlut.edu.cn", Issuer = "dlut.edu.cn", }; var token = tokenHandler.CreateToken(tokenDescriptor); var jwt = tokenHandler.WriteToken(token); return Ok(jwt); } /// /// 测试用户认证,需携带有效 JWT /// /// 认证成功信息 [Authorize] [HttpGet("TestAuth")] [EnableCors("Users")] [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public IActionResult TestAuth() { return Ok("认证成功!"); } /// /// 测试管理员用户认证,需携带有效 JWT /// /// 认证成功信息 [Authorize("Admin")] [HttpGet("TestAdminAuth")] [EnableCors("Users")] [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public IActionResult TestAdminAuth() { return Ok("认证成功!"); } /// /// 获取当前用户信息 /// /// 用户信息,包括ID、用户名、邮箱和板卡ID [Authorize] [HttpGet("GetUserInfo")] [EnableCors("Users")] [ProducesResponseType(typeof(UserInfo), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public IActionResult GetUserInfo() { // Get User Name var userName = User.Identity?.Name; if (string.IsNullOrEmpty(userName)) return Unauthorized("未找到用户名信息"); // Get User Info var ret = _userManager.GetUserByName(userName); if (!ret.IsSuccessful) return StatusCode(StatusCodes.Status500InternalServerError, "数据库操作失败"); if (!ret.Value.HasValue) return BadRequest("用户不存在"); var user = ret.Value.Value; return Ok(new UserInfo { ID = user.ID, Name = user.Name, EMail = user.EMail, BoardID = user.BoardID, BoardExpireTime = user.BoardExpireTime, }); } /// /// 注册新用户 /// /// 用户名(不超过255个字符) /// 邮箱地址 /// 用户密码 /// 操作结果,成功返回 true,失败返回错误信息 [HttpPost("SignUpUser")] [EnableCors("Users")] [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public IActionResult SignUpUser(string name, string email, string password) { // 验证输入参数 if (string.IsNullOrWhiteSpace(name)) return BadRequest("用户名不能为空"); if (name.Length > 255) return BadRequest("用户名不能超过255个字符"); if (string.IsNullOrWhiteSpace(email)) return BadRequest("邮箱不能为空"); if (string.IsNullOrWhiteSpace(password)) return BadRequest("密码不能为空"); try { var ret = _userManager.AddUser(name, email, password); return Ok(ret); } catch (Exception ex) { logger.Error(ex, "注册用户时发生异常"); return StatusCode(StatusCodes.Status500InternalServerError, "注册失败,请稍后重试"); } } /// /// 获取一个空闲的实验板(普通用户权限) /// /// 绑定持续时间(小时),默认为1小时 [Authorize] [HttpGet("GetAvailableBoard")] [EnableCors("Users")] [ProducesResponseType(typeof(Database.Board), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async ValueTask GetAvailableBoard(int durationHours = 1) { try { var userName = User.Identity?.Name; if (string.IsNullOrEmpty(userName)) return Unauthorized("未找到用户名信息"); var userRet = _userManager.GetUserByName(userName); if (!userRet.IsSuccessful || !userRet.Value.HasValue) return BadRequest("用户不存在"); var user = userRet.Value.Value; var expireTime = DateTime.UtcNow.AddHours(durationHours); var boardOpt = _userManager.GetAvailableBoard(user.ID, expireTime); if (!boardOpt.HasValue) return NotFound("没有可用的实验板"); var boardInfo = boardOpt.Value; if (!(await ArpClient.CheckOrAddAsync(boardInfo.IpAddr, boardInfo.MacAddr, GetLocalIPAddress().ToString()))) { logger.Error($"无法配置ARP,实验板可能会无法连接"); } return Ok(boardInfo); } catch (Exception ex) { logger.Error(ex, "获取空闲实验板时发生异常"); return StatusCode(StatusCodes.Status500InternalServerError, "获取失败,请稍后重试"); } } /// /// 解除当前用户绑定的实验板(普通用户权限) /// [Authorize] [HttpPost("UnbindBoard")] [EnableCors("Users")] [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public IActionResult UnbindBoard() { try { var userName = User.Identity?.Name; if (string.IsNullOrEmpty(userName)) return Unauthorized("未找到用户名信息"); var userRet = _userManager.GetUserByName(userName); if (!userRet.IsSuccessful || !userRet.Value.HasValue) return BadRequest("用户不存在"); var user = userRet.Value.Value; var result = _userManager.UnbindUserFromBoard(user.ID); return Ok(result > 0); } catch (Exception ex) { logger.Error(ex, "解除实验板绑定时发生异常"); return StatusCode(StatusCodes.Status500InternalServerError, "解除失败,请稍后重试"); } } /// /// 用户根据实验板ID获取实验板信息(普通用户权限) /// [Authorize] [HttpGet("GetBoardByID")] [EnableCors("Users")] [ProducesResponseType(typeof(Database.Board), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task GetBoardByID(Guid id) { try { var ret = _userManager.GetBoardByID(id); if (!ret.IsSuccessful) return StatusCode(StatusCodes.Status500InternalServerError, "数据库操作失败"); if (!ret.Value.HasValue) return NotFound("未找到对应的实验板"); var boardInfo = ret.Value.Value; if (!(await ArpClient.CheckOrAddAsync(boardInfo.IpAddr, boardInfo.MacAddr, GetLocalIPAddress().ToString()))) { logger.Error($"无法配置ARP,实验板可能会无法连接"); } return Ok(boardInfo); } catch (Exception ex) { logger.Error(ex, "获取实验板信息时发生异常"); return StatusCode(StatusCodes.Status500InternalServerError, "获取失败,请稍后重试"); } } /// /// 新增板子(管理员权限) /// [Authorize("Admin")] [HttpPost("AddBoard")] [EnableCors("Users")] [ProducesResponseType(typeof(Guid), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public IActionResult AddBoard(string name) { if (string.IsNullOrWhiteSpace(name)) return BadRequest("板子名称不能为空"); try { var ret = _userManager.AddBoard(name); return Ok(ret); } catch (Exception ex) { logger.Error(ex, "新增板子时发生异常"); return StatusCode(StatusCodes.Status500InternalServerError, "新增失败,请稍后重试"); } } /// /// 删除板子(管理员权限) /// [Authorize("Admin")] [HttpDelete("DeleteBoard")] [EnableCors("Users")] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public IActionResult DeleteBoard(Guid id) { if (id == Guid.Empty) return BadRequest("板子Guid不能为空"); try { var ret = _userManager.DeleteBoardByID(id); return Ok(ret); } catch (Exception ex) { logger.Error(ex, "删除板子时发生异常"); return StatusCode(StatusCodes.Status500InternalServerError, "删除失败,请稍后重试"); } } /// /// 获取全部板子(管理员权限) /// [Authorize("Admin")] [HttpGet("GetAllBoards")] [EnableCors("Users")] [ProducesResponseType(typeof(Database.Board[]), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public IActionResult GetAllBoards() { try { var boards = _userManager.GetAllBoard(); return Ok(boards); } catch (Exception ex) { logger.Error(ex, "获取全部板子时发生异常"); return StatusCode(StatusCodes.Status500InternalServerError, "获取失败,请稍后重试"); } } /// /// 更新板卡名称(管理员权限) /// [Authorize("Admin")] [HttpPost("UpdateBoardName")] [EnableCors("Users")] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public IActionResult UpdateBoardName(Guid boardId, string newName) { if (boardId == Guid.Empty) return BadRequest("板子Guid不能为空"); if (string.IsNullOrWhiteSpace(newName)) return BadRequest("新名称不能为空"); try { var result = _userManager.UpdateBoardName(boardId, newName); return Ok(result); } catch (Exception ex) { logger.Error(ex, "更新板卡名称时发生异常"); return StatusCode(StatusCodes.Status500InternalServerError, "更新失败,请稍后重试"); } } /// /// 更新板卡状态(管理员权限) /// [Authorize("Admin")] [HttpPost("UpdateBoardStatus")] [EnableCors("Users")] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public IActionResult UpdateBoardStatus(Guid boardId, Database.Board.BoardStatus newStatus) { if (boardId == Guid.Empty) return BadRequest("板子Guid不能为空"); try { var result = _userManager.UpdateBoardStatus(boardId, newStatus); return Ok(result); } catch (Exception ex) { logger.Error(ex, "更新板卡状态时发生异常"); return StatusCode(StatusCodes.Status500InternalServerError, "更新失败,请稍后重试"); } } /// /// [TODO:description] /// public class UserInfo { /// /// 用户的唯一标识符 /// public Guid ID { get; set; } /// /// 用户的名称 /// public required string Name { get; set; } /// /// 用户的电子邮箱 /// public required string EMail { get; set; } /// /// 用户关联的板卡ID /// public Guid BoardID { get; set; } /// /// 用户绑定板子的过期时间 /// public DateTime? BoardExpireTime { get; set; } } }