Polish UI, harden company creation, and add error pages
This commit is contained in:
@@ -57,6 +57,7 @@ public sealed class AuthController : ControllerBase
|
||||
string? LastName,
|
||||
string? DisplayName,
|
||||
string? ProfileCvText,
|
||||
string? AvatarImageDataUrl,
|
||||
IList<string> Roles,
|
||||
GoogleLinkDto? GoogleLink);
|
||||
public sealed record UpdateProfileRequest(string? Email, string? UserName, string? FirstName, string? LastName, string? DisplayName, string? ProfileCvText);
|
||||
@@ -172,6 +173,7 @@ public sealed class AuthController : ControllerBase
|
||||
LastName: User.FindFirstValue("family_name"),
|
||||
DisplayName: User.FindFirstValue("name"),
|
||||
ProfileCvText: null,
|
||||
AvatarImageDataUrl: null,
|
||||
Roles: Array.Empty<string>(),
|
||||
GoogleLink: provider == "google" ? new GoogleLinkDto(false, email, null) : null));
|
||||
}
|
||||
@@ -277,6 +279,70 @@ public sealed class AuthController : ControllerBase
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost("avatar")]
|
||||
[Authorize(AuthenticationSchemes = "local")]
|
||||
[RequestSizeLimit(5_000_000)]
|
||||
public async Task<IActionResult> UploadAvatar([FromForm] IFormFile? file)
|
||||
{
|
||||
var user = await _users.GetUserAsync(User);
|
||||
if (user is null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
if (file is null || file.Length == 0)
|
||||
{
|
||||
return BadRequest("Image file is required.");
|
||||
}
|
||||
|
||||
if (!string.Equals(file.ContentType, "image/png", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(file.ContentType, "image/jpeg", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(file.ContentType, "image/webp", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return BadRequest("Only PNG, JPEG, or WebP images are supported.");
|
||||
}
|
||||
|
||||
if (file.Length > 5_000_000)
|
||||
{
|
||||
return BadRequest("Avatar image is too large.");
|
||||
}
|
||||
|
||||
await using var stream = file.OpenReadStream();
|
||||
using var memory = new MemoryStream();
|
||||
await stream.CopyToAsync(memory);
|
||||
var bytes = memory.ToArray();
|
||||
var base64 = Convert.ToBase64String(bytes);
|
||||
user.AvatarImageDataUrl = $"data:{file.ContentType};base64,{base64}";
|
||||
|
||||
var result = await _users.UpdateAsync(user);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
return BadRequest(string.Join("; ", result.Errors.Select(e => e.Description)));
|
||||
}
|
||||
|
||||
return Ok(new { avatarImageDataUrl = user.AvatarImageDataUrl });
|
||||
}
|
||||
|
||||
[HttpDelete("avatar")]
|
||||
[Authorize(AuthenticationSchemes = "local")]
|
||||
public async Task<IActionResult> DeleteAvatar()
|
||||
{
|
||||
var user = await _users.GetUserAsync(User);
|
||||
if (user is null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
user.AvatarImageDataUrl = null;
|
||||
var result = await _users.UpdateAsync(user);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
return BadRequest(string.Join("; ", result.Errors.Select(e => e.Description)));
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
public sealed record ChangePasswordRequest(string CurrentPassword, string NewPassword);
|
||||
|
||||
[HttpPost("change-password")]
|
||||
@@ -374,6 +440,7 @@ public sealed class AuthController : ControllerBase
|
||||
LastName: user.LastName,
|
||||
DisplayName: user.DisplayName,
|
||||
ProfileCvText: user.ProfileCvText,
|
||||
AvatarImageDataUrl: user.AvatarImageDataUrl,
|
||||
Roles: roles,
|
||||
GoogleLink: new GoogleLinkDto(
|
||||
Linked: !string.IsNullOrWhiteSpace(user.GoogleSubject),
|
||||
|
||||
Reference in New Issue
Block a user