Updating Blazor Admin (#442)
* Updating Blazor services * Adding Settings and Refactoring Services * WIP - Fighting with DI * Configuring dependencies in both Web Startup and BlazorAdmin Program.cs has them working again. * Everything works; need to optimize calls to ListBrands * LocalStorageBrandService decorator working * Added cache duration of 1 minute * Refactoring to reduce token storage Fixed issue with dropdowns binding to int * Remove token stuff from login; moved to CustomAuthStateProvider * Migrated CatalogTypes to separate service Implemented cache decorator * Ardalis/blazor refactor (#440) * 1. Migrate CatalogItemServices -> CatalogItemService. 3. Add caching to CatalogItemService. * change to $"Loading {key} from local storage" ? * docker settings added. (#441) * docker settings added. * InDocker Removed * InDocker removed from web startup. * removed unused using * no reload list if close without save * startup patch for localhost * file name fixed * removed docker from launchSettings. * Configure logging via appsettings Co-authored-by: Shady Nagy <info@shadynagy.com>
This commit is contained in:
@@ -2,9 +2,8 @@ version: '3.4'
|
|||||||
services:
|
services:
|
||||||
eshopwebmvc:
|
eshopwebmvc:
|
||||||
environment:
|
environment:
|
||||||
- ASPNETCORE_ENVIRONMENT=Development
|
- ASPNETCORE_ENVIRONMENT=Docker
|
||||||
- ASPNETCORE_URLS=http://+:80
|
- ASPNETCORE_URLS=http://+:80
|
||||||
- DOTNET_RUNNING_IN_CONTAINER=true
|
|
||||||
ports:
|
ports:
|
||||||
- "5106:80"
|
- "5106:80"
|
||||||
volumes:
|
volumes:
|
||||||
@@ -12,9 +11,8 @@ services:
|
|||||||
- ~/.microsoft/usersecrets:/root/.microsoft/usersecrets:ro
|
- ~/.microsoft/usersecrets:/root/.microsoft/usersecrets:ro
|
||||||
eshoppublicapi:
|
eshoppublicapi:
|
||||||
environment:
|
environment:
|
||||||
- ASPNETCORE_ENVIRONMENT=Development
|
- ASPNETCORE_ENVIRONMENT=Docker
|
||||||
- ASPNETCORE_URLS=http://+:80
|
- ASPNETCORE_URLS=http://+:80
|
||||||
- DOTNET_RUNNING_IN_CONTAINER=true
|
|
||||||
ports:
|
ports:
|
||||||
- "5200:80"
|
- "5200:80"
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Build" Version="3.2.0" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Build" Version="3.2.0" PrivateAssets="all" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="3.2.0" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="3.2.0" PrivateAssets="all" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="3.1.5" />
|
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="3.1.5" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="3.1.6" />
|
||||||
<PackageReference Include="System.Net.Http.Json" Version="3.2.0" />
|
<PackageReference Include="System.Net.Http.Json" Version="3.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +1,41 @@
|
|||||||
using System;
|
using BlazorAdmin.Services;
|
||||||
using BlazorAdmin.Services;
|
using BlazorShared.Authorization;
|
||||||
using Microsoft.AspNetCore.Components.Authorization;
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Net.Http.Json;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using BlazorShared.Authorization;
|
|
||||||
|
|
||||||
namespace BlazorAdmin
|
namespace BlazorAdmin
|
||||||
{
|
{
|
||||||
public class CustomAuthStateProvider : AuthenticationStateProvider
|
public class CustomAuthStateProvider : AuthenticationStateProvider
|
||||||
{
|
{
|
||||||
|
// TODO: Get Default Cache Duration from Config
|
||||||
private static readonly TimeSpan UserCacheRefreshInterval = TimeSpan.FromSeconds(60);
|
private static readonly TimeSpan UserCacheRefreshInterval = TimeSpan.FromSeconds(60);
|
||||||
|
|
||||||
private readonly AuthService _authService;
|
private readonly AuthService _authService;
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
private readonly ILogger<CustomAuthStateProvider> _logger;
|
private readonly ILogger<CustomAuthStateProvider> _logger;
|
||||||
|
|
||||||
private DateTimeOffset _userLastCheck = DateTimeOffset.FromUnixTimeSeconds(0);
|
private DateTimeOffset _userLastCheck = DateTimeOffset.FromUnixTimeSeconds(0);
|
||||||
private ClaimsPrincipal _cachedUser = new ClaimsPrincipal(new ClaimsIdentity());
|
private ClaimsPrincipal _cachedUser = new ClaimsPrincipal(new ClaimsIdentity());
|
||||||
|
|
||||||
public CustomAuthStateProvider(AuthService authService, ILogger<CustomAuthStateProvider> logger)
|
public CustomAuthStateProvider(AuthService authService,
|
||||||
|
HttpClient httpClient,
|
||||||
|
ILogger<CustomAuthStateProvider> logger)
|
||||||
{
|
{
|
||||||
_authService = authService;
|
_authService = authService;
|
||||||
|
_httpClient = httpClient;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync() =>
|
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||||
new AuthenticationState(await GetUser(useCache: true));
|
{
|
||||||
|
return new AuthenticationState(await GetUser(useCache: true));
|
||||||
|
}
|
||||||
|
|
||||||
private async ValueTask<ClaimsPrincipal> GetUser(bool useCache = false)
|
private async ValueTask<ClaimsPrincipal> GetUser(bool useCache = false)
|
||||||
{
|
{
|
||||||
@@ -47,16 +57,17 @@ namespace BlazorAdmin
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
user = await _authService.GetTokenFromController();
|
_logger.LogInformation("Fetching user details from web api.");
|
||||||
|
user = await _httpClient.GetFromJsonAsync<UserInfo>("User");
|
||||||
}
|
}
|
||||||
catch (Exception exc)
|
catch (Exception exc)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(exc, "Fetching user failed.");
|
_logger.LogWarning(exc, "Fetching user failed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user == null || !user.IsAuthenticated)
|
if (user == null || !user.IsAuthenticated)
|
||||||
{
|
{
|
||||||
return new ClaimsPrincipal(new ClaimsIdentity());
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var identity = new ClaimsIdentity(
|
var identity = new ClaimsIdentity(
|
||||||
@@ -72,6 +83,8 @@ namespace BlazorAdmin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", user.Token);
|
||||||
|
|
||||||
return new ClaimsPrincipal(identity);
|
return new ClaimsPrincipal(identity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
@inject ILogger<Create> Logger
|
@inject ILogger<Create> Logger
|
||||||
@inject AuthService Auth
|
@inject AuthService Auth
|
||||||
@inject IJSRuntime JSRuntime
|
@inject IJSRuntime JSRuntime
|
||||||
|
@inject ICatalogItemService CatalogItemService
|
||||||
|
|
||||||
@inherits BlazorAdmin.Helpers.BlazorComponent
|
@inherits BlazorAdmin.Helpers.BlazorComponent
|
||||||
|
|
||||||
@@ -130,7 +131,7 @@
|
|||||||
public IEnumerable<CatalogType> Types { get; set; }
|
public IEnumerable<CatalogType> Types { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<string> OnCloseClick { get; set; }
|
public EventCallback<string> OnSaveClick { get; set; }
|
||||||
|
|
||||||
private string LoadPicture => string.IsNullOrEmpty(_item.PictureBase64) ? string.Empty : $"data:image/png;base64, {_item.PictureBase64}";
|
private string LoadPicture => string.IsNullOrEmpty(_item.PictureBase64) ? string.Empty : $"data:image/png;base64, {_item.PictureBase64}";
|
||||||
private bool HasPicture => !string.IsNullOrEmpty(_item.PictureBase64);
|
private bool HasPicture => !string.IsNullOrEmpty(_item.PictureBase64);
|
||||||
@@ -142,8 +143,8 @@
|
|||||||
|
|
||||||
private async Task CreateClick()
|
private async Task CreateClick()
|
||||||
{
|
{
|
||||||
await new BlazorAdmin.Services.CatalogItemServices.Create(Auth).HandleAsync(_item);
|
await CatalogItemService.Create(_item);
|
||||||
await OnCloseClick.InvokeAsync(null);
|
await OnSaveClick.InvokeAsync(null);
|
||||||
await Close();
|
await Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +173,6 @@
|
|||||||
_modalDisplay = "none";
|
_modalDisplay = "none";
|
||||||
_modalClass = "";
|
_modalClass = "";
|
||||||
_showCreateModal = false;
|
_showCreateModal = false;
|
||||||
await OnCloseClick.InvokeAsync(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddFile(IFileListEntry[] files)
|
private async Task AddFile(IFileListEntry[] files)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
@inject ILogger<Delete> Logger
|
@inject ILogger<Delete> Logger
|
||||||
@inject AuthService Auth
|
@inject AuthService Auth
|
||||||
@inject IJSRuntime JSRuntime
|
@inject IJSRuntime JSRuntime
|
||||||
|
@inject ICatalogItemService CatalogItemService
|
||||||
|
|
||||||
@inherits BlazorAdmin.Helpers.BlazorComponent
|
@inherits BlazorAdmin.Helpers.BlazorComponent
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@
|
|||||||
</dt>
|
</dt>
|
||||||
|
|
||||||
<dd>
|
<dd>
|
||||||
@Services.CatalogBrandServices.List.GetBrandName(Brands, _item.CatalogBrandId)
|
@_item.CatalogBrand
|
||||||
</dd>
|
</dd>
|
||||||
|
|
||||||
<dt>
|
<dt>
|
||||||
@@ -58,7 +59,7 @@
|
|||||||
</dt>
|
</dt>
|
||||||
|
|
||||||
<dd>
|
<dd>
|
||||||
@Services.CatalogTypeServices.List.GetTypeName(Types, _item.CatalogTypeId)
|
@_item.CatalogType
|
||||||
</dd>
|
</dd>
|
||||||
<dt>
|
<dt>
|
||||||
Price
|
Price
|
||||||
@@ -97,7 +98,7 @@
|
|||||||
public IEnumerable<CatalogType> Types { get; set; }
|
public IEnumerable<CatalogType> Types { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<string> OnCloseClick { get; set; }
|
public EventCallback<string> OnSaveClick { get; set; }
|
||||||
|
|
||||||
private bool HasPicture => !string.IsNullOrEmpty(_item.PictureUri);
|
private bool HasPicture => !string.IsNullOrEmpty(_item.PictureUri);
|
||||||
private string _modalDisplay = "none;";
|
private string _modalDisplay = "none;";
|
||||||
@@ -109,9 +110,9 @@
|
|||||||
{
|
{
|
||||||
// TODO: Add some kind of "are you sure" check before this
|
// TODO: Add some kind of "are you sure" check before this
|
||||||
|
|
||||||
await new BlazorAdmin.Services.CatalogItemServices.Delete(Auth).HandleAsync(id);
|
await CatalogItemService.Delete(id);
|
||||||
|
|
||||||
await OnCloseClick.InvokeAsync(null);
|
await OnSaveClick.InvokeAsync(null);
|
||||||
await Close();
|
await Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +122,7 @@
|
|||||||
|
|
||||||
await new Css(JSRuntime).HideBodyOverflow();
|
await new Css(JSRuntime).HideBodyOverflow();
|
||||||
|
|
||||||
_item = await new GetById(Auth).HandleAsync(id);
|
_item = await CatalogItemService.GetById(id);
|
||||||
|
|
||||||
_modalDisplay = "block;";
|
_modalDisplay = "block;";
|
||||||
_modalClass = "Show";
|
_modalClass = "Show";
|
||||||
@@ -136,6 +137,5 @@
|
|||||||
_modalDisplay = "none";
|
_modalDisplay = "none";
|
||||||
_modalClass = "";
|
_modalClass = "";
|
||||||
_showDeleteModal = false;
|
_showDeleteModal = false;
|
||||||
await OnCloseClick.InvokeAsync(null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
@inject ILogger<Details> Logger
|
@inject ILogger<Details> Logger
|
||||||
@inject AuthService Auth
|
@inject AuthService Auth
|
||||||
@inject IJSRuntime JSRuntime
|
@inject IJSRuntime JSRuntime
|
||||||
|
@inject ICatalogItemService CatalogItemService
|
||||||
|
|
||||||
@inherits BlazorAdmin.Helpers.BlazorComponent
|
@inherits BlazorAdmin.Helpers.BlazorComponent
|
||||||
|
|
||||||
@@ -53,7 +54,7 @@
|
|||||||
</dt>
|
</dt>
|
||||||
|
|
||||||
<dd>
|
<dd>
|
||||||
@Services.CatalogBrandServices.List.GetBrandName(Brands, _item.CatalogBrandId)
|
@_item.CatalogBrand
|
||||||
</dd>
|
</dd>
|
||||||
|
|
||||||
<dt>
|
<dt>
|
||||||
@@ -61,7 +62,7 @@
|
|||||||
</dt>
|
</dt>
|
||||||
|
|
||||||
<dd>
|
<dd>
|
||||||
@Services.CatalogTypeServices.List.GetTypeName(Types, _item.CatalogTypeId)
|
@_item.CatalogType
|
||||||
</dd>
|
</dd>
|
||||||
<dt>
|
<dt>
|
||||||
Price
|
Price
|
||||||
@@ -108,10 +109,10 @@
|
|||||||
private bool _showDetailsModal = false;
|
private bool _showDetailsModal = false;
|
||||||
private CatalogItem _item = new CatalogItem();
|
private CatalogItem _item = new CatalogItem();
|
||||||
|
|
||||||
public void EditClick()
|
public async Task EditClick()
|
||||||
{
|
{
|
||||||
OnEditClick.InvokeAsync(_item.Id);
|
await OnEditClick.InvokeAsync(_item.Id);
|
||||||
Close();
|
await Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Open(int id)
|
public async Task Open(int id)
|
||||||
@@ -121,7 +122,7 @@
|
|||||||
|
|
||||||
await new Css(JSRuntime).HideBodyOverflow();
|
await new Css(JSRuntime).HideBodyOverflow();
|
||||||
|
|
||||||
_item = await new GetById(Auth).HandleAsync(id);
|
_item = await CatalogItemService.GetById(id);
|
||||||
|
|
||||||
_modalDisplay = "block;";
|
_modalDisplay = "block;";
|
||||||
_modalClass = "Show";
|
_modalClass = "Show";
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
@inject ILogger<Edit> Logger
|
@inject ILogger<Edit> Logger
|
||||||
@inject AuthService Auth
|
@inject AuthService Auth
|
||||||
@inject IJSRuntime JSRuntime
|
@inject IJSRuntime JSRuntime
|
||||||
|
@inject ICatalogItemService CatalogItemService
|
||||||
|
|
||||||
@inherits BlazorAdmin.Helpers.BlazorComponent
|
@inherits BlazorAdmin.Helpers.BlazorComponent
|
||||||
|
|
||||||
@@ -54,12 +55,12 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-md-6">Brand</label>
|
<label class="control-label col-md-6">Brand</label>
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<InputSelect @bind-Value="_item.CatalogBrandId" class="form-control">
|
<CustomInputSelect @bind-Value="_item.CatalogBrandId" class="form-control">
|
||||||
@foreach (var brand in Brands)
|
@foreach (var brand in Brands)
|
||||||
{
|
{
|
||||||
<option value="@brand.Id">@brand.Name</option>
|
<option value="@brand.Id.ToString()">@brand.Name</option>
|
||||||
}
|
}
|
||||||
</InputSelect>
|
</CustomInputSelect>
|
||||||
<ValidationMessage For="(() => _item.CatalogBrandId)" />
|
<ValidationMessage For="(() => _item.CatalogBrandId)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -67,12 +68,12 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-md-6">Type</label>
|
<label class="control-label col-md-6">Type</label>
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<InputSelect @bind-Value="_item.CatalogTypeId" class="form-control">
|
<CustomInputSelect @bind-Value="_item.CatalogTypeId" class="form-control">
|
||||||
@foreach (var type in Types)
|
@foreach (var type in Types)
|
||||||
{
|
{
|
||||||
<option value="@type.Id">@type.Name</option>
|
<option value="@type.Id">@type.Name</option>
|
||||||
}
|
}
|
||||||
</InputSelect>
|
</CustomInputSelect>
|
||||||
<ValidationMessage For="(() => _item.CatalogTypeId)" />
|
<ValidationMessage For="(() => _item.CatalogTypeId)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -130,7 +131,7 @@
|
|||||||
public IEnumerable<CatalogType> Types { get; set; }
|
public IEnumerable<CatalogType> Types { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<string> OnCloseClick { get; set; }
|
public EventCallback<string> OnSaveClick { get; set; }
|
||||||
|
|
||||||
private string LoadPicture => string.IsNullOrEmpty(_item.PictureBase64) ? string.IsNullOrEmpty(_item.PictureUri) ? string.Empty : $"{_item.PictureUri}" : $"data:image/png;base64, {_item.PictureBase64}";
|
private string LoadPicture => string.IsNullOrEmpty(_item.PictureBase64) ? string.IsNullOrEmpty(_item.PictureUri) ? string.Empty : $"{_item.PictureUri}" : $"data:image/png;base64, {_item.PictureBase64}";
|
||||||
private bool HasPicture => !(string.IsNullOrEmpty(_item.PictureBase64) && string.IsNullOrEmpty(_item.PictureUri));
|
private bool HasPicture => !(string.IsNullOrEmpty(_item.PictureBase64) && string.IsNullOrEmpty(_item.PictureUri));
|
||||||
@@ -142,7 +143,8 @@
|
|||||||
|
|
||||||
private async Task SaveClick()
|
private async Task SaveClick()
|
||||||
{
|
{
|
||||||
await new BlazorAdmin.Services.CatalogItemServices.Edit(Auth).HandleAsync(_item);
|
await CatalogItemService.Edit(_item);
|
||||||
|
await OnSaveClick.InvokeAsync(null);
|
||||||
await Close();
|
await Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +154,7 @@
|
|||||||
|
|
||||||
await new Css(JSRuntime).HideBodyOverflow();
|
await new Css(JSRuntime).HideBodyOverflow();
|
||||||
|
|
||||||
_item = await new GetById(Auth).HandleAsync(id);
|
_item = await CatalogItemService.GetById(id);
|
||||||
|
|
||||||
_modalDisplay = "block;";
|
_modalDisplay = "block;";
|
||||||
_modalClass = "Show";
|
_modalClass = "Show";
|
||||||
@@ -168,7 +170,6 @@
|
|||||||
_modalDisplay = "none";
|
_modalDisplay = "none";
|
||||||
_modalClass = "";
|
_modalClass = "";
|
||||||
_showEditModal = false;
|
_showEditModal = false;
|
||||||
await OnCloseClick.InvokeAsync(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ChangeFile(IFileListEntry[] files)
|
private async Task ChangeFile(IFileListEntry[] files)
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
@page "/admin"
|
@page "/admin"
|
||||||
@attribute [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS)]
|
@attribute [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS)]
|
||||||
@inject AuthService Auth
|
@inject AuthService Auth
|
||||||
@inject BlazorAdmin.Services.CatalogItemServices.ListPaged CatalogItemListPaged
|
|
||||||
@inject BlazorAdmin.Services.CatalogTypeServices.List TypeList
|
|
||||||
@inject BlazorAdmin.Services.CatalogBrandServices.List BrandList
|
|
||||||
@inherits BlazorAdmin.Helpers.BlazorComponent
|
@inherits BlazorAdmin.Helpers.BlazorComponent
|
||||||
@namespace BlazorAdmin.Pages.CatalogItemPage
|
@namespace BlazorAdmin.Pages.CatalogItemPage
|
||||||
|
|
||||||
@@ -42,8 +39,8 @@ else
|
|||||||
<td>
|
<td>
|
||||||
<img class="img-thumbnail" src="@($"{item.PictureUri}")">
|
<img class="img-thumbnail" src="@($"{item.PictureUri}")">
|
||||||
</td>
|
</td>
|
||||||
<td>@Services.CatalogTypeServices.List.GetTypeName(catalogTypes, item.CatalogTypeId)</td>
|
<td>@item.CatalogType</td>
|
||||||
<td>@Services.CatalogBrandServices.List.GetBrandName(catalogBrands, item.CatalogBrandId)</td>
|
<td>@item.CatalogBrand</td>
|
||||||
<td>@item.Id</td>
|
<td>@item.Id</td>
|
||||||
<td>@item.Name</td>
|
<td>@item.Name</td>
|
||||||
<td>@item.Description</td>
|
<td>@item.Description</td>
|
||||||
@@ -63,7 +60,7 @@ else
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<Details Brands="@catalogBrands" Types="@catalogTypes" OnEditClick="EditClick" @ref="DetailsComponent"></Details>
|
<Details Brands="@catalogBrands" Types="@catalogTypes" OnEditClick="EditClick" @ref="DetailsComponent"></Details>
|
||||||
<Edit Brands="@catalogBrands" Types="@catalogTypes" OnCloseClick="ReloadCatalogItems" @ref="EditComponent"></Edit>
|
<Edit Brands="@catalogBrands" Types="@catalogTypes" OnSaveClick="ReloadCatalogItems" @ref="EditComponent"></Edit>
|
||||||
<Create Brands="@catalogBrands" Types="@catalogTypes" OnCloseClick="ReloadCatalogItems" @ref="CreateComponent"></Create>
|
<Create Brands="@catalogBrands" Types="@catalogTypes" OnSaveClick="ReloadCatalogItems" @ref="CreateComponent"></Create>
|
||||||
<Delete Brands="@catalogBrands" Types="@catalogTypes" OnCloseClick="ReloadCatalogItems" @ref="DeleteComponent"></Delete>
|
<Delete Brands="@catalogBrands" Types="@catalogTypes" OnSaveClick="ReloadCatalogItems" @ref="DeleteComponent"></Delete>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,22 @@
|
|||||||
using BlazorAdmin.Helpers;
|
using BlazorAdmin.Helpers;
|
||||||
using BlazorAdmin.Services.CatalogBrandServices;
|
|
||||||
using BlazorAdmin.Services.CatalogItemServices;
|
|
||||||
using BlazorAdmin.Services.CatalogTypeServices;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using BlazorShared.Interfaces;
|
||||||
|
using BlazorShared.Models;
|
||||||
|
|
||||||
namespace BlazorAdmin.Pages.CatalogItemPage
|
namespace BlazorAdmin.Pages.CatalogItemPage
|
||||||
{
|
{
|
||||||
public partial class List : BlazorComponent
|
public partial class List : BlazorComponent
|
||||||
{
|
{
|
||||||
|
[Microsoft.AspNetCore.Components.Inject]
|
||||||
|
public ICatalogItemService CatalogItemService { get; set; }
|
||||||
|
|
||||||
|
[Microsoft.AspNetCore.Components.Inject]
|
||||||
|
public ICatalogBrandService CatalogBrandService { get; set; }
|
||||||
|
|
||||||
|
[Microsoft.AspNetCore.Components.Inject]
|
||||||
|
public ICatalogTypeService CatalogTypeService { get; set; }
|
||||||
|
|
||||||
private List<CatalogItem> catalogItems = new List<CatalogItem>();
|
private List<CatalogItem> catalogItems = new List<CatalogItem>();
|
||||||
private List<CatalogType> catalogTypes = new List<CatalogType>();
|
private List<CatalogType> catalogTypes = new List<CatalogType>();
|
||||||
private List<CatalogBrand> catalogBrands = new List<CatalogBrand>();
|
private List<CatalogBrand> catalogBrands = new List<CatalogBrand>();
|
||||||
@@ -22,9 +30,9 @@ namespace BlazorAdmin.Pages.CatalogItemPage
|
|||||||
{
|
{
|
||||||
if (firstRender)
|
if (firstRender)
|
||||||
{
|
{
|
||||||
catalogItems = await CatalogItemListPaged.HandleAsync(50);
|
catalogItems = await CatalogItemService.List();
|
||||||
catalogTypes = await TypeList.HandleAsync();
|
catalogTypes = await CatalogTypeService.List();
|
||||||
catalogBrands = await BrandList.HandleAsync();
|
catalogBrands = await CatalogBrandService.List();
|
||||||
|
|
||||||
CallRequestRefresh();
|
CallRequestRefresh();
|
||||||
}
|
}
|
||||||
@@ -54,7 +62,7 @@ namespace BlazorAdmin.Pages.CatalogItemPage
|
|||||||
|
|
||||||
private async Task ReloadCatalogItems()
|
private async Task ReloadCatalogItems()
|
||||||
{
|
{
|
||||||
catalogItems = await new BlazorAdmin.Services.CatalogItemServices.ListPaged(Auth).HandleAsync(50);
|
catalogItems = await CatalogItemService.List();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
|
using BlazorAdmin.Services;
|
||||||
|
using Blazored.LocalStorage;
|
||||||
|
using BlazorShared;
|
||||||
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using BlazorAdmin.Services;
|
|
||||||
using Blazored.LocalStorage;
|
|
||||||
using Microsoft.AspNetCore.Components.Authorization;
|
|
||||||
|
|
||||||
namespace BlazorAdmin
|
namespace BlazorAdmin
|
||||||
{
|
{
|
||||||
@@ -16,18 +19,36 @@ namespace BlazorAdmin
|
|||||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||||
builder.RootComponents.Add<App>("admin");
|
builder.RootComponents.Add<App>("admin");
|
||||||
|
|
||||||
builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
var baseUrlConfig = new BaseUrlConfiguration();
|
||||||
|
builder.Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig);
|
||||||
|
builder.Services.AddScoped<BaseUrlConfiguration>(sp => baseUrlConfig);
|
||||||
|
|
||||||
builder.Services.AddSingleton<ILocalStorageService, LocalStorageService>();
|
builder.Services.AddScoped(sp => new HttpClient() { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||||
builder.Services.AddSingleton<AuthService>();
|
|
||||||
|
builder.Services.AddScoped<HttpService>();
|
||||||
|
|
||||||
|
builder.Services.AddScoped<ILocalStorageService, LocalStorageService>();
|
||||||
|
builder.Services.AddScoped<AuthService>();
|
||||||
|
|
||||||
builder.Services.AddAuthorizationCore();
|
builder.Services.AddAuthorizationCore();
|
||||||
builder.Services.AddSingleton<AuthenticationStateProvider, CustomAuthStateProvider>();
|
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
|
||||||
builder.Services.AddSingleton(sp => (CustomAuthStateProvider)sp.GetRequiredService<AuthenticationStateProvider>());
|
builder.Services.AddScoped(sp => (CustomAuthStateProvider)sp.GetRequiredService<AuthenticationStateProvider>());
|
||||||
|
|
||||||
builder.Services.AddBlazorServices();
|
builder.Services.AddBlazorServices();
|
||||||
|
|
||||||
await builder.Build().RunAsync();
|
builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging"));
|
||||||
|
|
||||||
|
await ClearLocalStorageCache(builder.Services);
|
||||||
|
|
||||||
|
builder.Build().RunAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task ClearLocalStorageCache(IServiceCollection services)
|
||||||
|
{
|
||||||
|
var sp = services.BuildServiceProvider();
|
||||||
|
var localStorageService = sp.GetRequiredService<ILocalStorageService>();
|
||||||
|
|
||||||
|
await localStorageService.RemoveItemAsync("brands");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Net.Http.Json;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BlazorAdmin.JavaScript;
|
using BlazorAdmin.JavaScript;
|
||||||
using Blazored.LocalStorage;
|
using Blazored.LocalStorage;
|
||||||
using Microsoft.JSInterop;
|
using Microsoft.JSInterop;
|
||||||
using BlazorShared.Authorization;
|
|
||||||
|
|
||||||
namespace BlazorAdmin.Services
|
namespace BlazorAdmin.Services
|
||||||
{
|
{
|
||||||
@@ -14,143 +11,32 @@ namespace BlazorAdmin.Services
|
|||||||
private readonly HttpClient _httpClient;
|
private readonly HttpClient _httpClient;
|
||||||
private readonly ILocalStorageService _localStorage;
|
private readonly ILocalStorageService _localStorage;
|
||||||
private readonly IJSRuntime _jSRuntime;
|
private readonly IJSRuntime _jSRuntime;
|
||||||
private static bool InDocker { get; set; }
|
|
||||||
|
|
||||||
public string ApiUrl => Constants.GetApiUrl(InDocker);
|
|
||||||
public bool IsLoggedIn { get; set; }
|
public bool IsLoggedIn { get; set; }
|
||||||
public string UserName { get; set; }
|
|
||||||
|
|
||||||
public AuthService(HttpClient httpClient, ILocalStorageService localStorage, IJSRuntime jSRuntime)
|
public AuthService(HttpClient httpClient,
|
||||||
|
ILocalStorageService localStorage,
|
||||||
|
IJSRuntime jSRuntime)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
_localStorage = localStorage;
|
_localStorage = localStorage;
|
||||||
_jSRuntime = jSRuntime;
|
_jSRuntime = jSRuntime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpClient GetHttpClient()
|
|
||||||
{
|
|
||||||
return _httpClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Logout()
|
public async Task Logout()
|
||||||
{
|
{
|
||||||
await DeleteLocalStorage();
|
|
||||||
await DeleteCookies();
|
await DeleteCookies();
|
||||||
RemoveAuthorizationHeader();
|
|
||||||
UserName = null;
|
|
||||||
IsLoggedIn = false;
|
IsLoggedIn = false;
|
||||||
await LogoutIdentityManager();
|
await LogoutIdentityManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RefreshLoginInfo()
|
|
||||||
{
|
|
||||||
await SetLoginData();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task RefreshLoginInfoFromCookie()
|
|
||||||
{
|
|
||||||
var token = await new Cookies(_jSRuntime).GetCookie("token");
|
|
||||||
await SaveTokenInLocalStorage(token);
|
|
||||||
|
|
||||||
var username = await new Cookies(_jSRuntime).GetCookie("username");
|
|
||||||
await SaveUsernameInLocalStorage(username);
|
|
||||||
|
|
||||||
var inDocker = await new Cookies(_jSRuntime).GetCookie("inDocker");
|
|
||||||
await SaveInDockerInLocalStorage(inDocker);
|
|
||||||
|
|
||||||
await RefreshLoginInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetToken()
|
|
||||||
{
|
|
||||||
|
|
||||||
var token = await _localStorage.GetItemAsync<string>("authToken");
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<UserInfo> GetTokenFromController()
|
|
||||||
{
|
|
||||||
return await _httpClient.GetFromJsonAsync<UserInfo>("User");
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetUsername()
|
|
||||||
{
|
|
||||||
var username = await _localStorage.GetItemAsync<string>("username");
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> GetInDocker()
|
|
||||||
{
|
|
||||||
return (await _localStorage.GetItemAsync<string>("inDocker")).ToLower() == "true";
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LogoutIdentityManager()
|
private async Task LogoutIdentityManager()
|
||||||
{
|
{
|
||||||
await _httpClient.PostAsync("Identity/Account/Logout", null);
|
await _httpClient.PostAsync("Identity/Account/Logout", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DeleteLocalStorage()
|
|
||||||
{
|
|
||||||
await _localStorage.RemoveItemAsync("authToken");
|
|
||||||
await _localStorage.RemoveItemAsync("username");
|
|
||||||
await _localStorage.RemoveItemAsync("inDocker");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DeleteCookies()
|
private async Task DeleteCookies()
|
||||||
{
|
{
|
||||||
await new Cookies(_jSRuntime).DeleteCookie("token");
|
await new Cookies(_jSRuntime).DeleteCookie("token");
|
||||||
await new Cookies(_jSRuntime).DeleteCookie("username");
|
|
||||||
await new Cookies(_jSRuntime).DeleteCookie("inDocker");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetLoginData()
|
|
||||||
{
|
|
||||||
IsLoggedIn = !string.IsNullOrEmpty(await GetToken());
|
|
||||||
UserName = await GetUsername();
|
|
||||||
InDocker = await GetInDocker();
|
|
||||||
await SetAuthorizationHeader();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveAuthorizationHeader()
|
|
||||||
{
|
|
||||||
if (_httpClient.DefaultRequestHeaders.Contains("Authorization"))
|
|
||||||
{
|
|
||||||
_httpClient.DefaultRequestHeaders.Remove("Authorization");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveTokenInLocalStorage(string token)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(token))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await _localStorage.SetItemAsync("authToken", token);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveUsernameInLocalStorage(string username)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(username))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await _localStorage.SetItemAsync("username", username);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveInDockerInLocalStorage(string inDocker)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(inDocker))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await _localStorage.SetItemAsync("inDocker", inDocker);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SetAuthorizationHeader()
|
|
||||||
{
|
|
||||||
var token = await GetToken();
|
|
||||||
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19
src/BlazorAdmin/Services/CacheEntry.cs
Normal file
19
src/BlazorAdmin/Services/CacheEntry.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace BlazorAdmin.Services
|
||||||
|
{
|
||||||
|
public class CacheEntry<T>
|
||||||
|
{
|
||||||
|
public CacheEntry(T item)
|
||||||
|
{
|
||||||
|
Value = item;
|
||||||
|
}
|
||||||
|
public CacheEntry()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Value { get; set; }
|
||||||
|
public DateTime DateCreated { get; set; } = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
using Blazored.LocalStorage;
|
||||||
|
using BlazorShared.Interfaces;
|
||||||
|
using BlazorShared.Models;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BlazorAdmin.Services
|
||||||
|
{
|
||||||
|
public class CachedCatalogBrandServiceDecorator : ICatalogBrandService
|
||||||
|
{
|
||||||
|
// TODO: Make a generic decorator for any LookupData type
|
||||||
|
private readonly ILocalStorageService _localStorageService;
|
||||||
|
private readonly CatalogBrandService _catalogBrandService;
|
||||||
|
private ILogger<CachedCatalogBrandServiceDecorator> _logger;
|
||||||
|
|
||||||
|
public CachedCatalogBrandServiceDecorator(ILocalStorageService localStorageService,
|
||||||
|
CatalogBrandService catalogBrandService,
|
||||||
|
ILogger<CachedCatalogBrandServiceDecorator> logger)
|
||||||
|
{
|
||||||
|
_localStorageService = localStorageService;
|
||||||
|
_catalogBrandService = catalogBrandService;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CatalogBrand> GetById(int id)
|
||||||
|
{
|
||||||
|
return (await List()).FirstOrDefault(x => x.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<CatalogBrand>> List()
|
||||||
|
{
|
||||||
|
string key = "brands";
|
||||||
|
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<CatalogBrand>>>(key);
|
||||||
|
if (cacheEntry != null)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Loading brands from local storage.");
|
||||||
|
// TODO: Get Default Cache Duration from Config
|
||||||
|
if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow)
|
||||||
|
{
|
||||||
|
return cacheEntry.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Cache expired; removing brands from local storage.");
|
||||||
|
await _localStorageService.RemoveItemAsync(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var brands = await _catalogBrandService.List();
|
||||||
|
var entry = new CacheEntry<List<CatalogBrand>>(brands);
|
||||||
|
await _localStorageService.SetItemAsync(key, entry);
|
||||||
|
return brands;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
114
src/BlazorAdmin/Services/CachedCatalogItemServiceDecorator.cs
Normal file
114
src/BlazorAdmin/Services/CachedCatalogItemServiceDecorator.cs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
using Blazored.LocalStorage;
|
||||||
|
using BlazorShared.Interfaces;
|
||||||
|
using BlazorShared.Models;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BlazorAdmin.Services
|
||||||
|
{
|
||||||
|
public class CachedCatalogItemServiceDecorator : ICatalogItemService
|
||||||
|
{
|
||||||
|
private readonly ILocalStorageService _localStorageService;
|
||||||
|
private readonly CatalogItemService _catalogItemService;
|
||||||
|
private ILogger<CachedCatalogItemServiceDecorator> _logger;
|
||||||
|
|
||||||
|
public CachedCatalogItemServiceDecorator(ILocalStorageService localStorageService,
|
||||||
|
CatalogItemService catalogItemService,
|
||||||
|
ILogger<CachedCatalogItemServiceDecorator> logger)
|
||||||
|
{
|
||||||
|
_localStorageService = localStorageService;
|
||||||
|
_catalogItemService = catalogItemService;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<CatalogItem>> ListPaged(int pageSize)
|
||||||
|
{
|
||||||
|
string key = "items";
|
||||||
|
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<CatalogItem>>>(key);
|
||||||
|
if (cacheEntry != null)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Loading items from local storage.");
|
||||||
|
if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow)
|
||||||
|
{
|
||||||
|
return cacheEntry.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"Loading {key} from local storage.");
|
||||||
|
await _localStorageService.RemoveItemAsync(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var items = await _catalogItemService.ListPaged(pageSize);
|
||||||
|
var entry = new CacheEntry<List<CatalogItem>>(items);
|
||||||
|
await _localStorageService.SetItemAsync(key, entry);
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<CatalogItem>> List()
|
||||||
|
{
|
||||||
|
string key = "items";
|
||||||
|
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<CatalogItem>>>(key);
|
||||||
|
if (cacheEntry != null)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Loading items from local storage.");
|
||||||
|
if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow)
|
||||||
|
{
|
||||||
|
return cacheEntry.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"Loading {key} from local storage.");
|
||||||
|
await _localStorageService.RemoveItemAsync(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var items = await _catalogItemService.List();
|
||||||
|
var entry = new CacheEntry<List<CatalogItem>>(items);
|
||||||
|
await _localStorageService.SetItemAsync(key, entry);
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CatalogItem> GetById(int id)
|
||||||
|
{
|
||||||
|
return (await List()).FirstOrDefault(x => x.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CatalogItem> Create(CreateCatalogItemRequest catalogItem)
|
||||||
|
{
|
||||||
|
var result = await _catalogItemService.Create(catalogItem);
|
||||||
|
await RefreshLocalStorageList();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CatalogItem> Edit(CatalogItem catalogItem)
|
||||||
|
{
|
||||||
|
var result = await _catalogItemService.Edit(catalogItem);
|
||||||
|
await RefreshLocalStorageList();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> Delete(int id)
|
||||||
|
{
|
||||||
|
var result = await _catalogItemService.Delete(id);
|
||||||
|
await RefreshLocalStorageList();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshLocalStorageList()
|
||||||
|
{
|
||||||
|
string key = "items";
|
||||||
|
|
||||||
|
await _localStorageService.RemoveItemAsync(key);
|
||||||
|
var items = await _catalogItemService.List();
|
||||||
|
var entry = new CacheEntry<List<CatalogItem>>(items);
|
||||||
|
await _localStorageService.SetItemAsync(key, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
using Blazored.LocalStorage;
|
||||||
|
using BlazorShared.Interfaces;
|
||||||
|
using BlazorShared.Models;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BlazorAdmin.Services
|
||||||
|
{
|
||||||
|
public class CachedCatalogTypeServiceDecorator : ICatalogTypeService
|
||||||
|
{
|
||||||
|
// TODO: Make a generic decorator for any LookupData type
|
||||||
|
private readonly ILocalStorageService _localStorageService;
|
||||||
|
private readonly CatalogTypeService _catalogTypeService;
|
||||||
|
private ILogger<CachedCatalogTypeServiceDecorator> _logger;
|
||||||
|
|
||||||
|
public CachedCatalogTypeServiceDecorator(ILocalStorageService localStorageService,
|
||||||
|
CatalogTypeService catalogTypeService,
|
||||||
|
ILogger<CachedCatalogTypeServiceDecorator> logger)
|
||||||
|
{
|
||||||
|
_localStorageService = localStorageService;
|
||||||
|
_catalogTypeService = catalogTypeService;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CatalogType> GetById(int id)
|
||||||
|
{
|
||||||
|
return (await List()).FirstOrDefault(x => x.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<CatalogType>> List()
|
||||||
|
{
|
||||||
|
string key = "types";
|
||||||
|
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<CatalogType>>>(key);
|
||||||
|
if (cacheEntry != null)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Loading types from local storage.");
|
||||||
|
if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow)
|
||||||
|
{
|
||||||
|
return cacheEntry.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Cache expired; removing types from local storage.");
|
||||||
|
await _localStorageService.RemoveItemAsync(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var types = await _catalogTypeService.List();
|
||||||
|
var entry = new CacheEntry<List<CatalogType>>(types);
|
||||||
|
await _localStorageService.SetItemAsync(key, entry);
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/BlazorAdmin/Services/CatalogBrandService.cs
Normal file
41
src/BlazorAdmin/Services/CatalogBrandService.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using BlazorShared;
|
||||||
|
using BlazorShared.Interfaces;
|
||||||
|
using BlazorShared.Models;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
||||||
|
namespace BlazorAdmin.Services
|
||||||
|
{
|
||||||
|
public class CatalogBrandService : ICatalogBrandService
|
||||||
|
{
|
||||||
|
// TODO: Make a generic service for any LookupData type
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly ILogger<CatalogBrandService> _logger;
|
||||||
|
private string _apiUrl;
|
||||||
|
|
||||||
|
public CatalogBrandService(HttpClient httpClient,
|
||||||
|
BaseUrlConfiguration baseUrlConfiguration,
|
||||||
|
ILogger<CatalogBrandService> logger)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_logger = logger;
|
||||||
|
_apiUrl = baseUrlConfiguration.ApiBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CatalogBrand> GetById(int id)
|
||||||
|
{
|
||||||
|
return (await List()).FirstOrDefault(x => x.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<CatalogBrand>> List()
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Fetching brands from API.");
|
||||||
|
return (await _httpClient.GetFromJsonAsync<CatalogBrandResponse>($"{_apiUrl}catalog-brands"))?.CatalogBrands;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace BlazorAdmin.Services.CatalogBrandServices
|
|
||||||
{
|
|
||||||
public class CatalogBrand
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
public string Name { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.Http.Json;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace BlazorAdmin.Services.CatalogBrandServices
|
|
||||||
{
|
|
||||||
public class List
|
|
||||||
{
|
|
||||||
private readonly AuthService _authService;
|
|
||||||
private readonly HttpClient _httpClient;
|
|
||||||
|
|
||||||
public List(AuthService authService, HttpClient httpClient)
|
|
||||||
{
|
|
||||||
_authService = authService;
|
|
||||||
_httpClient = httpClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<CatalogBrand>> HandleAsync()
|
|
||||||
{
|
|
||||||
return (await _httpClient.GetFromJsonAsync<CatalogBrandResult>($"{_authService.ApiUrl}catalog-brands"))?.CatalogBrands;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetBrandName(IEnumerable<CatalogBrand> brands, int brandId)
|
|
||||||
{
|
|
||||||
var type = brands.FirstOrDefault(t => t.Id == brandId);
|
|
||||||
|
|
||||||
return type == null ? "None" : type.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
102
src/BlazorAdmin/Services/CatalogItemService.cs
Normal file
102
src/BlazorAdmin/Services/CatalogItemService.cs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
using BlazorShared;
|
||||||
|
using BlazorShared.Interfaces;
|
||||||
|
using BlazorShared.Models;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
||||||
|
namespace BlazorAdmin.Services
|
||||||
|
{
|
||||||
|
public class CatalogItemService : ICatalogItemService
|
||||||
|
{
|
||||||
|
private readonly ICatalogBrandService _brandService;
|
||||||
|
private readonly ICatalogTypeService _typeService;
|
||||||
|
private readonly HttpService _httpService;
|
||||||
|
private readonly ILogger<CatalogItemService> _logger;
|
||||||
|
private string _apiUrl;
|
||||||
|
|
||||||
|
public CatalogItemService(ICatalogBrandService brandService,
|
||||||
|
ICatalogTypeService typeService,
|
||||||
|
HttpService httpService,
|
||||||
|
BaseUrlConfiguration baseUrlConfiguration,
|
||||||
|
ILogger<CatalogItemService> logger)
|
||||||
|
{
|
||||||
|
_brandService = brandService;
|
||||||
|
_typeService = typeService;
|
||||||
|
|
||||||
|
_httpService = httpService;
|
||||||
|
_logger = logger;
|
||||||
|
_apiUrl = baseUrlConfiguration.ApiBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CatalogItem> Create(CreateCatalogItemRequest catalogItem)
|
||||||
|
{
|
||||||
|
return (await _httpService.HttpPost<CreateCatalogItemResponse>("catalog-items", catalogItem)).CatalogItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CatalogItem> Edit(CatalogItem catalogItem)
|
||||||
|
{
|
||||||
|
return (await _httpService.HttpPut<EditCatalogItemResult>("catalog-items", catalogItem)).CatalogItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> Delete(int catalogItemId)
|
||||||
|
{
|
||||||
|
return (await _httpService.HttpDelete<DeleteCatalogItemResponse>("catalog-items", catalogItemId)).Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CatalogItem> GetById(int id)
|
||||||
|
{
|
||||||
|
var brandListTask = _brandService.List();
|
||||||
|
var typeListTask = _typeService.List();
|
||||||
|
var itemGetTask = _httpService.HttpGet<EditCatalogItemResult>($"catalog-items/{id}");
|
||||||
|
await Task.WhenAll(brandListTask, typeListTask, itemGetTask);
|
||||||
|
var brands = brandListTask.Result;
|
||||||
|
var types = typeListTask.Result;
|
||||||
|
var catalogItem = itemGetTask.Result.CatalogItem;
|
||||||
|
catalogItem.CatalogBrand = brands.FirstOrDefault(b => b.Id == catalogItem.CatalogBrandId)?.Name;
|
||||||
|
catalogItem.CatalogType = types.FirstOrDefault(t => t.Id == catalogItem.CatalogTypeId)?.Name;
|
||||||
|
return catalogItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<CatalogItem>> ListPaged(int pageSize)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Fetching catalog items from API.");
|
||||||
|
|
||||||
|
var brandListTask = _brandService.List();
|
||||||
|
var typeListTask = _typeService.List();
|
||||||
|
var itemListTask = _httpService.HttpGet<PagedCatalogItemResponse>($"catalog-items?PageSize=10");
|
||||||
|
await Task.WhenAll(brandListTask, typeListTask, itemListTask);
|
||||||
|
var brands = brandListTask.Result;
|
||||||
|
var types = typeListTask.Result;
|
||||||
|
var items = itemListTask.Result.CatalogItems;
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
item.CatalogBrand = brands.FirstOrDefault(b => b.Id == item.CatalogBrandId)?.Name;
|
||||||
|
item.CatalogType = types.FirstOrDefault(t => t.Id == item.CatalogTypeId)?.Name;
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<CatalogItem>> List()
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Fetching catalog items from API.");
|
||||||
|
|
||||||
|
var brandListTask = _brandService.List();
|
||||||
|
var typeListTask = _typeService.List();
|
||||||
|
//TODO: Need to change the api to support full list
|
||||||
|
var itemListTask = _httpService.HttpGet<PagedCatalogItemResponse>($"catalog-items?PageSize=100");
|
||||||
|
await Task.WhenAll(brandListTask, typeListTask, itemListTask);
|
||||||
|
var brands = brandListTask.Result;
|
||||||
|
var types = typeListTask.Result;
|
||||||
|
var items = itemListTask.Result.CatalogItems;
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
item.CatalogBrand = brands.FirstOrDefault(b => b.Id == item.CatalogBrandId)?.Name;
|
||||||
|
item.CatalogType = types.FirstOrDefault(t => t.Id == item.CatalogTypeId)?.Name;
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace BlazorAdmin.Services.CatalogItemServices
|
|
||||||
{
|
|
||||||
public class CreateCatalogItemResult
|
|
||||||
{
|
|
||||||
public CatalogItem CatalogItem { get; set; } = new CatalogItem();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace BlazorAdmin.Services.CatalogItemServices
|
|
||||||
{
|
|
||||||
public class Create
|
|
||||||
{
|
|
||||||
private readonly HttpService _httpService;
|
|
||||||
|
|
||||||
public Create(AuthService authService)
|
|
||||||
{
|
|
||||||
_httpService = new HttpService(authService.GetHttpClient(), authService.ApiUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<CatalogItem> HandleAsync(CreateCatalogItemRequest catalogItem)
|
|
||||||
{
|
|
||||||
return (await _httpService.HttpPost<CreateCatalogItemResult>("catalog-items", catalogItem)).CatalogItem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace BlazorAdmin.Services.CatalogItemServices
|
|
||||||
{
|
|
||||||
public class DeleteCatalogItemResult
|
|
||||||
{
|
|
||||||
public string Status { get; set; } = "Deleted";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace BlazorAdmin.Services.CatalogItemServices
|
|
||||||
{
|
|
||||||
public class Delete
|
|
||||||
{
|
|
||||||
private readonly HttpService _httpService;
|
|
||||||
|
|
||||||
public Delete(AuthService authService)
|
|
||||||
{
|
|
||||||
_httpService = new HttpService(authService.GetHttpClient(), authService.ApiUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> HandleAsync(int catalogItemId)
|
|
||||||
{
|
|
||||||
return (await _httpService.HttpDelete<DeleteCatalogItemResult>("catalog-items", catalogItemId)).Status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace BlazorAdmin.Services.CatalogItemServices
|
|
||||||
{
|
|
||||||
public class Edit
|
|
||||||
{
|
|
||||||
private readonly HttpService _httpService;
|
|
||||||
|
|
||||||
public Edit(AuthService authService)
|
|
||||||
{
|
|
||||||
_httpService = new HttpService(authService.GetHttpClient(), authService.ApiUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<CatalogItem> HandleAsync(CatalogItem catalogItem)
|
|
||||||
{
|
|
||||||
return (await _httpService.HttpPut<EditCatalogItemResult>("catalog-items", catalogItem)).CatalogItem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace BlazorAdmin.Services.CatalogItemServices
|
|
||||||
{
|
|
||||||
public class GetByIdCatalogItemResult
|
|
||||||
{
|
|
||||||
public CatalogItem CatalogItem { get; set; } = new CatalogItem();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace BlazorAdmin.Services.CatalogItemServices
|
|
||||||
{
|
|
||||||
public class GetById
|
|
||||||
{
|
|
||||||
private readonly HttpService _httpService;
|
|
||||||
|
|
||||||
public GetById(AuthService authService)
|
|
||||||
{
|
|
||||||
_httpService = new HttpService(authService.GetHttpClient(), authService.ApiUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<CatalogItem> HandleAsync(int catalogItemId)
|
|
||||||
{
|
|
||||||
return (await _httpService.HttpGet<EditCatalogItemResult>($"catalog-items/{catalogItemId}")).CatalogItem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace BlazorAdmin.Services.CatalogItemServices
|
|
||||||
{
|
|
||||||
public class ListPaged
|
|
||||||
{
|
|
||||||
private readonly HttpService _httpService;
|
|
||||||
|
|
||||||
public ListPaged(AuthService authService)
|
|
||||||
{
|
|
||||||
_httpService = new HttpService(authService.GetHttpClient(), authService.ApiUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<CatalogItem>> HandleAsync(int pageSize)
|
|
||||||
{
|
|
||||||
return (await _httpService.HttpGet<PagedCatalogItemResult>($"catalog-items?PageSize={pageSize}")).CatalogItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
40
src/BlazorAdmin/Services/CatalogTypeService.cs
Normal file
40
src/BlazorAdmin/Services/CatalogTypeService.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using BlazorShared;
|
||||||
|
using BlazorShared.Interfaces;
|
||||||
|
using BlazorShared.Models;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BlazorAdmin.Services
|
||||||
|
{
|
||||||
|
public class CatalogTypeService : ICatalogTypeService
|
||||||
|
{
|
||||||
|
// TODO: Make a generic service for any LookupData type
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly ILogger<CatalogTypeService> _logger;
|
||||||
|
private string _apiUrl;
|
||||||
|
|
||||||
|
public CatalogTypeService(HttpClient httpClient,
|
||||||
|
BaseUrlConfiguration baseUrlConfiguration,
|
||||||
|
ILogger<CatalogTypeService> logger)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_logger = logger;
|
||||||
|
_apiUrl = baseUrlConfiguration.ApiBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CatalogType> GetById(int id)
|
||||||
|
{
|
||||||
|
return (await List()).FirstOrDefault(x => x.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<CatalogType>> List()
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Fetching types from API.");
|
||||||
|
return (await _httpClient.GetFromJsonAsync<CatalogTypeResponse>($"{_apiUrl}catalog-types"))?.CatalogTypes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace BlazorAdmin.Services.CatalogTypeServices
|
|
||||||
{
|
|
||||||
public class CatalogType
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
public string Name { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.Http.Json;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace BlazorAdmin.Services.CatalogTypeServices
|
|
||||||
{
|
|
||||||
public class List
|
|
||||||
{
|
|
||||||
private readonly AuthService _authService;
|
|
||||||
private readonly HttpClient _httpClient;
|
|
||||||
|
|
||||||
public List(AuthService authService, HttpClient httpClient)
|
|
||||||
{
|
|
||||||
_authService = authService;
|
|
||||||
_httpClient = httpClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<CatalogType>> HandleAsync()
|
|
||||||
{
|
|
||||||
return (await _httpClient.GetFromJsonAsync<CatalogTypeResult>($"{_authService.ApiUrl}catalog-types"))?.CatalogTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetTypeName(IEnumerable<CatalogType> types, int typeId)
|
|
||||||
{
|
|
||||||
var type = types.FirstOrDefault(t => t.Id == typeId);
|
|
||||||
|
|
||||||
return type == null ? "None" : type.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Net.Http;
|
using BlazorShared;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -10,10 +11,11 @@ namespace BlazorAdmin.Services
|
|||||||
private readonly HttpClient _httpClient;
|
private readonly HttpClient _httpClient;
|
||||||
private readonly string _apiUrl;
|
private readonly string _apiUrl;
|
||||||
|
|
||||||
public HttpService(HttpClient httpClient, string apiUrl)
|
|
||||||
|
public HttpService(HttpClient httpClient, BaseUrlConfiguration baseUrlConfiguration)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
_apiUrl = apiUrl;
|
_apiUrl = baseUrlConfiguration.ApiBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<T> HttpGet<T>(string uri)
|
public async Task<T> HttpGet<T>(string uri)
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
using BlazorAdmin.Services.CatalogItemServices;
|
using BlazorAdmin.Services;
|
||||||
|
using BlazorShared.Interfaces;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace BlazorAdmin
|
namespace BlazorAdmin
|
||||||
{
|
{
|
||||||
public static class ServicesConfiguration
|
public static class ServicesConfiguration
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddBlazorServices(this IServiceCollection service)
|
public static IServiceCollection AddBlazorServices(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
service.AddScoped<Create>();
|
services.AddScoped<ICatalogBrandService, CachedCatalogBrandServiceDecorator>();
|
||||||
service.AddScoped<ListPaged>();
|
services.AddScoped<CatalogBrandService>();
|
||||||
service.AddScoped<Delete>();
|
services.AddScoped<ICatalogTypeService, CachedCatalogTypeServiceDecorator>();
|
||||||
service.AddScoped<Edit>();
|
services.AddScoped<CatalogTypeService>();
|
||||||
service.AddScoped<GetById>();
|
services.AddScoped<ICatalogItemService, CachedCatalogItemServiceDecorator>();
|
||||||
|
services.AddScoped<CatalogItemService>();
|
||||||
|
|
||||||
service.AddScoped<BlazorAdmin.Services.CatalogBrandServices.List>();
|
return services;
|
||||||
service.AddScoped<BlazorAdmin.Services.CatalogTypeServices.List>();
|
|
||||||
|
|
||||||
return service;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
src/BlazorAdmin/Shared/CustomInputSelect.cs
Normal file
38
src/BlazorAdmin/Shared/CustomInputSelect.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
|
||||||
|
namespace BlazorAdmin.Shared
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This is needed until 5.0 ships with native support
|
||||||
|
/// https://www.pragimtech.com/blog/blazor/inputselect-does-not-support-system.int32/
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TValue"></typeparam>
|
||||||
|
public class CustomInputSelect<TValue> : InputSelect<TValue>
|
||||||
|
{
|
||||||
|
protected override bool TryParseValueFromString(string value, out TValue result,
|
||||||
|
out string validationErrorMessage)
|
||||||
|
{
|
||||||
|
if (typeof(TValue) == typeof(int))
|
||||||
|
{
|
||||||
|
if (int.TryParse(value, out var resultInt))
|
||||||
|
{
|
||||||
|
result = (TValue)(object)resultInt;
|
||||||
|
validationErrorMessage = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = default;
|
||||||
|
validationErrorMessage =
|
||||||
|
$"The selected value {value} is not a valid number.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return base.TryParseValueFromString(value, out result,
|
||||||
|
out validationErrorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
@inject AuthService Auth
|
@inject AuthenticationStateProvider AuthStateProvider
|
||||||
@inject IJSRuntime JSRuntime
|
@inject IJSRuntime JSRuntime
|
||||||
|
|
||||||
@inherits BlazorAdmin.Helpers.BlazorLayoutComponent
|
@inherits BlazorAdmin.Helpers.BlazorLayoutComponent
|
||||||
@@ -25,8 +25,9 @@
|
|||||||
{
|
{
|
||||||
if (firstRender)
|
if (firstRender)
|
||||||
{
|
{
|
||||||
await Auth.RefreshLoginInfoFromCookie();
|
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||||
if (!Auth.IsLoggedIn)
|
|
||||||
|
if(authState.User == null)
|
||||||
{
|
{
|
||||||
await new Route(JSRuntime).RouteOutside("/Identity/Account/Login");
|
await new Route(JSRuntime).RouteOutside("/Identity/Account/Login");
|
||||||
}
|
}
|
||||||
@@ -35,5 +36,4 @@
|
|||||||
|
|
||||||
await base.OnAfterRenderAsync(firstRender);
|
await base.OnAfterRenderAsync(firstRender);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
@inject AuthService Auth
|
@inherits BlazorAdmin.Helpers.BlazorComponent
|
||||||
@inherits BlazorAdmin.Helpers.BlazorComponent
|
|
||||||
<div class="top-row pl-4 navbar navbar-dark">
|
<div class="top-row pl-4 navbar navbar-dark">
|
||||||
<a class="navbar-brand" href="">eShopOnWeb Admin</a>
|
<a class="navbar-brand" href="">eShopOnWeb Admin</a>
|
||||||
<button class="navbar-toggler" @onclick="ToggleNavMenu">
|
<button class="navbar-toggler" @onclick="ToggleNavMenu">
|
||||||
@@ -14,28 +13,27 @@
|
|||||||
<span class="oi oi-home" aria-hidden="true"></span> Home
|
<span class="oi oi-home" aria-hidden="true"></span> Home
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item px-3">
|
<AuthorizeView>
|
||||||
<NavLink class="nav-link" href="manage/my-account" Match="NavLinkMatch.All">
|
<Authorized>
|
||||||
<span class="oi oi-person" aria-hidden="true"></span> @Auth.UserName
|
<li class="nav-item px-3">
|
||||||
</NavLink>
|
<NavLink class="nav-link" href="manage/my-account" Match="NavLinkMatch.All">
|
||||||
</li>
|
<span class="oi oi-person" aria-hidden="true"></span> @context.User.Identity.Name
|
||||||
|
|
||||||
<li class="nav-item px-3">
|
</NavLink>
|
||||||
@if (Auth.IsLoggedIn)
|
</li>
|
||||||
{
|
<li class="nav-item px-3">
|
||||||
<NavLink class="nav-link" href="logout">
|
<NavLink class="nav-link" href="logout">
|
||||||
<span class="oi oi-account-logout" aria-hidden="true"></span> Logout
|
<span class="oi oi-account-logout" aria-hidden="true"></span> Logout
|
||||||
</NavLink>
|
</NavLink>
|
||||||
}
|
</li>
|
||||||
|
</Authorized>
|
||||||
</li>
|
</AuthorizeView>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
|
||||||
private bool collapseNavMenu = true;
|
private bool collapseNavMenu = true;
|
||||||
public string UserName { get; set; }
|
|
||||||
|
|
||||||
private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
|
private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,8 @@
|
|||||||
@using BlazorAdmin
|
@using BlazorAdmin
|
||||||
@using BlazorAdmin.Shared
|
@using BlazorAdmin.Shared
|
||||||
@using BlazorAdmin.Services
|
@using BlazorAdmin.Services
|
||||||
@using BlazorAdmin.Services.CatalogBrandServices
|
|
||||||
@using BlazorAdmin.Services.CatalogItemServices
|
|
||||||
@using BlazorAdmin.Services.CatalogTypeServices
|
|
||||||
@using BlazorAdmin.JavaScript
|
@using BlazorAdmin.JavaScript
|
||||||
@using BlazorShared.Authorization
|
@using BlazorShared.Authorization
|
||||||
|
@using BlazorShared.Interfaces
|
||||||
@using BlazorInputFile
|
@using BlazorInputFile
|
||||||
|
@using BlazorShared.Models
|
||||||
|
|||||||
6
src/BlazorAdmin/wwwroot/appsettings.Docker.json
Normal file
6
src/BlazorAdmin/wwwroot/appsettings.Docker.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"baseUrls": {
|
||||||
|
"apiBase": "http://localhost:5200/api/",
|
||||||
|
"webBase": "http://host.docker.internal:5106/"
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/BlazorAdmin/wwwroot/appsettings.json
Normal file
14
src/BlazorAdmin/wwwroot/appsettings.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"baseUrls": {
|
||||||
|
"apiBase": "https://localhost:5099/api/",
|
||||||
|
"webBase": "https://localhost:44315/"
|
||||||
|
},
|
||||||
|
"Logging": {
|
||||||
|
"IncludeScopes": false,
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"System": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"date": "2018-05-06",
|
|
||||||
"temperatureC": 1,
|
|
||||||
"summary": "Freezing"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2018-05-07",
|
|
||||||
"temperatureC": 14,
|
|
||||||
"summary": "Bracing"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2018-05-08",
|
|
||||||
"temperatureC": -13,
|
|
||||||
"summary": "Freezing"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2018-05-09",
|
|
||||||
"temperatureC": -16,
|
|
||||||
"summary": "Balmy"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2018-05-10",
|
|
||||||
"temperatureC": -2,
|
|
||||||
"summary": "Chilly"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -6,23 +6,5 @@
|
|||||||
{
|
{
|
||||||
public const string ADMINISTRATORS = "Administrators";
|
public const string ADMINISTRATORS = "Administrators";
|
||||||
}
|
}
|
||||||
public static string GetApiUrl(bool inDocker) =>
|
|
||||||
inDocker ? DOCKER_API_URL : API_URL;
|
|
||||||
|
|
||||||
public static string GetWebUrl(bool inDocker) =>
|
|
||||||
inDocker ? DOCKER_WEB_URL : WEB_URL;
|
|
||||||
|
|
||||||
public static string GetWebUrlInternal(bool inDocker) =>
|
|
||||||
inDocker ? DOCKER_WEB_URL.Replace("localhost", "host.docker.internal") : WEB_URL;
|
|
||||||
|
|
||||||
public static string GetOriginWebUrl(bool inDocker) =>
|
|
||||||
GetWebUrl(inDocker).TrimEnd('/');
|
|
||||||
|
|
||||||
private const string API_URL = "https://localhost:5099/api/";
|
|
||||||
private const string DOCKER_API_URL = "http://localhost:5200/api/";
|
|
||||||
|
|
||||||
private const string WEB_URL = "https://localhost:44315/";
|
|
||||||
private const string DOCKER_WEB_URL = "http://localhost:5106/";
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace BlazorShared.Authorization
|
|||||||
public bool IsAuthenticated { get; set; }
|
public bool IsAuthenticated { get; set; }
|
||||||
public string NameClaimType { get; set; }
|
public string NameClaimType { get; set; }
|
||||||
public string RoleClaimType { get; set; }
|
public string RoleClaimType { get; set; }
|
||||||
|
public string Token { get; set; }
|
||||||
public IEnumerable<ClaimValue> Claims { get; set; }
|
public IEnumerable<ClaimValue> Claims { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/BlazorShared/BaseUrlConfiguration.cs
Normal file
10
src/BlazorShared/BaseUrlConfiguration.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace BlazorShared
|
||||||
|
{
|
||||||
|
public class BaseUrlConfiguration
|
||||||
|
{
|
||||||
|
public const string CONFIG_NAME = "baseUrls";
|
||||||
|
|
||||||
|
public string ApiBase { get; set; }
|
||||||
|
public string WebBase { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,4 +6,8 @@
|
|||||||
<AssemblyName>BlazorShared</AssemblyName>
|
<AssemblyName>BlazorShared</AssemblyName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="BlazorInputFile" Version="0.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
12
src/BlazorShared/Interfaces/ICatalogBrandService.cs
Normal file
12
src/BlazorShared/Interfaces/ICatalogBrandService.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BlazorShared.Models;
|
||||||
|
|
||||||
|
namespace BlazorShared.Interfaces
|
||||||
|
{
|
||||||
|
public interface ICatalogBrandService
|
||||||
|
{
|
||||||
|
Task<List<CatalogBrand>> List();
|
||||||
|
Task<CatalogBrand> GetById(int id);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/BlazorShared/Interfaces/ICatalogItemService.cs
Normal file
16
src/BlazorShared/Interfaces/ICatalogItemService.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BlazorShared.Models;
|
||||||
|
|
||||||
|
namespace BlazorShared.Interfaces
|
||||||
|
{
|
||||||
|
public interface ICatalogItemService
|
||||||
|
{
|
||||||
|
Task<CatalogItem> Create(CreateCatalogItemRequest catalogItem);
|
||||||
|
Task<CatalogItem> Edit(CatalogItem catalogItem);
|
||||||
|
Task<string> Delete(int id);
|
||||||
|
Task<CatalogItem> GetById(int id);
|
||||||
|
Task<List<CatalogItem>> ListPaged(int pageSize);
|
||||||
|
Task<List<CatalogItem>> List();
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/BlazorShared/Interfaces/ICatalogTypeService.cs
Normal file
12
src/BlazorShared/Interfaces/ICatalogTypeService.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using BlazorShared.Models;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BlazorShared.Interfaces
|
||||||
|
{
|
||||||
|
public interface ICatalogTypeService
|
||||||
|
{
|
||||||
|
Task<List<CatalogType>> List();
|
||||||
|
Task<CatalogType> GetById(int id);
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/BlazorShared/Models/CatalogBrand.cs
Normal file
6
src/BlazorShared/Models/CatalogBrand.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace BlazorShared.Models
|
||||||
|
{
|
||||||
|
public class CatalogBrand : LookupData
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace BlazorAdmin.Services.CatalogBrandServices
|
namespace BlazorShared.Models
|
||||||
{
|
{
|
||||||
public class CatalogBrandResult
|
public class CatalogBrandResponse
|
||||||
{
|
{
|
||||||
public List<CatalogBrand> CatalogBrands { get; set; } = new List<CatalogBrand>();
|
public List<CatalogBrand> CatalogBrands { get; set; } = new List<CatalogBrand>();
|
||||||
}
|
}
|
||||||
@@ -4,15 +4,17 @@ using System.IO;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BlazorInputFile;
|
using BlazorInputFile;
|
||||||
|
|
||||||
namespace BlazorAdmin.Services.CatalogItemServices
|
namespace BlazorShared.Models
|
||||||
{
|
{
|
||||||
public class CatalogItem
|
public class CatalogItem
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
public int CatalogTypeId { get; set; }
|
public int CatalogTypeId { get; set; }
|
||||||
|
public string CatalogType { get; set; } = "NotSet";
|
||||||
|
|
||||||
public int CatalogBrandId { get; set; }
|
public int CatalogBrandId { get; set; }
|
||||||
|
public string CatalogBrand { get; set; } = "NotSet";
|
||||||
|
|
||||||
[Required(ErrorMessage = "The Name field is required")]
|
[Required(ErrorMessage = "The Name field is required")]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
@@ -60,14 +62,17 @@ namespace BlazorAdmin.Services.CatalogItemServices
|
|||||||
|
|
||||||
public static async Task<string> DataToBase64(IFileListEntry fileItem)
|
public static async Task<string> DataToBase64(IFileListEntry fileItem)
|
||||||
{
|
{
|
||||||
using var reader = new StreamReader(fileItem.Data);
|
using ( var reader = new StreamReader(fileItem.Data))
|
||||||
|
{
|
||||||
|
using (var memStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
await reader.BaseStream.CopyToAsync(memStream);
|
||||||
|
var fileData = memStream.ToArray();
|
||||||
|
var encodedBase64 = Convert.ToBase64String(fileData);
|
||||||
|
|
||||||
await using var memStream = new MemoryStream();
|
return encodedBase64;
|
||||||
await reader.BaseStream.CopyToAsync(memStream);
|
}
|
||||||
var fileData = memStream.ToArray();
|
}
|
||||||
var encodedBase64 = Convert.ToBase64String(fileData);
|
|
||||||
|
|
||||||
return encodedBase64;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsExtensionValid(string fileName)
|
private static bool IsExtensionValid(string fileName)
|
||||||
6
src/BlazorShared/Models/CatalogType.cs
Normal file
6
src/BlazorShared/Models/CatalogType.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace BlazorShared.Models
|
||||||
|
{
|
||||||
|
public class CatalogType : LookupData
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace BlazorAdmin.Services.CatalogTypeServices
|
namespace BlazorShared.Models
|
||||||
{
|
{
|
||||||
public class CatalogTypeResult
|
public class CatalogTypeResponse
|
||||||
{
|
{
|
||||||
public List<CatalogType> CatalogTypes { get; set; } = new List<CatalogType>();
|
public List<CatalogType> CatalogTypes { get; set; } = new List<CatalogType>();
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace BlazorAdmin.Services.CatalogItemServices
|
namespace BlazorShared.Models
|
||||||
{
|
{
|
||||||
public class CreateCatalogItemRequest
|
public class CreateCatalogItemRequest
|
||||||
{
|
{
|
||||||
7
src/BlazorShared/Models/CreateCatalogItemResponse.cs
Normal file
7
src/BlazorShared/Models/CreateCatalogItemResponse.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace BlazorShared.Models
|
||||||
|
{
|
||||||
|
public class CreateCatalogItemResponse
|
||||||
|
{
|
||||||
|
public CatalogItem CatalogItem { get; set; } = new CatalogItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/BlazorShared/Models/DeleteCatalogItemResponse.cs
Normal file
7
src/BlazorShared/Models/DeleteCatalogItemResponse.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace BlazorShared.Models
|
||||||
|
{
|
||||||
|
public class DeleteCatalogItemResponse
|
||||||
|
{
|
||||||
|
public string Status { get; set; } = "Deleted";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace BlazorAdmin.Services.CatalogItemServices
|
namespace BlazorShared.Models
|
||||||
{
|
{
|
||||||
public class EditCatalogItemResult
|
public class EditCatalogItemResult
|
||||||
{
|
{
|
||||||
8
src/BlazorShared/Models/LookupData.cs
Normal file
8
src/BlazorShared/Models/LookupData.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace BlazorShared.Models
|
||||||
|
{
|
||||||
|
public abstract class LookupData
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace BlazorAdmin.Services.CatalogItemServices
|
namespace BlazorShared.Models
|
||||||
{
|
{
|
||||||
public class PagedCatalogItemResult
|
public class PagedCatalogItemResponse
|
||||||
{
|
{
|
||||||
public List<CatalogItem> CatalogItems { get; set; } = new List<CatalogItem>();
|
public List<CatalogItem> CatalogItems { get; set; } = new List<CatalogItem>();
|
||||||
public int PageCount { get; set; } = 0;
|
public int PageCount { get; set; } = 0;
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
using System;
|
using System.IO;
|
||||||
using System.IO;
|
|
||||||
using Ardalis.ApiEndpoints;
|
using Ardalis.ApiEndpoints;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.eShopWeb.ApplicationCore.Constants;
|
|
||||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||||
using Swashbuckle.AspNetCore.Annotations;
|
using Swashbuckle.AspNetCore.Annotations;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Microsoft.Extensions.Hosting;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.PublicApi
|
namespace Microsoft.eShopWeb.PublicApi
|
||||||
{
|
{
|
||||||
@@ -43,6 +44,13 @@ namespace Microsoft.eShopWeb.PublicApi
|
|||||||
|
|
||||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||||
Host.CreateDefaultBuilder(args)
|
Host.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureAppConfiguration((builderContext, config) =>
|
||||||
|
{
|
||||||
|
var env = builderContext.HostingEnvironment;
|
||||||
|
config
|
||||||
|
.AddJsonFile("appsettings.json")
|
||||||
|
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||||
|
})
|
||||||
.ConfigureWebHostDefaults(webBuilder =>
|
.ConfigureWebHostDefaults(webBuilder =>
|
||||||
{
|
{
|
||||||
webBuilder.UseStartup<Startup>();
|
webBuilder.UseStartup<Startup>();
|
||||||
|
|||||||
@@ -25,13 +25,6 @@
|
|||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
},
|
},
|
||||||
"applicationUrl": "https://localhost:5099;http://localhost:5098"
|
"applicationUrl": "https://localhost:5099;http://localhost:5098"
|
||||||
},
|
|
||||||
"Docker": {
|
|
||||||
"commandName": "Docker",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
|
|
||||||
"publishAllPorts": true,
|
|
||||||
"useSSL": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
|
using BlazorShared;
|
||||||
using BlazorShared.Authorization;
|
using BlazorShared.Authorization;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
@@ -28,7 +29,6 @@ namespace Microsoft.eShopWeb.PublicApi
|
|||||||
public class Startup
|
public class Startup
|
||||||
{
|
{
|
||||||
private const string CORS_POLICY = "CorsPolicy";
|
private const string CORS_POLICY = "CorsPolicy";
|
||||||
public static bool InDocker => Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") == "true";
|
|
||||||
|
|
||||||
public Startup(IConfiguration configuration)
|
public Startup(IConfiguration configuration)
|
||||||
{
|
{
|
||||||
@@ -46,6 +46,11 @@ namespace Microsoft.eShopWeb.PublicApi
|
|||||||
//ConfigureProductionServices(services);
|
//ConfigureProductionServices(services);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ConfigureDockerServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
ConfigureDevelopmentServices(services);
|
||||||
|
}
|
||||||
|
|
||||||
private void ConfigureInMemoryDatabases(IServiceCollection services)
|
private void ConfigureInMemoryDatabases(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddDbContext<CatalogContext>(c =>
|
services.AddDbContext<CatalogContext>(c =>
|
||||||
@@ -90,7 +95,10 @@ namespace Microsoft.eShopWeb.PublicApi
|
|||||||
services.AddSingleton<IUriComposer>(new UriComposer(Configuration.Get<CatalogSettings>()));
|
services.AddSingleton<IUriComposer>(new UriComposer(Configuration.Get<CatalogSettings>()));
|
||||||
services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>));
|
services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>));
|
||||||
services.AddScoped<ITokenClaimsService, IdentityTokenClaimService>();
|
services.AddScoped<ITokenClaimsService, IdentityTokenClaimService>();
|
||||||
services.AddScoped<IFileSystem, WebFileSystem>(x => new WebFileSystem($"{Constants.GetWebUrlInternal(Startup.InDocker)}File"));
|
|
||||||
|
var baseUrlConfig = new BaseUrlConfiguration();
|
||||||
|
Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig);
|
||||||
|
services.AddScoped<IFileSystem, WebFileSystem>(x => new WebFileSystem($"{baseUrlConfig.WebBase}File"));
|
||||||
|
|
||||||
services.AddMemoryCache();
|
services.AddMemoryCache();
|
||||||
|
|
||||||
@@ -112,15 +120,12 @@ namespace Microsoft.eShopWeb.PublicApi
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
services.AddCors(options =>
|
services.AddCors(options =>
|
||||||
{
|
{
|
||||||
options.AddPolicy(name: CORS_POLICY,
|
options.AddPolicy(name: CORS_POLICY,
|
||||||
builder =>
|
builder =>
|
||||||
{
|
{
|
||||||
builder.WithOrigins("http://localhost:44319",
|
builder.WithOrigins(baseUrlConfig.WebBase.Replace("host.docker.internal", "localhost").TrimEnd('/'));
|
||||||
"https://localhost:44319",
|
|
||||||
Constants.GetOriginWebUrl(InDocker));
|
|
||||||
builder.AllowAnyMethod();
|
builder.AllowAnyMethod();
|
||||||
builder.AllowAnyHeader();
|
builder.AllowAnyHeader();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
{
|
{
|
||||||
|
"baseUrls": {
|
||||||
|
"apiBase": "https://localhost:5099/api/",
|
||||||
|
"webBase": "https://localhost:44315/"
|
||||||
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
|
|||||||
17
src/PublicApi/appsettings.Docker.json
Normal file
17
src/PublicApi/appsettings.Docker.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"CatalogConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;",
|
||||||
|
"IdentityConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.Identity;"
|
||||||
|
},
|
||||||
|
"baseUrls": {
|
||||||
|
"apiBase": "http://localhost:5200/api/",
|
||||||
|
"webBase": "http://host.docker.internal:5106/"
|
||||||
|
},
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
{
|
{
|
||||||
|
"baseUrls": {
|
||||||
|
"apiBase": "https://localhost:5099/api/",
|
||||||
|
"webBase": "https://localhost:44315/"
|
||||||
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"CatalogConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;",
|
"CatalogConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;",
|
||||||
"IdentityConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.Identity;"
|
"IdentityConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.Identity;"
|
||||||
|
|||||||
@@ -22,16 +22,12 @@ namespace Microsoft.eShopWeb.Web.Areas.Identity.Pages.Account
|
|||||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||||
private readonly ILogger<LoginModel> _logger;
|
private readonly ILogger<LoginModel> _logger;
|
||||||
private readonly IBasketService _basketService;
|
private readonly IBasketService _basketService;
|
||||||
private readonly AuthService _authService;
|
|
||||||
private readonly ITokenClaimsService _tokenClaimsService;
|
|
||||||
|
|
||||||
public LoginModel(SignInManager<ApplicationUser> signInManager, ILogger<LoginModel> logger, IBasketService basketService, AuthService authService, ITokenClaimsService tokenClaimsService)
|
public LoginModel(SignInManager<ApplicationUser> signInManager, ILogger<LoginModel> logger, IBasketService basketService)
|
||||||
{
|
{
|
||||||
_signInManager = signInManager;
|
_signInManager = signInManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_basketService = basketService;
|
_basketService = basketService;
|
||||||
_authService = authService;
|
|
||||||
_tokenClaimsService = tokenClaimsService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BindProperty]
|
[BindProperty]
|
||||||
@@ -88,8 +84,6 @@ namespace Microsoft.eShopWeb.Web.Areas.Identity.Pages.Account
|
|||||||
|
|
||||||
if (result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
var token = await _tokenClaimsService.GetTokenAsync(Input.Email);
|
|
||||||
CreateAuthCookie(Input.Email, token, Startup.InDocker);
|
|
||||||
_logger.LogInformation("User logged in.");
|
_logger.LogInformation("User logged in.");
|
||||||
await TransferAnonymousBasketToUserAsync(Input.Email);
|
await TransferAnonymousBasketToUserAsync(Input.Email);
|
||||||
return LocalRedirect(returnUrl);
|
return LocalRedirect(returnUrl);
|
||||||
@@ -114,15 +108,6 @@ namespace Microsoft.eShopWeb.Web.Areas.Identity.Pages.Account
|
|||||||
return Page();
|
return Page();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateAuthCookie(string username, string token, bool inDocker)
|
|
||||||
{
|
|
||||||
var cookieOptions = new CookieOptions();
|
|
||||||
cookieOptions.Expires = DateTime.Today.AddYears(10);
|
|
||||||
Response.Cookies.Append("token", token, cookieOptions);
|
|
||||||
Response.Cookies.Append("username", username, cookieOptions);
|
|
||||||
Response.Cookies.Append("inDocker", inDocker.ToString(), cookieOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task TransferAnonymousBasketToUserAsync(string userName)
|
private async Task TransferAnonymousBasketToUserAsync(string userName)
|
||||||
{
|
{
|
||||||
if (Request.Cookies.ContainsKey(Constants.BASKET_COOKIENAME))
|
if (Request.Cookies.ContainsKey(Constants.BASKET_COOKIENAME))
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<environment include="Development">
|
<environment include="Development,Docker">
|
||||||
<script src="~/Identity/lib/jquery-validation/dist/jquery.validate.js"></script>
|
<script src="~/Identity/lib/jquery-validation/dist/jquery.validate.js"></script>
|
||||||
<script src="~/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
|
<script src="~/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
|
||||||
</environment>
|
</environment>
|
||||||
<environment exclude="Development">
|
<environment exclude="Development,Docker">
|
||||||
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.17.0/jquery.validate.min.js"
|
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.17.0/jquery.validate.min.js"
|
||||||
asp-fallback-src="~/Identity/lib/jquery-validation/dist/jquery.validate.min.js"
|
asp-fallback-src="~/Identity/lib/jquery-validation/dist/jquery.validate.min.js"
|
||||||
asp-fallback-test="window.jQuery && window.jQuery.validator"
|
asp-fallback-test="window.jQuery && window.jQuery.validator"
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ using System.Security.Claims;
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using BlazorShared.Authorization;
|
using BlazorShared.Authorization;
|
||||||
|
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.Web.Controllers
|
namespace Microsoft.eShopWeb.Web.Controllers
|
||||||
{
|
{
|
||||||
@@ -11,13 +13,20 @@ namespace Microsoft.eShopWeb.Web.Controllers
|
|||||||
[ApiController]
|
[ApiController]
|
||||||
public class UserController : ControllerBase
|
public class UserController : ControllerBase
|
||||||
{
|
{
|
||||||
|
private readonly ITokenClaimsService _tokenClaimsService;
|
||||||
|
|
||||||
|
public UserController(ITokenClaimsService tokenClaimsService)
|
||||||
|
{
|
||||||
|
_tokenClaimsService = tokenClaimsService;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public IActionResult GetCurrentUser() =>
|
public async Task<IActionResult> GetCurrentUser() =>
|
||||||
Ok(User.Identity.IsAuthenticated ? CreateUserInfo(User) : UserInfo.Anonymous);
|
Ok(User.Identity.IsAuthenticated ? await CreateUserInfo(User) : UserInfo.Anonymous);
|
||||||
|
|
||||||
private UserInfo CreateUserInfo(ClaimsPrincipal claimsPrincipal)
|
private async Task<UserInfo> CreateUserInfo(ClaimsPrincipal claimsPrincipal)
|
||||||
{
|
{
|
||||||
if (!claimsPrincipal.Identity.IsAuthenticated)
|
if (!claimsPrincipal.Identity.IsAuthenticated)
|
||||||
{
|
{
|
||||||
@@ -57,6 +66,9 @@ namespace Microsoft.eShopWeb.Web.Controllers
|
|||||||
userInfo.Claims = claims;
|
userInfo.Claims = claims;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var token = await _tokenClaimsService.GetTokenAsync(claimsPrincipal.Identity.Name);
|
||||||
|
userInfo.Token = token;
|
||||||
|
|
||||||
return userInfo;
|
return userInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,10 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<title>@ViewData["Title"] - Microsoft.eShopOnWeb</title>
|
<title>@ViewData["Title"] - Microsoft.eShopOnWeb</title>
|
||||||
<base href="~/" />
|
<base href="~/" />
|
||||||
<environment include="Development">
|
<environment include="Development,Docker">
|
||||||
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
|
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
|
||||||
</environment>
|
</environment>
|
||||||
<environment exclude="Development">
|
<environment exclude="Development,Docker">
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
||||||
asp-fallback-href="css/bootstrap/bootstrap.min.css"
|
asp-fallback-href="css/bootstrap/bootstrap.min.css"
|
||||||
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute"
|
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Microsoft.Extensions.Hosting;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.Web
|
namespace Microsoft.eShopWeb.Web
|
||||||
{
|
{
|
||||||
@@ -42,6 +43,13 @@ namespace Microsoft.eShopWeb.Web
|
|||||||
|
|
||||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||||
Host.CreateDefaultBuilder(args)
|
Host.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureAppConfiguration((builderContext, config) =>
|
||||||
|
{
|
||||||
|
var env = builderContext.HostingEnvironment;
|
||||||
|
config
|
||||||
|
.AddJsonFile("appsettings.json")
|
||||||
|
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||||
|
})
|
||||||
.ConfigureWebHostDefaults(webBuilder =>
|
.ConfigureWebHostDefaults(webBuilder =>
|
||||||
{
|
{
|
||||||
webBuilder.UseStartup<Startup>();
|
webBuilder.UseStartup<Startup>();
|
||||||
|
|||||||
@@ -25,13 +25,13 @@ using Blazored.LocalStorage;
|
|||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.DataProtection;
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||||
|
using BlazorShared;
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.Web
|
namespace Microsoft.eShopWeb.Web
|
||||||
{
|
{
|
||||||
public class Startup
|
public class Startup
|
||||||
{
|
{
|
||||||
private IServiceCollection _services;
|
private IServiceCollection _services;
|
||||||
public static bool InDocker => Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") == "true";
|
|
||||||
|
|
||||||
public Startup(IConfiguration configuration)
|
public Startup(IConfiguration configuration)
|
||||||
{
|
{
|
||||||
@@ -49,6 +49,15 @@ namespace Microsoft.eShopWeb.Web
|
|||||||
//ConfigureProductionServices(services);
|
//ConfigureProductionServices(services);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ConfigureDockerServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddDataProtection()
|
||||||
|
.SetApplicationName("eshopwebmvc")
|
||||||
|
.PersistKeysToFileSystem(new DirectoryInfo(@"./"));
|
||||||
|
|
||||||
|
ConfigureDevelopmentServices(services);
|
||||||
|
}
|
||||||
|
|
||||||
private void ConfigureInMemoryDatabases(IServiceCollection services)
|
private void ConfigureInMemoryDatabases(IServiceCollection services)
|
||||||
{
|
{
|
||||||
// use in-memory database
|
// use in-memory database
|
||||||
@@ -88,12 +97,6 @@ namespace Microsoft.eShopWeb.Web
|
|||||||
{
|
{
|
||||||
services.AddCookieSettings();
|
services.AddCookieSettings();
|
||||||
|
|
||||||
if (InDocker)
|
|
||||||
{
|
|
||||||
services.AddDataProtection()
|
|
||||||
.SetApplicationName("eshopwebmvc")
|
|
||||||
.PersistKeysToFileSystem(new DirectoryInfo(@"./"));
|
|
||||||
}
|
|
||||||
|
|
||||||
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
||||||
.AddCookie(options =>
|
.AddCookie(options =>
|
||||||
@@ -141,15 +144,22 @@ namespace Microsoft.eShopWeb.Web
|
|||||||
config.Path = "/allservices";
|
config.Path = "/allservices";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var baseUrlConfig = new BaseUrlConfiguration();
|
||||||
|
Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig);
|
||||||
|
services.AddScoped<BaseUrlConfiguration>(sp => baseUrlConfig);
|
||||||
// Blazor Admin Required Services for Prerendering
|
// Blazor Admin Required Services for Prerendering
|
||||||
services.AddScoped<HttpClient>(s => new HttpClient
|
services.AddScoped<HttpClient>(s => new HttpClient
|
||||||
{
|
{
|
||||||
BaseAddress = new Uri(BlazorShared.Authorization.Constants.GetWebUrl(InDocker))
|
BaseAddress = new Uri(baseUrlConfig.WebBase)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// add blazor services
|
||||||
services.AddBlazoredLocalStorage();
|
services.AddBlazoredLocalStorage();
|
||||||
services.AddServerSideBlazor();
|
services.AddServerSideBlazor();
|
||||||
services.AddScoped<AuthService>();
|
services.AddScoped<AuthService>();
|
||||||
|
|
||||||
|
services.AddScoped<HttpService>();
|
||||||
services.AddBlazorServices();
|
services.AddBlazorServices();
|
||||||
|
|
||||||
_services = services; // used to debug registered services
|
_services = services; // used to debug registered services
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>@ViewData["Title"] - Microsoft.eShopOnWeb</title>
|
<title>@ViewData["Title"] - Microsoft.eShopOnWeb</title>
|
||||||
<environment names="Development">
|
<environment names="Development,Docker">
|
||||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
|
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
|
||||||
<link rel="stylesheet" href="~/css/app.css" />
|
<link rel="stylesheet" href="~/css/app.css" />
|
||||||
<link rel="stylesheet" href="~/css/app.component.css" />
|
<link rel="stylesheet" href="~/css/app.component.css" />
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<environment names="Development">
|
<environment names="Development,Docker">
|
||||||
<script src="~/lib/jquery/jquery.js"></script>
|
<script src="~/lib/jquery/jquery.js"></script>
|
||||||
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
|
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
|
||||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<environment names="Development">
|
<environment names="Development,Docker">
|
||||||
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
|
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
|
||||||
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
|
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
|
||||||
</environment>
|
</environment>
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
{
|
{
|
||||||
|
"baseUrls": {
|
||||||
|
"apiBase": "https://localhost:5099/api/",
|
||||||
|
"webBase": "https://localhost:44315/"
|
||||||
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Debug",
|
"Default": "Debug",
|
||||||
|
|||||||
17
src/Web/appsettings.Docker.json
Normal file
17
src/Web/appsettings.Docker.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"CatalogConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;",
|
||||||
|
"IdentityConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.Identity;"
|
||||||
|
},
|
||||||
|
"baseUrls": {
|
||||||
|
"apiBase": "http://localhost:5200/api/",
|
||||||
|
"webBase": "http://host.docker.internal:5106/"
|
||||||
|
},
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Debug",
|
||||||
|
"System": "Information",
|
||||||
|
"Microsoft": "Information"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
{
|
{
|
||||||
|
"baseUrls": {
|
||||||
|
"apiBase": "https://localhost:5099/api/",
|
||||||
|
"webBase": "https://localhost:44315/"
|
||||||
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"CatalogConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;",
|
"CatalogConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;",
|
||||||
"IdentityConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.Identity;"
|
"IdentityConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.Identity;"
|
||||||
|
|||||||
2
src/Web/wwwroot/css/site.min.css
vendored
2
src/Web/wwwroot/css/site.min.css
vendored
File diff suppressed because one or more lines are too long
BIN
src/Web/wwwroot/images/products/5.jpg
Normal file
BIN
src/Web/wwwroot/images/products/5.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Reference in New Issue
Block a user