381 lines
13 KiB
C#
381 lines
13 KiB
C#
using System.IdentityModel.Tokens.Jwt;
|
||
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;
|
||
|
||
/// <summary>
|
||
/// 数据控制器
|
||
/// </summary>
|
||
[ApiController]
|
||
[Route("api/[controller]")]
|
||
public class DataController : ControllerBase
|
||
{
|
||
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||
|
||
/// <summary>
|
||
/// [TODO:description]
|
||
/// </summary>
|
||
public class UserInfo
|
||
{
|
||
/// <summary>
|
||
/// 用户的唯一标识符
|
||
/// </summary>
|
||
public Guid ID { get; set; }
|
||
|
||
/// <summary>
|
||
/// 用户的名称
|
||
/// </summary>
|
||
public required string Name { get; set; }
|
||
|
||
/// <summary>
|
||
/// 用户的电子邮箱
|
||
/// </summary>
|
||
public required string EMail { get; set; }
|
||
|
||
/// <summary>
|
||
/// 用户关联的板卡ID
|
||
/// </summary>
|
||
public Guid BoardID { get; set; }
|
||
|
||
/// <summary>
|
||
/// 用户绑定板子的过期时间
|
||
/// </summary>
|
||
public DateTime? BoardExpireTime { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 用户登录,获取 JWT 令牌
|
||
/// </summary>
|
||
/// <param name="name">用户名</param>
|
||
/// <param name="password">用户密码</param>
|
||
/// <returns>JWT 令牌字符串</returns>
|
||
[HttpPost("Login")]
|
||
[EnableCors("Users")]
|
||
[ProducesResponseType(typeof(string), StatusCodes.Status200OK)]
|
||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||
public IActionResult Login(string name, string password)
|
||
{
|
||
// 验证用户密码
|
||
using var db = new Database.AppDataConnection();
|
||
var ret = db.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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试用户认证,需携带有效 JWT
|
||
/// </summary>
|
||
/// <returns>认证成功信息</returns>
|
||
[Authorize]
|
||
[HttpGet("TestAuth")]
|
||
[EnableCors("Users")]
|
||
[ProducesResponseType(typeof(string), StatusCodes.Status200OK)]
|
||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||
public IActionResult TestAuth()
|
||
{
|
||
return Ok("认证成功!");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试管理员用户认证,需携带有效 JWT
|
||
/// </summary>
|
||
/// <returns>认证成功信息</returns>
|
||
[Authorize("Admin")]
|
||
[HttpGet("TestAdminAuth")]
|
||
[EnableCors("Users")]
|
||
[ProducesResponseType(typeof(string), StatusCodes.Status200OK)]
|
||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||
public IActionResult TestAdminAuth()
|
||
{
|
||
return Ok("认证成功!");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前用户信息
|
||
/// </summary>
|
||
/// <returns>用户信息,包括ID、用户名、邮箱和板卡ID</returns>
|
||
[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
|
||
using var db = new Database.AppDataConnection();
|
||
var ret = db.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,
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 注册新用户
|
||
/// </summary>
|
||
/// <param name="name">用户名(不超过255个字符)</param>
|
||
/// <param name="email">邮箱地址</param>
|
||
/// <param name="password">用户密码</param>
|
||
/// <returns>操作结果,成功返回 true,失败返回错误信息</returns>
|
||
[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
|
||
{
|
||
using var db = new Database.AppDataConnection();
|
||
var ret = db.AddUser(name, email, password);
|
||
return Ok(ret);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.Error(ex, "注册用户时发生异常");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, "注册失败,请稍后重试");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取一个空闲的实验板(普通用户权限)
|
||
/// </summary>
|
||
/// <param name="durationHours">绑定持续时间(小时),默认为1小时</param>
|
||
[Authorize]
|
||
[HttpGet("GetAvailableBoard")]
|
||
[EnableCors("Users")]
|
||
[ProducesResponseType(typeof(Database.Board), StatusCodes.Status200OK)]
|
||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||
public IActionResult GetAvailableBoard(int durationHours = 1)
|
||
{
|
||
try
|
||
{
|
||
var userName = User.Identity?.Name;
|
||
if (string.IsNullOrEmpty(userName))
|
||
return Unauthorized("未找到用户名信息");
|
||
|
||
using var db = new Database.AppDataConnection();
|
||
var userRet = db.GetUserByName(userName);
|
||
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
||
return BadRequest("用户不存在");
|
||
|
||
var user = userRet.Value.Value;
|
||
var expireTime = DateTime.UtcNow.AddHours(durationHours);
|
||
|
||
var boardOpt = db.GetAvailableBoard(user.ID, expireTime);
|
||
if (!boardOpt.HasValue)
|
||
return NotFound("没有可用的实验板");
|
||
|
||
return Ok(boardOpt.Value);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.Error(ex, "获取空闲实验板时发生异常");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, "获取失败,请稍后重试");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 解除当前用户绑定的实验板(普通用户权限)
|
||
/// </summary>
|
||
[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("未找到用户名信息");
|
||
|
||
using var db = new Database.AppDataConnection();
|
||
var userRet = db.GetUserByName(userName);
|
||
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
||
return BadRequest("用户不存在");
|
||
|
||
var user = userRet.Value.Value;
|
||
var result = db.UnbindUserFromBoard(user.ID);
|
||
return Ok(result > 0);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.Error(ex, "解除实验板绑定时发生异常");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, "解除失败,请稍后重试");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 用户根据实验板ID获取实验板信息(普通用户权限)
|
||
/// </summary>
|
||
[Authorize]
|
||
[HttpGet("GetBoardByID")]
|
||
[EnableCors("Users")]
|
||
[ProducesResponseType(typeof(Database.Board), StatusCodes.Status200OK)]
|
||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||
public IActionResult GetBoardByID(Guid id)
|
||
{
|
||
try
|
||
{
|
||
using var db = new Database.AppDataConnection();
|
||
var ret = db.GetBoardByID(id);
|
||
if (!ret.IsSuccessful)
|
||
return StatusCode(StatusCodes.Status500InternalServerError, "数据库操作失败");
|
||
if (!ret.Value.HasValue)
|
||
return NotFound("未找到对应的实验板");
|
||
return Ok(ret.Value.Value);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.Error(ex, "获取实验板信息时发生异常");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, "获取失败,请稍后重试");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 新增板子(管理员权限)
|
||
/// </summary>
|
||
[Authorize("Admin")]
|
||
[HttpPost("AddBoard")]
|
||
[EnableCors("Users")]
|
||
[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
|
||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||
public IActionResult AddBoard(string name, string ipAddr, int port)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(name))
|
||
return BadRequest("板子名称不能为空");
|
||
if (string.IsNullOrWhiteSpace(ipAddr))
|
||
return BadRequest("IP地址不能为空");
|
||
if (port <= 0 || port > 65535)
|
||
return BadRequest("端口号不合法");
|
||
try
|
||
{
|
||
using var db = new Database.AppDataConnection();
|
||
var ret = db.AddBoard(name, ipAddr, port);
|
||
return Ok(ret);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.Error(ex, "新增板子时发生异常");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, "新增失败,请稍后重试");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除板子(管理员权限)
|
||
/// </summary>
|
||
[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
|
||
{
|
||
using var db = new Database.AppDataConnection();
|
||
var ret = db.DeleteBoardByID(id);
|
||
return Ok(ret);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.Error(ex, "删除板子时发生异常");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, "删除失败,请稍后重试");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取全部板子(管理员权限)
|
||
/// </summary>
|
||
[Authorize("Admin")]
|
||
[HttpGet("GetAllBoards")]
|
||
[EnableCors("Users")]
|
||
[ProducesResponseType(typeof(Database.Board[]), StatusCodes.Status200OK)]
|
||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||
public IActionResult GetAllBoards()
|
||
{
|
||
try
|
||
{
|
||
using var db = new Database.AppDataConnection();
|
||
var boards = db.GetAllBoard();
|
||
return Ok(boards);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.Error(ex, "获取全部板子时发生异常");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, "获取失败,请稍后重试");
|
||
}
|
||
}
|
||
}
|
||
|