.
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
using Blahblah.FlowerStory.Server.Data;
|
||||
using Blahblah.FlowerStory.Server.Data.Model;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkiaSharp;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
@@ -14,6 +16,8 @@ public abstract partial class BaseController : ControllerBase
|
||||
{
|
||||
private const string Salt = "Blah blah, o! Flower story, intimately help you record every bit of the garden.";
|
||||
|
||||
private const int ThumbWidth = 600;
|
||||
|
||||
/// <summary>
|
||||
/// 临时 id
|
||||
/// </summary>
|
||||
@@ -23,7 +27,7 @@ public abstract partial class BaseController : ControllerBase
|
||||
/// <summary>
|
||||
/// 文件上传路径
|
||||
/// </summary>
|
||||
protected static string UploadsDirectory => uploadsDirectory ??= Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "uploads");
|
||||
protected static string UploadsDirectory => uploadsDirectory ??= Path.Combine(Program.DataPath, "uploads");
|
||||
|
||||
/// <summary>
|
||||
/// 支持的图片文件签名
|
||||
@@ -31,6 +35,7 @@ public abstract partial class BaseController : ControllerBase
|
||||
protected static readonly List<byte[]> PhotoSignatures = new()
|
||||
{
|
||||
// jpeg
|
||||
new byte[] { 0xFF, 0xD8, 0xFF, 0xDB },
|
||||
new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 },
|
||||
new byte[] { 0xFF, 0xD8, 0xFF, 0xE1 },
|
||||
new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 },
|
||||
@@ -195,6 +200,29 @@ public abstract partial class BaseController : ControllerBase
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取嵌入流的数据
|
||||
/// </summary>
|
||||
/// <param name="filename">文件名</param>
|
||||
/// <param name="namespace">命名空间,默认为程序集.wwwroot</param>
|
||||
/// <returns></returns>
|
||||
protected static byte[] GetEmbeddedData(string filename, string? @namespace = null)
|
||||
{
|
||||
Assembly asm = Assembly.GetExecutingAssembly();
|
||||
if (string.IsNullOrEmpty(@namespace))
|
||||
{
|
||||
@namespace = typeof(Program).Namespace + ".wwwroot";
|
||||
}
|
||||
using Stream? stream = asm.GetManifestResourceStream($"{@namespace}.{filename}");
|
||||
if (stream == null)
|
||||
{
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
using var ms = new MemoryStream();
|
||||
stream.CopyTo(ms);
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取文件到 byte 数组
|
||||
/// </summary>
|
||||
@@ -230,7 +258,7 @@ public abstract partial class BaseController : ControllerBase
|
||||
var ext = Path.GetExtension(name);
|
||||
var path = $"{WebUtility.UrlEncode(Path.GetFileNameWithoutExtension(name))}_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}{ext}";
|
||||
|
||||
var image = SkiaSharp.SKImage.FromEncodedData(data);
|
||||
var image = SKImage.FromEncodedData(data);
|
||||
|
||||
return new FileResult
|
||||
{
|
||||
@@ -256,17 +284,57 @@ public abstract partial class BaseController : ControllerBase
|
||||
return directory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建缩略图
|
||||
/// </summary>
|
||||
/// <param name="data">图片数据</param>
|
||||
/// <param name="maxWidth">最大宽度</param>
|
||||
/// <param name="quality">缩放质量</param>
|
||||
/// <returns></returns>
|
||||
protected static byte[] CreateThumbnail(byte[] data, int maxWidth = ThumbWidth, SKFilterQuality quality = SKFilterQuality.Medium)
|
||||
{
|
||||
if (maxWidth <= 0)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
using var bitmap = SKBitmap.Decode(data);
|
||||
if (maxWidth >= bitmap.Width)
|
||||
{
|
||||
using var enc = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80);
|
||||
return enc.ToArray();
|
||||
}
|
||||
var height = maxWidth * bitmap.Height / bitmap.Width;
|
||||
using var image = bitmap.Resize(new SKImageInfo(maxWidth, height), quality);
|
||||
using var encode = image.Encode(SKEncodedImageFormat.Jpeg, 80);
|
||||
return encode.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入文件到用户的花草目录中
|
||||
/// </summary>
|
||||
/// <param name="fid">花草唯一 id</param>
|
||||
/// <param name="file">文件对象</param>
|
||||
/// <param name="thumbnail">创建缩略图</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
protected static async Task WriteToFile(int fid, FileResult file, CancellationToken token = default)
|
||||
protected async Task WriteToFile(int fid, FileResult file, bool thumbnail = true, CancellationToken token = default)
|
||||
{
|
||||
var directory = GetFlowerDirectory(fid, true);
|
||||
var path = Path.Combine(directory, file.Path);
|
||||
await System.IO.File.WriteAllBytesAsync(path, file.Content, token);
|
||||
|
||||
if (thumbnail)
|
||||
{
|
||||
try
|
||||
{
|
||||
var thumb = CreateThumbnail(file.Content);
|
||||
path = Path.Combine(directory, $"{file.Path}.thumb");
|
||||
await System.IO.File.WriteAllBytesAsync(path, thumb, token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger?.LogError(ex, "failed to create thumbnail for flower: {fid}, file: {file}, error: {message}", fid, file.Filename, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@@ -52,7 +52,7 @@ partial class BaseController
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户头像数据
|
||||
/// 根据用户 uid 获取用户头像数据
|
||||
/// </summary>
|
||||
/// <param name="uid">用户唯一 id</param>
|
||||
/// <returns></returns>
|
||||
@@ -61,6 +61,16 @@ partial class BaseController
|
||||
return database.Database.SqlQuery<byte[]>($"SELECT \"avatar\" AS \"Value\" FROM \"users\" WHERE \"uid\" = {uid} LIMIT 1").SingleOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据用户 id 获取用户头像数据
|
||||
/// </summary>
|
||||
/// <param name="id">用户 id</param>
|
||||
/// <returns></returns>
|
||||
protected byte[]? QueryUserAvatar(string id)
|
||||
{
|
||||
return database.Database.SqlQuery<byte[]>($"SELECT \"avatar\" AS \"Value\" FROM \"users\" WHERE \"id\" = {id} LIMIT 1").SingleOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除用户头像
|
||||
/// </summary>
|
||||
|
@@ -316,7 +316,7 @@ public class EventApiController : BaseController
|
||||
};
|
||||
AddPhotoItem(p);
|
||||
|
||||
await WriteToFile(@event.FlowerId, file, token);
|
||||
await WriteToFile(@event.FlowerId, file, token: token);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -443,7 +443,7 @@ public class EventApiController : BaseController
|
||||
};
|
||||
AddPhotoItem(cover);
|
||||
|
||||
await WriteToFile(update.FlowerId, file, token);
|
||||
await WriteToFile(update.FlowerId, file, token: token);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -541,7 +541,7 @@ public class EventApiController : BaseController
|
||||
};
|
||||
AddPhotoItem(p);
|
||||
|
||||
await WriteToFile(record.FlowerId, file, token);
|
||||
await WriteToFile(record.FlowerId, file, token: token);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -639,7 +639,7 @@ public class EventApiController : BaseController
|
||||
};
|
||||
AddPhotoItem(p);
|
||||
|
||||
await WriteToFile(record.FlowerId, file, token);
|
||||
await WriteToFile(record.FlowerId, file, token: token);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@@ -235,7 +235,7 @@ public class FlowerApiController : BaseController
|
||||
f.Photos = database.Photos.Where(p => p.FlowerId == f.Id && p.RecordId == null).ToList();
|
||||
foreach (var photo in f.Photos)
|
||||
{
|
||||
photo.Url = $"photo/flower/{f.Id}/{photo.Path}";
|
||||
photo.Url = $"photo/flower/{f.Id}/{photo.Path}?thumb=1";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -532,7 +532,7 @@ public class FlowerApiController : BaseController
|
||||
};
|
||||
AddPhotoItem(cover);
|
||||
|
||||
await WriteToFile(item.Id, file, token);
|
||||
await WriteToFile(item.Id, file, token: token);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -753,7 +753,7 @@ public class FlowerApiController : BaseController
|
||||
};
|
||||
AddPhotoItem(cover);
|
||||
|
||||
await WriteToFile(update.Id, file, token);
|
||||
await WriteToFile(update.Id, file, token: token);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -854,7 +854,7 @@ public class FlowerApiController : BaseController
|
||||
};
|
||||
AddPhotoItem(cover);
|
||||
|
||||
await WriteToFile(param.Id, file, token);
|
||||
await WriteToFile(param.Id, file, token: token);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@@ -8,35 +8,36 @@ namespace Blahblah.FlowerStory.Server.Controller;
|
||||
/// <summary>
|
||||
/// 图片相关服务
|
||||
/// </summary>
|
||||
[Produces("image/png")]
|
||||
[Route("photo")]
|
||||
public class ImageController : BaseController
|
||||
{
|
||||
static byte[]? emptyAvatar;
|
||||
|
||||
static byte[] EmptyAvatar => emptyAvatar ??= GetEmbeddedData("image.avatar.jpg");
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ImageController(FlowerDatabase database, ILogger<BaseController>? logger = null) : base(database, logger)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求用户头像
|
||||
/// 请求自己的头像
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// GET /photo/avatar
|
||||
/// GET /photo/my_avatar
|
||||
/// Authorization: authorization id
|
||||
///
|
||||
/// </remarks>
|
||||
/// <returns>认证通过则显示用户头像</returns>
|
||||
/// <returns>认证通过则显示自己的头像</returns>
|
||||
/// <response code="200">返回头像</response>
|
||||
/// <response code="401">认证失败</response>
|
||||
/// <response code="404">未找到头像</response>
|
||||
[Route("avatar", Name = "getAvatar")]
|
||||
[Route("my_avatar", Name = "getMyAvatar")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpGet]
|
||||
public ActionResult GetUserAvatar()
|
||||
public ActionResult GetMyAvatar()
|
||||
{
|
||||
var (result, token) = CheckToken();
|
||||
if (result != null)
|
||||
@@ -48,11 +49,47 @@ public class ImageController : BaseController
|
||||
return Unauthorized();
|
||||
}
|
||||
var avatar = QueryUserAvatar(token.UserId);
|
||||
if (avatar == null)
|
||||
if (avatar?.Length > 0)
|
||||
{
|
||||
return NotFound();
|
||||
return File(avatar, "image/jpeg");
|
||||
}
|
||||
return File(avatar, "image/png");
|
||||
return File(EmptyAvatar, "image/jpeg");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求用户头像
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// GET /photo/avatar/2.jpg
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="uid">用户唯一 id</param>
|
||||
/// <returns>认证通过则显示用户头像</returns>
|
||||
/// <response code="200">返回头像</response>
|
||||
/// <response code="401">认证失败</response>
|
||||
[Route("avatar/{uid}.jpg", Name = "getAvatar")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[HttpGet]
|
||||
public ActionResult GetAvatar([Required] int uid)
|
||||
{
|
||||
//var (result, token) = CheckToken();
|
||||
//if (result != null)
|
||||
//{
|
||||
// return result;
|
||||
//}
|
||||
//if (token == null)
|
||||
//{
|
||||
// return Unauthorized();
|
||||
//}
|
||||
var avatar = QueryUserAvatar(uid);
|
||||
if (avatar?.Length > 0)
|
||||
{
|
||||
return File(avatar, "image/jpeg");
|
||||
}
|
||||
return File(EmptyAvatar, "image/jpeg");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -61,11 +98,12 @@ public class ImageController : BaseController
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// GET /photo/flower/{fid}/{name}
|
||||
/// GET /photo/flower/1/test.jpg
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="fid">花草唯一 id</param>
|
||||
/// <param name="name">照片名称</param>
|
||||
/// <param name="thumb">是否为缩略图</param>
|
||||
/// <returns>认证通过则显示花草照片</returns>
|
||||
/// <response code="200">返回花草照片</response>
|
||||
/// <response code="401">认证失败</response>
|
||||
@@ -75,7 +113,7 @@ public class ImageController : BaseController
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpGet]
|
||||
public async Task<ActionResult> GetFlowerPhoto([Required] int fid, [Required] string name)
|
||||
public async Task<ActionResult> GetFlowerPhoto([Required] int fid, [Required] string name, [FromQuery] string? thumb = null)
|
||||
{
|
||||
//var (result, token) = CheckToken();
|
||||
//if (result != null)
|
||||
@@ -87,7 +125,7 @@ public class ImageController : BaseController
|
||||
// return Unauthorized();
|
||||
//}
|
||||
|
||||
#if !DEBUG
|
||||
#if PRODUCTION
|
||||
var referrer = Request.Headers.Referer.ToString();
|
||||
if (string.IsNullOrEmpty(referrer))
|
||||
{
|
||||
@@ -98,12 +136,41 @@ public class ImageController : BaseController
|
||||
return Forbid();
|
||||
}
|
||||
#endif
|
||||
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "uploads", fid.ToString(), WebUtility.UrlEncode(name));
|
||||
if (System.IO.File.Exists(path))
|
||||
var filename = WebUtility.UrlEncode(name);
|
||||
var directory = Path.Combine(Program.DataPath, "uploads", fid.ToString());
|
||||
var original = Path.Combine(directory, filename);
|
||||
var thumbnail = Path.Combine(directory, $"{filename}.thumb");
|
||||
if (!string.IsNullOrEmpty(thumb))
|
||||
{
|
||||
var data = await System.IO.File.ReadAllBytesAsync(path);
|
||||
var ext = Path.GetExtension(path).ToLower();
|
||||
return File(data, ext == ".png" ? "image/png" : "image/jpeg");
|
||||
if (System.IO.File.Exists(thumbnail))
|
||||
{
|
||||
var thumbnailData = await System.IO.File.ReadAllBytesAsync(thumbnail);
|
||||
return File(thumbnailData, "image/jpeg");
|
||||
}
|
||||
else if (System.IO.File.Exists(original))
|
||||
{
|
||||
try
|
||||
{
|
||||
var originalData = await System.IO.File.ReadAllBytesAsync(original);
|
||||
var thumbnailData = CreateThumbnail(originalData);
|
||||
await System.IO.File.WriteAllBytesAsync(thumbnail, thumbnailData);
|
||||
return File(thumbnailData, "image/jpeg");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger?.LogWarning(ex, "failed to create thumbnail for flower: {fid}, name: {name}, error: {message}", fid, name, ex.Message);
|
||||
}
|
||||
}
|
||||
return NotFound();
|
||||
}
|
||||
if (System.IO.File.Exists(original))
|
||||
{
|
||||
var data = await System.IO.File.ReadAllBytesAsync(original);
|
||||
return Path.GetExtension(original).ToLower() switch
|
||||
{
|
||||
".png" => File(data, "image/png"),
|
||||
_ => File(data, "image/jpeg"),
|
||||
};
|
||||
}
|
||||
return NotFound();
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using Blahblah.FlowerStory.Server.Data;
|
||||
using Blahblah.FlowerStory.Server.Data.Model;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
@@ -81,6 +82,9 @@ public partial class UserApiController : BaseController
|
||||
clientApp = "browser";
|
||||
expires = 20 * 60; // 20 mins
|
||||
}
|
||||
|
||||
database.Tokens.Where(t => t.UserId == user.Id && t.ClientApp == clientApp).ExecuteDelete();
|
||||
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var token = new TokenItem
|
||||
{
|
||||
@@ -184,26 +188,31 @@ public partial class UserApiController : BaseController
|
||||
/// 请求示例:
|
||||
///
|
||||
/// POST /api/user/register
|
||||
/// {
|
||||
/// "id": "blahblah",
|
||||
/// "password": "pwd123",
|
||||
/// "userName": "Blah blah",
|
||||
/// "email": "blah@example.com",
|
||||
/// "mobile": "18012345678"
|
||||
/// }
|
||||
///
|
||||
/// 参数:
|
||||
///
|
||||
/// id: "blahblah"
|
||||
/// password: "pwd123"
|
||||
/// name: "Blah blah"
|
||||
/// email: "blah@example.com"
|
||||
/// mobile: "18012345678"
|
||||
/// avatar: <avatar>
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="user">注册参数</param>
|
||||
/// <returns>成功注册则返回已注册的用户对象</returns>
|
||||
/// <response code="200">返回已注册的用户对象</response>
|
||||
/// <response code="400">用户头像格式非法</response>
|
||||
/// <response code="500">用户重复或其他服务器错误</response>
|
||||
[Route("register", Name = "register")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
[ProducesErrorResponseType(typeof(ErrorResponse))]
|
||||
[HttpPost]
|
||||
[Consumes("application/json")]
|
||||
public ActionResult<UserItem> Register([FromBody] UserParameter user)
|
||||
[Consumes("multipart/form-data")]
|
||||
[RequestSizeLimit(15 * 1024 * 1024)]
|
||||
public ActionResult<UserItem> Register([FromForm] UserParameter user)
|
||||
{
|
||||
#if DEBUG
|
||||
logger?.LogInformation("user register, {user}", user);
|
||||
@@ -214,6 +223,21 @@ public partial class UserApiController : BaseController
|
||||
return Problem("duplicateUser", "api/user/register");
|
||||
}
|
||||
|
||||
byte[]? data;
|
||||
if (user.Avatar != null)
|
||||
{
|
||||
var avatar = WrapFormFile(user.Avatar);
|
||||
if (avatar == null)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
data = CreateThumbnail(avatar.Content);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = null;
|
||||
}
|
||||
|
||||
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
var item = new UserItem
|
||||
{
|
||||
@@ -224,7 +248,8 @@ public partial class UserApiController : BaseController
|
||||
ActiveDateUnixTime = now,
|
||||
Name = user.UserName,
|
||||
Email = user.Email,
|
||||
Mobile = user.Mobile
|
||||
Mobile = user.Mobile,
|
||||
Avatar = data
|
||||
};
|
||||
database.Users.Add(item);
|
||||
SaveDatabase();
|
||||
@@ -281,30 +306,35 @@ public partial class UserApiController : BaseController
|
||||
///
|
||||
/// PUT /api/user/update
|
||||
/// Authorization: authorization id
|
||||
/// {
|
||||
/// "userName": "Blah blah",
|
||||
/// "email": "blah@example.com",
|
||||
/// "mobile": "18012345678"
|
||||
/// }
|
||||
///
|
||||
/// 参数:
|
||||
///
|
||||
/// name": "Blah blah"
|
||||
/// email": "blah@example.com"
|
||||
/// mobile": "18012345678",
|
||||
/// avatar: <avatar>
|
||||
///
|
||||
/// </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>
|
||||
/// <response code="413">提交正文过大</response>
|
||||
[Route("update", Name = "updateProfile")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
|
||||
[ProducesErrorResponseType(typeof(ErrorResponse))]
|
||||
[HttpPut]
|
||||
[Consumes("application/json")]
|
||||
public ActionResult<UserItem> Update([FromBody] UpdateParameter update)
|
||||
[Consumes("multipart/form-data")]
|
||||
[RequestSizeLimit(15 * 1024 * 1024)]
|
||||
public ActionResult<UserItem> Update([FromForm] UpdateParameter update)
|
||||
{
|
||||
#if DEBUG
|
||||
logger?.LogInformation("user update, {user}", update);
|
||||
@@ -319,6 +349,17 @@ public partial class UserApiController : BaseController
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (update.Avatar != null)
|
||||
{
|
||||
var avatar = WrapFormFile(update.Avatar);
|
||||
if (avatar == null)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
user.Avatar = CreateThumbnail(avatar.Content);
|
||||
}
|
||||
|
||||
user.Name = update.UserName;
|
||||
user.Email = update.Email;
|
||||
user.Mobile = update.Mobile;
|
||||
@@ -382,7 +423,7 @@ public partial class UserApiController : BaseController
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
user.Avatar = file.Content;
|
||||
user.Avatar = CreateThumbnail(file.Content);
|
||||
}
|
||||
SaveDatabase();
|
||||
|
||||
@@ -429,7 +470,7 @@ public partial class UserApiController : BaseController
|
||||
return Ok(count);
|
||||
}
|
||||
|
||||
//#if DEBUG
|
||||
#if !PRODUCTION
|
||||
/// <summary>
|
||||
/// #DEBUG 获取所有用户
|
||||
/// </summary>
|
||||
@@ -453,5 +494,5 @@ public partial class UserApiController : BaseController
|
||||
{
|
||||
return Ok(database.Tokens.ToArray());
|
||||
}
|
||||
//#endif
|
||||
#endif
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Blahblah.FlowerStory.Server.Controller;
|
||||
|
||||
@@ -33,12 +34,14 @@ public record UserParameter : UpdateParameter
|
||||
/// 用户 id
|
||||
/// </summary>
|
||||
[Required]
|
||||
[FromForm(Name = "id")]
|
||||
public required string Id { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 密码
|
||||
/// </summary>
|
||||
[Required]
|
||||
[FromForm(Name = "password")]
|
||||
public required string Password { get; init; }
|
||||
}
|
||||
|
||||
@@ -51,15 +54,24 @@ public record UpdateParameter
|
||||
/// 用户名
|
||||
/// </summary>
|
||||
[Required]
|
||||
[FromForm(Name = "name")]
|
||||
public required string UserName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 邮箱
|
||||
/// </summary>
|
||||
[FromForm(Name = "email")]
|
||||
public string? Email { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 联系电话
|
||||
/// </summary>
|
||||
[FromForm(Name = "mobile")]
|
||||
public string? Mobile { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户头像
|
||||
/// </summary>
|
||||
[FromForm(Name = "avatar")]
|
||||
public IFormFile? Avatar { get; init; }
|
||||
}
|
||||
|
Reference in New Issue
Block a user