flower-story/Server/Controller/UserController.cs
2023-05-23 22:08:56 +08:00

228 lines
7.7 KiB
C#

using Blahblah.FlowerStory.Server.Data;
using Blahblah.FlowerStory.Server.Data.Model;
using Microsoft.AspNetCore.Mvc;
namespace Blahblah.FlowerStory.Server.Controller
{
/// <summary>
/// 用户会话相关服务
/// </summary>
[ApiController]
[Produces("application/json")]
[Route("users")]
public partial class UserController : BaseController
{
/// <summary>
/// 构造用户会话服务
/// </summary>
/// <param name="db">数据库对象</param>
/// <param name="logger">日志对象</param>
public UserController(FlowerDatabase db, ILogger<UserController> logger) : base(db, logger)
{
}
/// <summary>
/// 用户登录
/// </summary>
/// <remarks>
/// 提交示例:
///
/// POST /users/auth
/// {
/// "id": "blahblah",
/// "password": "pwd123"
/// }
///
/// </remarks>
/// <param name="login">登录参数</param>
/// <returns>成功登录则返回自定义认证头</returns>
/// <response code="200">返回自定义认证头</response>
/// <response code="401">认证失败</response>
/// <response code="404">未找到用户</response>
[Route("auth")]
[Consumes("application/json")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[HttpPost]
public ActionResult Authenticate([FromBody] LoginParamter login)
{
#if DEBUG
logger?.LogInformation("user \"{user}\" try to login with password \"{password}\"", login.Id, login.Password);
#endif
var user = database.Users.FirstOrDefault(u => u.UserId == login.Id);
if (user == null)
{
logger?.LogWarning("user \"{user}\" not found", login.Id);
return NotFound();
}
// comput password hash with salt
string hash = HashPassword(login.Password, login.Id);
if (hash != user.Password)
{
logger?.LogWarning("hash result {hash}, unauthorized with hash {password}", hash, user.Password);
return Unauthorized();
}
// record the session
// TODO: singleton token
var now = DateTimeOffset.UtcNow;
var expires = 1200; // 20 minutes
var token = new TokenItem
{
Id = Guid.NewGuid().ToString("N"),
UserId = user.Id,
LogonDateUnixTime = now.ToUnixTimeMilliseconds(),
ActiveDateUnixTime = now.ToUnixTimeMilliseconds(),
ExpireDateUnixTime = now.AddSeconds(expires).ToUnixTimeMilliseconds(),
ExpireSeconds = expires,
ClientApp = "browser", // TODO: support app later
ClientAgent = Request.Headers.UserAgent
};
database.Tokens.Add(token);
user.ActiveDateUnixTime = token.ActiveDateUnixTime;
database.Users.Update(user);
SaveDatabase();
Response.Headers.Add(AuthHeader, token.Id);
return Ok();
}
/// <summary>
/// 注册用户
/// </summary>
/// <remarks>
/// 提交示例:
///
/// POST /users/register
/// {
/// "id": "blahblah",
/// "password": "pwd123",
/// "userName": "Blah blah",
/// "email": "blah@example.com",
/// "mobile": "18012345678"
/// }
///
/// </remarks>
/// <param name="user">注册参数</param>
/// <returns>成功注册则返回已注册的用户对象</returns>
/// <response code="200">返回已注册的用户对象</response>
/// <response code="500">用户重复或其他服务器错误</response>
[Route("register")]
[Consumes("application/json")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[HttpPost]
public ActionResult<UserItem> Register([FromBody] UserParameter user)
{
#if DEBUG
logger?.LogInformation("user register, {user}", user);
#endif
var u = database.Users.FirstOrDefault(u => u.UserId == user.Id);
if (u != null)
{
logger?.LogWarning("duplicate user \"{id}\"", user.Id);
return Problem("duplicateUser", "users/register", 500);
}
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var item = new UserItem
{
UserId = user.Id,
Password = HashPassword(user.Password, user.Id),
Level = UserCommon,
RegisterDateUnixTime = now,
ActiveDateUnixTime = now,
Name = user.UserName,
Email = user.Email,
Mobile = user.Mobile
};
database.Users.Add(item);
SaveDatabase();
return Ok(item);
}
/// <summary>
/// 修改用户
/// </summary>
/// <remarks>
/// 提交示例:
///
/// POST /users/update
/// {
/// "userName": "Blah blah",
/// "email": "blah@example.com",
/// "mobile": "18012345678"
/// }
///
/// </remarks>
/// <param name="update">修改参数</param>
/// <returns>修改成功则返回已修改的用户对象</returns>
/// <response code="200">返回已修改的用户对象</response>
/// <response code="400">认证头未找到</response>
/// <response code="401">服务器未找到登录会话</response>
/// <response code="403">用户权限不足</response>
/// <response code="404">未找到关联用户</response>
[Route("update")]
[Consumes("application/json")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[HttpPost]
public ActionResult<UserItem> Update([FromBody] UpdateParameter update)
{
#if DEBUG
logger?.LogInformation("user update, {user}", update);
#endif
var (result, user) = CheckPermission();
if (result != null)
{
return result;
}
if (user == null)
{
return NotFound();
}
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
user.ActiveDateUnixTime = now;
user.Name = update.UserName;
user.Email = update.Email;
user.Mobile = update.Mobile;
database.Users.Update(user);
SaveDatabase();
return Ok(user);
}
//#if DEBUG
/// <summary>
/// 获取所有用户
/// </summary>
/// <returns></returns>
[Route("query")]
[HttpGet]
public ActionResult<UserItem[]> GetUsers()
{
return Ok(database.Users.ToArray());
}
/// <summary>
/// 获取所有 token
/// </summary>
/// <returns></returns>
[Route("tokens")]
[HttpGet]
public ActionResult<TokenItem[]> GetTokens()
{
return Ok(database.Tokens.ToArray());
}
//#endif
}
}