sync full source from private myoffice
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
namespace MyOffice.Web.Controllers;
|
||||
|
||||
using AutoMapper;
|
||||
using Core;
|
||||
using Core.Extensions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Models.Account;
|
||||
using Models.Item;
|
||||
using Models.Motion;
|
||||
using MyOffice.Web.Infrastructure.Attributes;
|
||||
using Services.Account;
|
||||
using Services.Account.Domain;
|
||||
using Services.Item;
|
||||
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/accounts")]
|
||||
[DefaultFromBody]
|
||||
public class AccountController : BaseApiController
|
||||
{
|
||||
private readonly ILogger<AccountController> _logger;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly AccountService _accountService;
|
||||
private readonly ItemService _itemService;
|
||||
|
||||
public AccountController(
|
||||
ILogger<AccountController> logger,
|
||||
IMapper mapper,
|
||||
AccountService accountService,
|
||||
ItemService itemService
|
||||
)
|
||||
{
|
||||
_logger = logger;
|
||||
_mapper = mapper;
|
||||
_accountService = accountService;
|
||||
_itemService = itemService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ObjectResult> AccountsGet([FromQuery] string category)
|
||||
{
|
||||
var list = await _accountService.GetByCategoryDetailedAsync(UserId, category!.AsGuid());
|
||||
|
||||
return OkResponse(_mapper.Map<List<AccountDetailedViewModel>>(list.OrderBy(x => x.Account.Name).ToList()));
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ObjectResult> AccountGet(string id)
|
||||
{
|
||||
var exec = await _accountService.GetByIdDetailedAsync(UserId, id!.AsGuid());
|
||||
|
||||
return MapGeneralExec(
|
||||
exec,
|
||||
result => OkResponse(_mapper.Map<AccountDetailedViewModel>(result)),
|
||||
notFoundDetail: "Account not found.",
|
||||
failureDetail: "Failed to load account.");
|
||||
}
|
||||
|
||||
[HttpGet("{id}/motions")]
|
||||
public async Task<ObjectResult> MotionsGet(string id, [FromQuery] MotionsGetRequest request)
|
||||
{
|
||||
var exec = await _accountService.GetMotionsAsync(
|
||||
UserId,
|
||||
id!.AsGuid(),
|
||||
request.From.StartOfDay(),
|
||||
request.To.EndOfDay());
|
||||
|
||||
return MapGeneralExec(
|
||||
exec,
|
||||
result => OkResponse(_mapper.Map<List<MotionViewModel>>(result)),
|
||||
notFoundDetail: "Account not found.",
|
||||
failureDetail: "Failed to load motions.");
|
||||
}
|
||||
|
||||
[HttpPost("{id}/motions")]
|
||||
public async Task<ObjectResult> MotionsPost(string id, MotionRequest motion)
|
||||
{
|
||||
var exec = await _accountService.MotionAddAsync(
|
||||
UserId,
|
||||
id.AsGuid(),
|
||||
_mapper.Map<MotionAddUpdate>(motion));
|
||||
|
||||
switch (exec.Status)
|
||||
{
|
||||
case MotionAddStatus.account_not_found:
|
||||
return ProblemNotFoundResponse("Account not found.");
|
||||
case MotionAddStatus.forbidden:
|
||||
return ProblemForbiddenResponse("Write access required.");
|
||||
case MotionAddStatus.failure:
|
||||
return ProblemBadResponse("Adding motion failed.");
|
||||
case MotionAddStatus.success:
|
||||
return Ok(_mapper.Map<MotionViewModel[]>(exec.Result!));
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{id}/motions/{motionId}")]
|
||||
public async Task<ObjectResult> MotionsPut(string id, string motionId, MotionRequest motion)
|
||||
{
|
||||
var exec = await _accountService.MotionUpdateAsync(
|
||||
UserId,
|
||||
id.AsGuid(),
|
||||
motionId.AsGuid(),
|
||||
_mapper.Map<MotionAddUpdate>(motion));
|
||||
|
||||
switch (exec.Status)
|
||||
{
|
||||
case MotionUpdateStatus.not_found:
|
||||
return ProblemNotFoundResponse("Motion not found.");
|
||||
case MotionUpdateStatus.forbidden:
|
||||
return ProblemForbiddenResponse("Write access required.");
|
||||
case MotionUpdateStatus.failure:
|
||||
return ProblemBadResponse("Updating motion failed.");
|
||||
case MotionUpdateStatus.success:
|
||||
return OkResponse(_mapper.Map<List<MotionViewModel>>(exec.Result!));
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{id}/motions/{motionId}")]
|
||||
public async Task<ObjectResult> MotionsDelete(string id, string motionId)
|
||||
{
|
||||
var exec = await _accountService.MotionRemoveAsync(
|
||||
UserId,
|
||||
id.AsGuid(),
|
||||
motionId.AsGuid());
|
||||
|
||||
switch (exec.Status)
|
||||
{
|
||||
case MotionDeleteStatus.not_found:
|
||||
return ProblemNotFoundResponse("Motion not found.");
|
||||
case MotionDeleteStatus.forbidden:
|
||||
return ProblemForbiddenResponse("Write access required.");
|
||||
case MotionDeleteStatus.failure:
|
||||
return ProblemBadResponse("Deliting motion failed.");
|
||||
case MotionDeleteStatus.success:
|
||||
return OkResponse(_mapper.Map<MotionViewModel>(exec.Result!));
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("~/api/items")]
|
||||
public async Task<ObjectResult> FindItems([FromQuery] string term, CancellationToken cancellationToken)
|
||||
{
|
||||
var itemsDto = await _itemService.FindItemsAsync(UserId, term, cancellationToken: cancellationToken);
|
||||
|
||||
var items = _mapper.Map<List<ItemViewModel>>(itemsDto);
|
||||
|
||||
if (term.StartsWith("+") && term.Length > 1)
|
||||
{
|
||||
var foundAccounts = await _accountService.FindAccountsAsync(UserId, term.Substring(1), cancellationToken);
|
||||
var accounts = foundAccounts.OrderByDescending(x => x.Name);
|
||||
|
||||
foreach (var account in accounts)
|
||||
{
|
||||
var accountName = $"+{account.Name}";
|
||||
var item = items.FirstOrDefault(x => x.Name.IsPresent() && x.Name!.Length > 1 && x.Name.Substring(1) == accountName);
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
items.Add(new ItemViewModel
|
||||
{
|
||||
Name = accountName,
|
||||
AccountId = account.Id.ToShort(),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
item.AccountId = account.Id.ToShort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return OkResponse(_mapper
|
||||
.Map<List<ItemViewModel>>(items)
|
||||
.OrderBy(x => x.AccountId)
|
||||
.ThenBy(x => x.Name)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
namespace MyOffice.Web.Controllers;
|
||||
|
||||
using System.Security.Claims;
|
||||
using Data.Repositories.Users;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using OpenIddict.Server.AspNetCore;
|
||||
using OpenIddict.Validation.AspNetCore;
|
||||
using static OpenIddict.Abstractions.OpenIddictConstants;
|
||||
|
||||
[ApiController]
|
||||
public sealed class AuthorizationController : ControllerBase
|
||||
{
|
||||
private readonly IUserRepository _userRepository;
|
||||
|
||||
public AuthorizationController(IUserRepository userRepository)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
}
|
||||
|
||||
[Authorize(AuthenticationSchemes = OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)]
|
||||
[HttpGet("~/connect/authorize")]
|
||||
[HttpPost("~/connect/authorize")]
|
||||
public async Task<IActionResult> Authorize()
|
||||
{
|
||||
var result = await HttpContext.AuthenticateAsync(
|
||||
OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
return Forbid(
|
||||
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
|
||||
properties: new AuthenticationProperties(new Dictionary<string, string?>
|
||||
{
|
||||
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.LoginRequired,
|
||||
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
|
||||
"The user is not authenticated."
|
||||
}));
|
||||
}
|
||||
|
||||
return SignIn(result.Principal!, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
|
||||
}
|
||||
|
||||
[Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)]
|
||||
[HttpGet("~/connect/userinfo")]
|
||||
[HttpPost("~/connect/userinfo")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IActionResult> Userinfo()
|
||||
{
|
||||
var userIdValue = User.FindFirstValue(Claims.Subject);
|
||||
if (!Guid.TryParse(userIdValue, out var userId))
|
||||
return Challenge(authenticationSchemes: OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
|
||||
|
||||
var user = await _userRepository.GetUserAsync(userId);
|
||||
if (user is null)
|
||||
return Challenge(authenticationSchemes: OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
sub = userIdValue,
|
||||
id = userIdValue,
|
||||
email = user.Email,
|
||||
name = user.FullName ?? user.UserName,
|
||||
firstName = user.FirstName,
|
||||
lastName = user.LastName,
|
||||
fullName = user.FullName,
|
||||
phone = user.Phone
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
namespace MyOffice.Web.Controllers;
|
||||
|
||||
using System.Security.Authentication;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Core;
|
||||
using Core.Extensions;
|
||||
using IdentityModel;
|
||||
using MyOffice.Web.Infrastructure.Attributes;
|
||||
using MyOffice.Web.Models;
|
||||
|
||||
[DefaultFromBody]
|
||||
public class BaseApiController : ControllerBase
|
||||
{
|
||||
public Guid UserId
|
||||
{
|
||||
get
|
||||
{
|
||||
var subject = User.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject)?.Value;
|
||||
if (subject.IsPresent() && Guid.TryParse(subject, out var guid)) return guid;
|
||||
|
||||
throw new AuthenticationException("Get UserId failed");
|
||||
}
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
public ObjectResult ProblemBadResponse(string? detail = null)
|
||||
{
|
||||
return Problem(detail, statusCode: StatusCodes.Status400BadRequest);
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
public ObjectResult ProblemNotFoundResponse(string? detail = null)
|
||||
{
|
||||
return Problem(detail ?? "Not found.", statusCode: StatusCodes.Status404NotFound);
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
public ObjectResult ProblemForbiddenResponse(string? detail = null)
|
||||
{
|
||||
return Problem(detail ?? "Forbidden.", statusCode: StatusCodes.Status403Forbidden);
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
public ObjectResult OkResponse(IResponseModel response)
|
||||
{
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
public ObjectResult OkResponse<T>(IEnumerable<T> response) where T : IResponseModel
|
||||
{
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps <see cref="GeneralExecStatus"/> to ProblemDetails status codes:
|
||||
/// not_found → 404, forbidden → 403, failure → 400.
|
||||
/// </summary>
|
||||
protected ObjectResult MapGeneralExec<T>(
|
||||
Exec<T, GeneralExecStatus> exec,
|
||||
Func<T, ObjectResult> onSuccess,
|
||||
string notFoundDetail,
|
||||
string? failureDetail = null,
|
||||
string? forbiddenDetail = null) where T : class
|
||||
{
|
||||
switch (exec.Status)
|
||||
{
|
||||
case GeneralExecStatus.success:
|
||||
return onSuccess(exec.Result!);
|
||||
case GeneralExecStatus.not_found:
|
||||
return ProblemNotFoundResponse(notFoundDetail);
|
||||
case GeneralExecStatus.forbidden:
|
||||
return ProblemForbiddenResponse(forbiddenDetail);
|
||||
case GeneralExecStatus.failure:
|
||||
return ProblemBadResponse(failureDetail ?? "Operation failed.");
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
namespace MyOffice.Web.Controllers;
|
||||
|
||||
using AutoMapper;
|
||||
using Core.Extensions;
|
||||
using Infrastructure.Attributes;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MyOffice.Web.Models.Dashboard;
|
||||
using Services.Dashboard;
|
||||
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/dashboard")]
|
||||
[DefaultFromBody]
|
||||
public class DashboardController : BaseApiController
|
||||
{
|
||||
private readonly ILogger<DashboardController> _logger;
|
||||
private readonly DashboardService _dashboardService;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public DashboardController(
|
||||
ILogger<DashboardController> logger,
|
||||
DashboardService dashboardService,
|
||||
IMapper mapper
|
||||
)
|
||||
{
|
||||
_logger = logger;
|
||||
_dashboardService = dashboardService;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ObjectResult> Index(CancellationToken cancellationToken)
|
||||
{
|
||||
var data = await _dashboardService.GetDashboardRestDataAsync(UserId, cancellationToken);
|
||||
|
||||
return OkResponse(_mapper.Map<DashboardViewModel>(data));
|
||||
}
|
||||
|
||||
[HttpGet("income")]
|
||||
public async Task<ObjectResult> Income(
|
||||
DateTime from,
|
||||
DateTime to,
|
||||
[AsGuid(true)] string? category,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var data = await _dashboardService.GetDashboardIncomeDataAsync(
|
||||
UserId,
|
||||
from.StartOfDay(),
|
||||
to.EndOfDay(),
|
||||
category?.AsGuidNull(),
|
||||
cancellationToken);
|
||||
|
||||
return OkResponse(_mapper.Map<DashboardIncomeDataViewModel>(data));
|
||||
}
|
||||
|
||||
[HttpGet("outcome")]
|
||||
public async Task<ObjectResult> Outcome(
|
||||
DateTime from,
|
||||
DateTime to,
|
||||
[AsGuid(true)] string? category,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var data = await _dashboardService.GetDashboardOutcomeDataAsync(
|
||||
UserId,
|
||||
from.StartOfDay(),
|
||||
to.EndOfDay(),
|
||||
category?.AsGuidNull(),
|
||||
cancellationToken);
|
||||
|
||||
return OkResponse(_mapper.Map<DashboardIncomeDataViewModel>(data));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
namespace MyOffice.Web.Controllers;
|
||||
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Models.Currency;
|
||||
using Services.Currency;
|
||||
using MyOffice.Web.Infrastructure.Attributes;
|
||||
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/general")]
|
||||
[DefaultFromBody]
|
||||
public class GeneralController : BaseApiController
|
||||
{
|
||||
private readonly CurrencyService _currencyService;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public GeneralController(
|
||||
CurrencyService currencyService,
|
||||
IMapper mapper
|
||||
)
|
||||
{
|
||||
_currencyService = currencyService;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
[HttpGet("currencies")]
|
||||
public async Task<ObjectResult> GetGlobalCurrencies(CancellationToken cancellationToken)
|
||||
{
|
||||
var list = await _currencyService.GetGlobalAllAsync(cancellationToken);
|
||||
|
||||
return Ok(_mapper.Map<List<CurrencyGlobalViewModel>>(list));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
namespace MyOffice.Web.Controllers;
|
||||
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
using Core;
|
||||
using Core.Extensions;
|
||||
using Models.Account;
|
||||
using Services.Account;
|
||||
using Services.Account.Domain;
|
||||
using Infrastructure.Attributes;
|
||||
using Services.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/settings/account-categories")]
|
||||
[DefaultFromBody]
|
||||
public class SettingsAccountCategoryController : BaseApiController
|
||||
{
|
||||
private readonly ILogger<SettingsAccountController> _logger;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly AccountService _accountService;
|
||||
private readonly IContextProvider _contextProvider;
|
||||
|
||||
public SettingsAccountCategoryController(
|
||||
ILogger<SettingsAccountController> logger,
|
||||
IMapper mapper,
|
||||
AccountService accountService,
|
||||
IContextProvider contextProvider
|
||||
)
|
||||
{
|
||||
_logger = logger;
|
||||
_mapper = mapper;
|
||||
_accountService = accountService;
|
||||
_contextProvider = contextProvider;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ObjectResult> AccountCategories(CancellationToken cancellationToken)
|
||||
{
|
||||
var list = await _accountService.GetAllCategoriesAsync(UserId, cancellationToken);
|
||||
|
||||
return OkResponse(_mapper.Map<List<AccountCategoryViewModel>>(list).OrderBy(x => x.Name));
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ObjectResult> AccountCategory([AsGuid] string id, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _accountService.GetCategoryAsync(UserId, id.AsGuid(), cancellationToken);
|
||||
|
||||
switch (exec.Status)
|
||||
{
|
||||
case GeneralExecStatus.not_found:
|
||||
case GeneralExecStatus.failure:
|
||||
return ProblemNotFoundResponse("Account category not found.");
|
||||
|
||||
case GeneralExecStatus.success:
|
||||
return OkResponse(_mapper.Map<AccountCategoryViewModel>(exec.Result!));
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ObjectResult> AccountCategoriesAdd(AccountCategoryViewModel request, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _accountService.CategoryAddAsync(UserId, new AccountCategoryDto { Name = request.Name! }, cancellationToken);
|
||||
switch (exec.Status)
|
||||
{
|
||||
case GeneralExecStatus.success:
|
||||
return OkResponse(_mapper.Map<AccountCategoryViewModel>(exec.Result!));
|
||||
|
||||
case GeneralExecStatus.failure:
|
||||
case GeneralExecStatus.not_found:
|
||||
return ProblemBadResponse("Adding account category failed.");
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<ObjectResult> AccountCategoriesUpdate([AsGuid] string id, AccountCategoryViewModel request, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _accountService.CategoryUpdateAsync(UserId, id.AsGuid(), new AccountCategoryDto { Name = request.Name! }, cancellationToken);
|
||||
switch (exec.Status)
|
||||
{
|
||||
case GeneralExecStatus.success:
|
||||
return OkResponse(_mapper.Map<AccountCategoryViewModel>(exec.Result!));
|
||||
|
||||
case GeneralExecStatus.failure:
|
||||
case GeneralExecStatus.not_found:
|
||||
return ProblemBadResponse("Update account category failed.");
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<ObjectResult> AccountCategoriesDelete([AsGuid] string id, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _accountService.CategoryRemoveAsync(UserId, id.AsGuid(), cancellationToken);
|
||||
switch (exec.Status)
|
||||
{
|
||||
case AccountCategoryRemoveResult.success:
|
||||
return OkResponse(_mapper.Map<AccountCategoryViewModel>(exec.Result!));
|
||||
|
||||
case AccountCategoryRemoveResult.failure:
|
||||
return ProblemBadResponse("Remove account category failed.");
|
||||
case AccountCategoryRemoveResult.not_found:
|
||||
return ProblemNotFoundResponse("Account category not found.");
|
||||
case AccountCategoryRemoveResult.accounts_exists:
|
||||
return ProblemBadResponse("Account category have accounts.");
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
namespace MyOffice.Web.Controllers;
|
||||
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
using Core;
|
||||
using Core.Extensions;
|
||||
using Models.Account;
|
||||
using Services.Account;
|
||||
using Services.Account.Domain;
|
||||
using Infrastructure.Attributes;
|
||||
using Services.Identity;
|
||||
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/settings/accounts")]
|
||||
[DefaultFromBody]
|
||||
public class SettingsAccountController : BaseApiController
|
||||
{
|
||||
private readonly ILogger<SettingsAccountController> _logger;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly AccountService _accountService;
|
||||
private readonly IContextProvider _contextProvider;
|
||||
|
||||
public SettingsAccountController(
|
||||
ILogger<SettingsAccountController> logger,
|
||||
IMapper mapper,
|
||||
AccountService accountService,
|
||||
IContextProvider contextProvider
|
||||
)
|
||||
{
|
||||
_logger = logger;
|
||||
_mapper = mapper;
|
||||
_accountService = accountService;
|
||||
_contextProvider = contextProvider;
|
||||
}
|
||||
|
||||
[HttpGet("invites")]
|
||||
public async Task<ObjectResult> AccountInvites(CancellationToken cancellationToken)
|
||||
{
|
||||
var invites = await _accountService.InvitesGetAsync(_contextProvider.User.Email, cancellationToken);
|
||||
|
||||
return OkResponse(_mapper.Map<List<AccountAccessInviteViewModel>>(invites));
|
||||
}
|
||||
|
||||
[HttpPost("invites/{id}/accept")]
|
||||
public async Task<ObjectResult> AccountInviteAccept([AsGuid] string id, AccountInviteAcceptRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _accountService.InviteAcceptAsync(UserId, id.AsGuid(), request.Name, cancellationToken);
|
||||
|
||||
switch (exec.Status)
|
||||
{
|
||||
case InviteAcceptStatus.invite_not_found:
|
||||
return ProblemNotFoundResponse("Invite not found.");
|
||||
case InviteAcceptStatus.account_not_found:
|
||||
return ProblemNotFoundResponse("Account not found.");
|
||||
|
||||
case InviteAcceptStatus.already_accepted:
|
||||
case InviteAcceptStatus.success:
|
||||
return OkResponse(_mapper.Map<AccountViewModel>(exec.Result!));
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("invites/{id}/reject")]
|
||||
public async Task<ObjectResult> AccountInviteReject([AsGuid] string id, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _accountService.InviteRejectAsync(id.AsGuid(), cancellationToken);
|
||||
|
||||
return MapGeneralExec(
|
||||
exec,
|
||||
result => OkResponse(_mapper.Map<AccountAccessInviteViewModel>(result)),
|
||||
notFoundDetail: "Invite not found.",
|
||||
failureDetail: "Invite reject failed.");
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ObjectResult> AccountsGet([FromQuery][AsGuid(true)] string? category, CancellationToken cancellationToken)
|
||||
{
|
||||
var list = category.IsPresent()
|
||||
? await _accountService.GetByCategoryAsync(UserId, category!.AsGuid(), cancellationToken)
|
||||
: await _accountService.GetAllAccountsAsync(UserId, cancellationToken);
|
||||
|
||||
return OkResponse(_mapper.Map<List<AccountViewModel>>(list.OrderBy(x => x.Name)));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ObjectResult> AccountsAdd(AccountViewModel request, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _accountService.AccountAddAsync(UserId, new AccountAdd
|
||||
{
|
||||
Name = request.Name,
|
||||
CurrencyId = request.CurrencyId,
|
||||
CategoryId = request.CategoryId.AsGuid(),
|
||||
Type = request.Type,
|
||||
}, cancellationToken);
|
||||
|
||||
switch (exec.Status)
|
||||
{
|
||||
case AccountAddStatus.category_not_found:
|
||||
return ProblemNotFoundResponse("Category not found.");
|
||||
case AccountAddStatus.currency_not_found:
|
||||
return ProblemNotFoundResponse("Currency not found.");
|
||||
case AccountAddStatus.failure:
|
||||
return ProblemBadResponse("Adding account failed.");
|
||||
case AccountAddStatus.success:
|
||||
return OkResponse(_mapper.Map<AccountViewModel>(exec.Result!));
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<ObjectResult> AccountsUpdate([AsGuid] string id, AccountEditRequestModel request, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _accountService.AccountUpdateAsync(UserId, id.AsGuid(), new AccountEdit
|
||||
{
|
||||
Name = request.Name,
|
||||
CurrencyId = request.CurrencyId,
|
||||
CategoryId = request.CategoryId?.AsGuidNull(),
|
||||
UserId = request.UserId?.AsGuidNull(),
|
||||
Type = request.Type,
|
||||
}, cancellationToken);
|
||||
|
||||
switch (exec.Status)
|
||||
{
|
||||
case AccountEditStatus.not_found:
|
||||
return ProblemNotFoundResponse("Account not found.");
|
||||
case AccountEditStatus.forbidden:
|
||||
return ProblemForbiddenResponse("Manage access required.");
|
||||
case AccountEditStatus.category_not_found:
|
||||
return ProblemNotFoundResponse("Category not found.");
|
||||
case AccountEditStatus.currency_not_found:
|
||||
return ProblemNotFoundResponse("Currency not found.");
|
||||
case AccountEditStatus.failure:
|
||||
return ProblemBadResponse("Adding account failed.");
|
||||
case AccountEditStatus.success:
|
||||
return OkResponse(_mapper.Map<AccountViewModel>(exec.Result!));
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<ObjectResult> AccountsDelete([AsGuid] string id, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _accountService.AccountDeleteAsync(UserId, id, cancellationToken);
|
||||
return MapGeneralExec(
|
||||
exec,
|
||||
result => OkResponse(_mapper.Map<AccountViewModel>(result)),
|
||||
notFoundDetail: "Account not found.",
|
||||
forbiddenDetail: "Manage access required.");
|
||||
}
|
||||
|
||||
[HttpDelete("{id}/category/{categoryId}")]
|
||||
public async Task<ObjectResult> AccountsCategoryDelete([AsGuid] string id, [AsGuid] string categoryId, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _accountService.AccountCategoryRemoveAsync(UserId, id.AsGuid(), categoryId.AsGuid(), cancellationToken);
|
||||
|
||||
return MapGeneralExec(
|
||||
exec,
|
||||
result => OkResponse(_mapper.Map<AccountViewModel>(result)),
|
||||
notFoundDetail: "Category not found.",
|
||||
failureDetail: "Category remove failed.",
|
||||
forbiddenDetail: "Manage access required.");
|
||||
}
|
||||
|
||||
[HttpPost("{id}/access")]
|
||||
public async Task<ObjectResult> AccountsAccessAdd([AsGuid] string id, AccountAccessViewModel request, CancellationToken cancellationToken)
|
||||
{
|
||||
var model = _mapper.Map<List<AccountAccessDto>>(request.Accesses);
|
||||
|
||||
var exec = await _accountService.AccessUpdateAsync(UserId, id.AsGuid(), model, cancellationToken);
|
||||
|
||||
if (exec.Status != GeneralExecStatus.success)
|
||||
{
|
||||
return MapGeneralExec(
|
||||
exec,
|
||||
_ => OkResponse(_mapper.Map<AccountViewModel>(exec.Result!)),
|
||||
notFoundDetail: "Account not found.",
|
||||
failureDetail: "Access update failed.",
|
||||
forbiddenDetail: "Manage access required.");
|
||||
}
|
||||
|
||||
if (!request.Email.IsPresent())
|
||||
{
|
||||
return OkResponse(_mapper.Map<AccountViewModel>(exec.Result!));
|
||||
}
|
||||
|
||||
var inviteExec = await _accountService.AccessInviteAsync(UserId, id.AsGuid(), request.Email!, request.AllowWrite, cancellationToken);
|
||||
|
||||
switch (inviteExec.Status)
|
||||
{
|
||||
case AccessInviteStatus.account_not_found:
|
||||
return ProblemNotFoundResponse("Account not found.");
|
||||
case AccessInviteStatus.forbidden:
|
||||
return ProblemForbiddenResponse("Manage access required.");
|
||||
|
||||
case AccessInviteStatus.access_exists:
|
||||
case AccessInviteStatus.invite_exists:
|
||||
case AccessInviteStatus.success:
|
||||
return OkResponse(_mapper.Map<AccountViewModel>(exec.Result!));
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{id}/access/{userId}")]
|
||||
public async Task<ObjectResult> AccountsAccessDelete([AsGuid] string id, [AsGuid] string userId, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _accountService.AccessDeleteAsync(UserId, id.AsGuid(), userId.AsGuid(), cancellationToken);
|
||||
|
||||
return MapGeneralExec(
|
||||
exec,
|
||||
result => OkResponse(_mapper.Map<AccountViewModel>(result)),
|
||||
notFoundDetail: "Account not found.",
|
||||
failureDetail: "Access delete failed.",
|
||||
forbiddenDetail: "Manage access required.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
namespace MyOffice.Web.Controllers;
|
||||
|
||||
using AutoMapper;
|
||||
using Core;
|
||||
using Core.Extensions;
|
||||
using Infrastructure.Attributes;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Models.Currency;
|
||||
using Services.Currency;
|
||||
using Services.Currency.Domain;
|
||||
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/settings/currencies")]
|
||||
[DefaultFromBody]
|
||||
public class SettingCurrencyController : BaseApiController
|
||||
{
|
||||
private readonly CurrencyService _currencyService;
|
||||
private readonly ILogger<SettingCurrencyController> _logger;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public SettingCurrencyController(
|
||||
CurrencyService currencyService,
|
||||
ILogger<SettingCurrencyController> logger,
|
||||
IMapper mapper
|
||||
)
|
||||
{
|
||||
_currencyService = currencyService;
|
||||
_logger = logger;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ObjectResult> Get(CancellationToken cancellationToken)
|
||||
{
|
||||
var list = await _currencyService.GetAllWithRatesAsync(UserId, cancellationToken);
|
||||
|
||||
return OkResponse(_mapper.Map<List<CurrencyViewModel>>(list).OrderBy(x => x.Id));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ObjectResult> Add(CurrencyAddModel currency, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _currencyService.CurrencyAddAsync(UserId, new CurrencyDto
|
||||
{
|
||||
UserId = UserId,
|
||||
CurrencyGlobalId = currency.Id,
|
||||
Name = currency.Name,
|
||||
ShortName = currency.ShortName,
|
||||
}, cancellationToken);
|
||||
|
||||
switch (exec.Status)
|
||||
{
|
||||
case CurrencyAddStatus.success:
|
||||
case CurrencyAddStatus.exists:
|
||||
await _currencyService.CurrencyRateAddAsync(UserId, exec.Result!.Id, new CurrencyRateDto
|
||||
{
|
||||
CurrencyId = exec.Result!.Id,
|
||||
Rate = currency.Rate,
|
||||
Quantity = currency.Quantity,
|
||||
DateTime = currency.RateDate.Date,
|
||||
}, cancellationToken);
|
||||
return OkResponse(_mapper.Map<CurrencyViewModel>(exec.Result!));
|
||||
|
||||
case CurrencyAddStatus.failed:
|
||||
return ProblemBadResponse("Adding currency failed.");
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<ObjectResult> Update(string id, CurrencyEditModel currency, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _currencyService.CurrencyUpdateAsync(
|
||||
UserId,
|
||||
id.AsGuid(),
|
||||
new CurrencyEdit { Name = currency.Name, ShortName = currency.ShortName, IsPrimary = currency.IsPrimary },
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
switch (exec.Status)
|
||||
{
|
||||
case CurrencyEditStatus.success:
|
||||
return OkResponse(_mapper.Map<CurrencyViewModel>(exec.Result!));
|
||||
|
||||
case CurrencyEditStatus.not_found:
|
||||
case CurrencyEditStatus.failed:
|
||||
return ProblemNotFoundResponse("Currency not found.");
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<ObjectResult> Delete([AsGuid] string id, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _currencyService.RemoveAsync(UserId, id.AsGuid(), cancellationToken);
|
||||
|
||||
switch (exec.Status)
|
||||
{
|
||||
case GeneralExecStatus.success:
|
||||
return OkResponse(_mapper.Map<CurrencyViewModel>(exec.Result!));
|
||||
|
||||
case GeneralExecStatus.not_found:
|
||||
case GeneralExecStatus.failure:
|
||||
return ProblemNotFoundResponse("Currency not found.");
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("{id}/rate")]
|
||||
public async Task<ObjectResult> AddRate(string id, CurrencyRateModel currencyRate, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _currencyService.CurrencyRateAddAsync(UserId, id.AsGuid(), new CurrencyRateDto
|
||||
{
|
||||
CurrencyId = id.AsGuid(),
|
||||
Quantity = currencyRate.Quantity,
|
||||
Rate = currencyRate.Rate,
|
||||
DateTime = currencyRate.RateDate.Date,
|
||||
}, cancellationToken);
|
||||
|
||||
switch (exec.Status)
|
||||
{
|
||||
case CurrencyAddRateStatus.failed:
|
||||
return ProblemBadResponse("Adding currency rate failed.");
|
||||
case CurrencyAddRateStatus.not_found:
|
||||
return ProblemNotFoundResponse("Currency not found.");
|
||||
|
||||
case CurrencyAddRateStatus.success:
|
||||
return OkResponse(_mapper.Map<CurrencyRateViewModel>(exec.Result!));
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
namespace MyOffice.Web.Controllers;
|
||||
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Models.Item;
|
||||
using Core.Extensions;
|
||||
using Core;
|
||||
using Infrastructure.Attributes;
|
||||
using Services.Item;
|
||||
using Services.Item.Domain;
|
||||
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/settings")]
|
||||
[DefaultFromBody]
|
||||
public class SettingsItemController : BaseApiController
|
||||
{
|
||||
private ILogger<SettingsItemController> _logger;
|
||||
private IMapper _mapper;
|
||||
private ItemService _itemService;
|
||||
|
||||
public SettingsItemController(
|
||||
ILogger<SettingsItemController> logger,
|
||||
ItemService itemService,
|
||||
IMapper mapper
|
||||
)
|
||||
{
|
||||
_itemService = itemService;
|
||||
_logger = logger;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
[HttpGet("item-categories")]
|
||||
public async Task<ObjectResult> ItemsCategories(CancellationToken cancellationToken)
|
||||
{
|
||||
var list = await _itemService.GetAllCategoriesAsync(UserId, cancellationToken);
|
||||
|
||||
return Ok(_mapper.Map<List<ItemCategoryViewModel>>(list)
|
||||
.OrderByDescending(x => x.SortOrder)
|
||||
.ThenBy(x => x.Name));
|
||||
}
|
||||
|
||||
[HttpPost("item-categories")]
|
||||
public async Task<ObjectResult> ItemCategoriesAdd(ItemCategoryViewModel request, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _itemService.CategoryAddAsync(UserId, request.FromModel(), cancellationToken);
|
||||
|
||||
switch (exec.Status)
|
||||
{
|
||||
case GeneralExecStatus.success:
|
||||
return Ok(_mapper.Map<ItemCategoryViewModel>(exec.Result!));
|
||||
|
||||
case GeneralExecStatus.failure:
|
||||
case GeneralExecStatus.not_found:
|
||||
return ProblemBadResponse("Adding item category failed.");
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("item-categories/{id}")]
|
||||
public async Task<ObjectResult> ItemCategoriesUpdate(
|
||||
[AsGuid] string id,
|
||||
ItemCategoryViewModel request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _itemService.CategoryUpdateAsync(UserId, id.AsGuid(), request.FromModel(), cancellationToken);
|
||||
|
||||
switch (exec.Status)
|
||||
{
|
||||
case GeneralExecStatus.success:
|
||||
return Ok(_mapper.Map<ItemCategoryViewModel>(exec.Result!));
|
||||
|
||||
case GeneralExecStatus.failure:
|
||||
case GeneralExecStatus.not_found:
|
||||
return ProblemBadResponse("Update item category failed.");
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("item-categories/{id}")]
|
||||
public async Task<ObjectResult> ItemCategoriesDelete([AsGuid] string id, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _itemService.CategoryRemoveAsync(UserId, id.AsGuid(), cancellationToken);
|
||||
switch (exec.Status)
|
||||
{
|
||||
case ItemCategoryRemoveResult.success:
|
||||
return Ok(_mapper.Map<ItemCategoryViewModel>(exec.Result!));
|
||||
|
||||
case ItemCategoryRemoveResult.failure:
|
||||
return ProblemBadResponse("Remove item category failed.");
|
||||
case ItemCategoryRemoveResult.not_found:
|
||||
return ProblemNotFoundResponse("Account item not found.");
|
||||
case ItemCategoryRemoveResult.accounts_exists:
|
||||
return ProblemBadResponse("Account motion have accounts.");
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("items")]
|
||||
public async Task<ObjectResult> Items([FromQuery][AsGuid] string category, CancellationToken cancellationToken)
|
||||
{
|
||||
var categoryId = category.AsGuid();
|
||||
|
||||
var list = categoryId != UserId
|
||||
? await _itemService.GetByCategoryAsync(UserId, categoryId, cancellationToken)
|
||||
: await _itemService.GetByUncategorizedAsync(UserId, cancellationToken);
|
||||
|
||||
return Ok(_mapper.Map<List<ItemViewModel>>(list.OrderBy(x => x.Name)));
|
||||
}
|
||||
|
||||
[HttpPut("items/{id}")]
|
||||
public async Task<ObjectResult> ItemsUpdate(
|
||||
[AsGuid] string id,
|
||||
ItemEditModel request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _itemService.UpdateAsync(UserId, id.AsGuid(), request.Category.AsGuid(), cancellationToken);
|
||||
|
||||
switch (exec.Status)
|
||||
{
|
||||
case GeneralExecStatus.not_found:
|
||||
return ProblemNotFoundResponse("Item not found.");
|
||||
case GeneralExecStatus.failure:
|
||||
return ProblemBadResponse("Item update failed.");
|
||||
case GeneralExecStatus.success:
|
||||
return Ok(_mapper.Map<ItemViewModel>(exec.Result!));
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("items")]
|
||||
public async Task<ObjectResult> ItemsPost(ItemChangeCategoryModel request, CancellationToken cancellationToken)
|
||||
{
|
||||
var exec = await _itemService.UpdateItemsCategoryAsync(
|
||||
UserId,
|
||||
request.category.AsGuid(),
|
||||
request.Items.Select(x => x.AsGuid()).ToList(),
|
||||
cancellationToken);
|
||||
|
||||
switch (exec)
|
||||
{
|
||||
case GeneralExecStatus.not_found:
|
||||
return ProblemNotFoundResponse("Category not found.");
|
||||
case GeneralExecStatus.failure:
|
||||
return ProblemBadResponse("Update failed.");
|
||||
case GeneralExecStatus.success:
|
||||
return Ok(new {});
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(exec.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
namespace MyOffice.Web.Controllers;
|
||||
|
||||
using Models.Auth;
|
||||
using Identity.Domain;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using MyOffice.Data.Models.Users;
|
||||
using Core.Extensions;
|
||||
using MyOffice.Core.Identity;
|
||||
using Core;
|
||||
using Services.Users;
|
||||
using Services.Users.Domain;
|
||||
using MyOffice.Data.Models.Currencies;
|
||||
using MyOffice.Web.Infrastructure.Attributes;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/user")]
|
||||
[DefaultFromBody]
|
||||
public class UserController : BaseApiController
|
||||
{
|
||||
private readonly UserManager<ApplicationUser<Guid>> _userManager;
|
||||
private readonly UserService _userService;
|
||||
private readonly IEnumerable<IExternalProviderValidator> _externalProviderValidators;
|
||||
|
||||
public UserController(
|
||||
UserManager<ApplicationUser<Guid>> userManager,
|
||||
UserService userService,
|
||||
IEnumerable<IExternalProviderValidator> externalProviderValidators
|
||||
)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_userService = userService;
|
||||
_externalProviderValidators = externalProviderValidators;
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("register")]
|
||||
public async Task<IActionResult> Register([FromBody] RegisterModel request)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return BadRequest(new
|
||||
{
|
||||
Succeeded = false,
|
||||
Errors = ModelState.Values
|
||||
.SelectMany(v => v.Errors)
|
||||
.Select(e => new { Code = "Validation", Description = e.ErrorMessage })
|
||||
});
|
||||
|
||||
var email = request.UserName.Trim();
|
||||
var user = new ApplicationUser<Guid>
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserName = email,
|
||||
Email = email,
|
||||
IsEmailConfirmed = true,
|
||||
CurrencyId = CurrencyGlobalIdEnum.USD.ToString(),
|
||||
};
|
||||
|
||||
var result = await _userManager.CreateAsync(user, request.Password);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
result.Succeeded,
|
||||
Errors = result.Errors
|
||||
.Where(x => !x.Code.Equals("DuplicateUserName"))
|
||||
.Select(x => new { x.Code, x.Description })
|
||||
});
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpGet("profile")]
|
||||
public async Task<object> ProfileGet()
|
||||
{
|
||||
var user = await _userManager.FindByIdAsync(UserId.ToString());
|
||||
|
||||
return user.ToModel(await _userService.GetUserExternals(user.Id));
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost("profile")]
|
||||
public async Task<object> ProfileUpdate([FromBody] ProfileModel model)
|
||||
{
|
||||
var user = await _userManager.FindByIdAsync(UserId.ToString()); ;
|
||||
|
||||
user.FirstName = model.FirstName;
|
||||
user.LastName = model.LastName;
|
||||
user.FullName = model.FullName.NullIfEmpty() ?? $"{model.FirstName} {model.LastName}";
|
||||
user.CurrencyId = model.Currency;
|
||||
|
||||
await _userManager.UpdateAsync(user);
|
||||
|
||||
return user.ToModel(await _userService.GetUserExternals(user.Id));
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost("attach")]
|
||||
public async Task<object> AttachProvider([FromBody] AttachModel model)
|
||||
{
|
||||
var validator = _externalProviderValidators.FirstOrDefault(x => x.Provider.EqualsIgnoreCase(model.Provider));
|
||||
if (validator == null) return ProblemBadResponse("Provider not supported");
|
||||
|
||||
var result = await validator.ValidateAsync(model.Token);
|
||||
if (!result.IsSuccessed) return ProblemBadResponse("Token not valid");
|
||||
|
||||
var execResult = await _userService.AddUserExternalAsync(UserId, model.Provider, result.ExternalId!, result.Email!);
|
||||
switch (execResult.Status)
|
||||
{
|
||||
case AddUserExternalStatusEnum.success:
|
||||
return Ok(new
|
||||
{
|
||||
success = true
|
||||
});
|
||||
|
||||
case AddUserExternalStatusEnum.externalid_used:
|
||||
case AddUserExternalStatusEnum.user_not_valid:
|
||||
return ProblemBadResponse("Provider already connected");
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(execResult.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost("deattach")]
|
||||
public async Task<object> DeattachProvider([FromBody] DeattachModel model)
|
||||
{
|
||||
var exec = await _userService.RemoveUserExternal(UserId, model.Provider);
|
||||
|
||||
if (exec.Status == GeneralExecStatus.not_found) return ProblemBadResponse("Provider not connected");
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
success = true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class DeattachModel
|
||||
{
|
||||
[Required] public string Provider { get; set; } = null!;
|
||||
}
|
||||
|
||||
public class AttachModel
|
||||
{
|
||||
[Required] public string Provider { get; set; } = null!;
|
||||
[Required] public string Token { get; set; } = null!;
|
||||
}
|
||||
|
||||
public class ProfileModel
|
||||
{
|
||||
public class ProviderModel
|
||||
{
|
||||
public string Provider { get; set; } = null!;
|
||||
|
||||
public DateTime CreatedOn { get; set; }
|
||||
}
|
||||
|
||||
public string? Email { get; set; }
|
||||
public string? FirstName { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? FullName { get; set; }
|
||||
public bool? IsEmailConfirmed { get; set; }
|
||||
public string? Currency { get; set; }
|
||||
public List<ProviderModel>? Providers { get; set; }
|
||||
}
|
||||
|
||||
public static class ProfileModelExtensions
|
||||
{
|
||||
public static ProfileModel ToModel(this ApplicationUser<Guid> user, List<UserExternal> userClaims)
|
||||
{
|
||||
return new ProfileModel
|
||||
{
|
||||
Email = user.Email,
|
||||
FirstName = user.FirstName,
|
||||
LastName = user.LastName,
|
||||
FullName = user.FullName,
|
||||
IsEmailConfirmed = user.IsEmailConfirmed,
|
||||
Currency = user.CurrencyId,
|
||||
Providers = userClaims?.Select(x => new ProfileModel.ProviderModel
|
||||
{
|
||||
Provider = x.Provider,
|
||||
CreatedOn = x.CreatedOn
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user