From a804a53f8c7693ab265bdd551c2eb1a486299c7f Mon Sep 17 00:00:00 2001 From: espent1004 Date: Sun, 27 Oct 2019 22:45:23 +0100 Subject: [PATCH] Adding project --- eShopOnContainers-ServicesAndWebApps.sln | 108 ++--- .../Controllers/ValuesController.cs | 45 ++ src/Services/TenantACustomisations/Dockerfile | 59 +++ .../Filters/AuthorizeCheckOperationFilter.cs | 33 ++ .../Filters/HttpGlobalExceptionFilter.cs | 70 +++ src/Services/TenantACustomisations/Program.cs | 87 ++++ .../Properties/launchSettings.json | 30 ++ src/Services/TenantACustomisations/Startup.cs | 411 ++++++++++++++++++ .../TenantACustomisations.csproj | 61 +++ .../appsettings.Development.json | 9 + .../TenantACustomisations/appsettings.json | 8 + 11 files changed, 867 insertions(+), 54 deletions(-) create mode 100644 src/Services/TenantACustomisations/Controllers/ValuesController.cs create mode 100644 src/Services/TenantACustomisations/Dockerfile create mode 100644 src/Services/TenantACustomisations/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs create mode 100644 src/Services/TenantACustomisations/Infrastructure/Filters/HttpGlobalExceptionFilter.cs create mode 100644 src/Services/TenantACustomisations/Program.cs create mode 100644 src/Services/TenantACustomisations/Properties/launchSettings.json create mode 100644 src/Services/TenantACustomisations/Startup.cs create mode 100644 src/Services/TenantACustomisations/TenantACustomisations.csproj create mode 100644 src/Services/TenantACustomisations/appsettings.Development.json create mode 100644 src/Services/TenantACustomisations/appsettings.json diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln index 0de6d0ed7..929dde778 100644 --- a/eShopOnContainers-ServicesAndWebApps.sln +++ b/eShopOnContainers-ServicesAndWebApps.sln @@ -150,14 +150,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Devspace.Support", "Devspac EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devspaces.Support", "src\BuildingBlocks\Devspaces.Support\Devspaces.Support.csproj", "{56C2EF0B-6BF2-41D9-BE07-6E6D08D06B35}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TenantCustomisations", "TenantCustomisations", "{D2D53ADC-5230-4EF4-95B0-31FD734992F6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TenantACustomisations", "..\TenantACustomisations\TenantACustomisations.csproj", "{6CBF0D7F-7566-428E-B704-4059A10FFD52}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TenantManager", "TenantManager", "{71587505-2945-4286-BF0B-BA4C004B0BA7}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TenantManager", "src\Services\TenantManager\TenantManager\TenantManager.csproj", "{9C101827-119D-44EE-B0F0-94E7ABA8AE6A}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TenantCustomisations", "TenantCustomisations", "{773A0C2A-CA6F-4D4A-860B-C518EFA6FACB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TenantACustomisations", "src\Services\TenantACustomisations\TenantACustomisations.csproj", "{58221C04-F14C-47E3-A802-E5979B4A5630}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -1804,54 +1804,6 @@ Global {56C2EF0B-6BF2-41D9-BE07-6E6D08D06B35}.Release|x64.Build.0 = Release|Any CPU {56C2EF0B-6BF2-41D9-BE07-6E6D08D06B35}.Release|x86.ActiveCfg = Release|Any CPU {56C2EF0B-6BF2-41D9-BE07-6E6D08D06B35}.Release|x86.Build.0 = Release|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|ARM.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|iPhone.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|x64.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|x64.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|x86.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|x86.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|ARM.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|ARM.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|iPhone.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|x64.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|x64.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|x86.ActiveCfg = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|x86.Build.0 = Debug|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|Any CPU.Build.0 = Release|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|ARM.ActiveCfg = Release|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|ARM.Build.0 = Release|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|iPhone.ActiveCfg = Release|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|iPhone.Build.0 = Release|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|x64.ActiveCfg = Release|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|x64.Build.0 = Release|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|x86.ActiveCfg = Release|Any CPU - {6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|x86.Build.0 = Release|Any CPU {9C101827-119D-44EE-B0F0-94E7ABA8AE6A}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {9C101827-119D-44EE-B0F0-94E7ABA8AE6A}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {9C101827-119D-44EE-B0F0-94E7ABA8AE6A}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU @@ -1900,6 +1852,54 @@ Global {9C101827-119D-44EE-B0F0-94E7ABA8AE6A}.Release|x64.Build.0 = Release|Any CPU {9C101827-119D-44EE-B0F0-94E7ABA8AE6A}.Release|x86.ActiveCfg = Release|Any CPU {9C101827-119D-44EE-B0F0-94E7ABA8AE6A}.Release|x86.Build.0 = Release|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.AppStore|ARM.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.AppStore|iPhone.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.AppStore|x64.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.AppStore|x64.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.AppStore|x86.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.AppStore|x86.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Debug|ARM.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Debug|ARM.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Debug|iPhone.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Debug|x64.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Debug|x64.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Debug|x86.ActiveCfg = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Debug|x86.Build.0 = Debug|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Release|Any CPU.ActiveCfg = Release|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Release|Any CPU.Build.0 = Release|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Release|ARM.ActiveCfg = Release|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Release|ARM.Build.0 = Release|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Release|iPhone.ActiveCfg = Release|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Release|iPhone.Build.0 = Release|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Release|x64.ActiveCfg = Release|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Release|x64.Build.0 = Release|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Release|x86.ActiveCfg = Release|Any CPU + {58221C04-F14C-47E3-A802-E5979B4A5630}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1966,10 +1966,10 @@ Global {766D7E92-6AF0-476C-ADD5-282BF4D8C576} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04} {68F5041D-51F2-4630-94B6-B49789F5E51A} = {DB0EFB20-B024-4E5E-A75C-52143C131D25} {56C2EF0B-6BF2-41D9-BE07-6E6D08D06B35} = {68F5041D-51F2-4630-94B6-B49789F5E51A} - {D2D53ADC-5230-4EF4-95B0-31FD734992F6} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8} - {6CBF0D7F-7566-428E-B704-4059A10FFD52} = {D2D53ADC-5230-4EF4-95B0-31FD734992F6} {71587505-2945-4286-BF0B-BA4C004B0BA7} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8} {9C101827-119D-44EE-B0F0-94E7ABA8AE6A} = {71587505-2945-4286-BF0B-BA4C004B0BA7} + {773A0C2A-CA6F-4D4A-860B-C518EFA6FACB} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8} + {58221C04-F14C-47E3-A802-E5979B4A5630} = {773A0C2A-CA6F-4D4A-860B-C518EFA6FACB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {25728519-5F0F-4973-8A64-0A81EB4EA8D9} diff --git a/src/Services/TenantACustomisations/Controllers/ValuesController.cs b/src/Services/TenantACustomisations/Controllers/ValuesController.cs new file mode 100644 index 000000000..e297683b6 --- /dev/null +++ b/src/Services/TenantACustomisations/Controllers/ValuesController.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace TenantACustomisations.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class ValuesController : ControllerBase + { + // GET api/values + [HttpGet] + public ActionResult> Get() + { + return new string[] { "value1", "value2" }; + } + + // GET api/values/5 + [HttpGet("{id}")] + public ActionResult Get(int id) + { + return "value"; + } + + // POST api/values + [HttpPost] + public void Post([FromBody] string value) + { + } + + // PUT api/values/5 + [HttpPut("{id}")] + public void Put(int id, [FromBody] string value) + { + } + + // DELETE api/values/5 + [HttpDelete("{id}")] + public void Delete(int id) + { + } + } +} diff --git a/src/Services/TenantACustomisations/Dockerfile b/src/Services/TenantACustomisations/Dockerfile new file mode 100644 index 000000000..5a3805dec --- /dev/null +++ b/src/Services/TenantACustomisations/Dockerfile @@ -0,0 +1,59 @@ +FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base +WORKDIR /app +EXPOSE 80 + +FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build +WORKDIR /src + +# Keep the project list and command dotnet restore identical in all Dockfiles to maximize image cache utilization +COPY eShopOnContainers-ServicesAndWebApps.sln . +COPY docker-compose.dcproj /src/ +COPY src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj src/ApiGateways/ApiGw-Base/ +COPY src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj src/ApiGateways/Mobile.Bff.Shopping/aggregator/ +COPY src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj src/ApiGateways/Web.Bff.Shopping/aggregator/ +COPY src/BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj src/BuildingBlocks/Devspaces.Support/ +COPY src/BuildingBlocks/EventBus/EventBus/EventBus.csproj src/BuildingBlocks/EventBus/EventBus/ +COPY src/BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj src/BuildingBlocks/EventBus/EventBus.Tests/ +COPY src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj src/BuildingBlocks/EventBus/EventBusRabbitMQ/ +COPY src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj src/BuildingBlocks/EventBus/EventBusServiceBus/ +COPY src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj src/BuildingBlocks/EventBus/IntegrationEventLogEF/ +COPY src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj src/BuildingBlocks/WebHostCustomization/WebHost.Customization/ +COPY src/Services/Basket/Basket.API/Basket.API.csproj src/Services/Basket/Basket.API/ +COPY src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj src/Services/Basket/Basket.FunctionalTests/ +COPY src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj src/Services/Basket/Basket.UnitTests/ +COPY src/Services/Catalog/Catalog.API/Catalog.API.csproj src/Services/Catalog/Catalog.API/ +COPY src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj src/Services/Catalog/Catalog.FunctionalTests/ +COPY src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj src/Services/Catalog/Catalog.UnitTests/ +COPY src/Services/Identity/Identity.API/Identity.API.csproj src/Services/Identity/Identity.API/ +COPY src/Services/Location/Locations.API/Locations.API.csproj src/Services/Location/Locations.API/ +COPY src/Services/Location/Locations.FunctionalTests/Locations.FunctionalTests.csproj src/Services/Location/Locations.FunctionalTests/ +COPY src/Services/Marketing/Marketing.API/Marketing.API.csproj src/Services/Marketing/Marketing.API/ +COPY src/Services/Marketing/Marketing.FunctionalTests/Marketing.FunctionalTests.csproj src/Services/Marketing/Marketing.FunctionalTests/ +COPY src/Services/Ordering/Ordering.API/Ordering.API.csproj src/Services/Ordering/Ordering.API/ +COPY src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj src/Services/Ordering/Ordering.BackgroundTasks/ +COPY src/Services/Ordering/Ordering.Domain/Ordering.Domain.csproj src/Services/Ordering/Ordering.Domain/ +COPY src/Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj src/Services/Ordering/Ordering.FunctionalTests/ +COPY src/Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj src/Services/Ordering/Ordering.Infrastructure/ +COPY src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj src/Services/Ordering/Ordering.SignalrHub/ +COPY src/Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj src/Services/Ordering/Ordering.UnitTests/ +COPY src/Services/Payment/Payment.API/Payment.API.csproj src/Services/Payment/Payment.API/ +COPY src/Services/Webhooks/Webhooks.API/Webhooks.API.csproj src/Services/Webhooks/Webhooks.API/ +COPY src/Web/WebhookClient/WebhookClient.csproj src/Web/WebhookClient/ +COPY src/Web/WebMVC/WebMVC.csproj src/Web/WebMVC/ +COPY src/Web/WebSPA/WebSPA.csproj src/Web/WebSPA/ +COPY src/Web/WebStatus/WebStatus.csproj src/Web/WebStatus/ +COPY test/ServicesTests/Application.FunctionalTests/Application.FunctionalTests.csproj test/ServicesTests/Application.FunctionalTests/ +COPY test/ServicesTests/LoadTest/LoadTest.csproj test/ServicesTests/LoadTest/ + +RUN dotnet restore eShopOnContainers-ServicesAndWebApps.sln + +COPY . . +WORKDIR /src/src/Services/TenantCustomisations/TenantACustomisations +RUN dotnet publish --no-restore -c Release -o /app + +FROM build AS publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "TenantACustomisations.dll"] diff --git a/src/Services/TenantACustomisations/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs b/src/Services/TenantACustomisations/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs new file mode 100644 index 000000000..2851101a8 --- /dev/null +++ b/src/Services/TenantACustomisations/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Authorization; +using Swashbuckle.AspNetCore.Swagger; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace TenantACustomisations.Infrastructure.Filters +{ + public class AuthorizeCheckOperationFilter : IOperationFilter + { + public void Apply(Operation operation, OperationFilterContext context) + { + // Check for authorize attribute + var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType().Any() || + context.MethodInfo.GetCustomAttributes(true).OfType().Any(); + + if (!hasAuthorize) return; + + operation.Responses.TryAdd("401", new Response { Description = "Unauthorized" }); + operation.Responses.TryAdd("403", new Response { Description = "Forbidden" }); + + operation.Security = new List>> + { + new Dictionary> + { + { "oauth2", new [] { "orderingapi" } } + } + }; + } + } +} \ No newline at end of file diff --git a/src/Services/TenantACustomisations/Infrastructure/Filters/HttpGlobalExceptionFilter.cs b/src/Services/TenantACustomisations/Infrastructure/Filters/HttpGlobalExceptionFilter.cs new file mode 100644 index 000000000..a4ce415ea --- /dev/null +++ b/src/Services/TenantACustomisations/Infrastructure/Filters/HttpGlobalExceptionFilter.cs @@ -0,0 +1,70 @@ +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Filters +{ + using AspNetCore.Mvc; + using global::Ordering.Domain.Exceptions; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Mvc.Filters; + using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.ActionResults; + using Microsoft.Extensions.Logging; + using System.Net; + + public class HttpGlobalExceptionFilter : IExceptionFilter + { + private readonly IHostingEnvironment env; + private readonly ILogger logger; + + public HttpGlobalExceptionFilter(IHostingEnvironment env, ILogger logger) + { + this.env = env; + this.logger = logger; + } + + public void OnException(ExceptionContext context) + { + logger.LogError(new EventId(context.Exception.HResult), + context.Exception, + context.Exception.Message); + + if (context.Exception.GetType() == typeof(OrderingDomainException)) + { + var problemDetails = new ValidationProblemDetails() + { + Instance = context.HttpContext.Request.Path, + Status = StatusCodes.Status400BadRequest, + Detail = "Please refer to the errors property for additional details." + }; + + problemDetails.Errors.Add("DomainValidations", new string[] { context.Exception.Message.ToString() }); + + context.Result = new BadRequestObjectResult(problemDetails); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; + } + else + { + var json = new JsonErrorResponse + { + Messages = new[] { "An error occur.Try it again." } + }; + + if (env.IsDevelopment()) + { + json.DeveloperMessage = context.Exception; + } + + // Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 + // It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information + context.Result = new InternalServerErrorObjectResult(json); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + } + context.ExceptionHandled = true; + } + + private class JsonErrorResponse + { + public string[] Messages { get; set; } + + public object DeveloperMessage { get; set; } + } + } +} diff --git a/src/Services/TenantACustomisations/Program.cs b/src/Services/TenantACustomisations/Program.cs new file mode 100644 index 000000000..bfe683782 --- /dev/null +++ b/src/Services/TenantACustomisations/Program.cs @@ -0,0 +1,87 @@ +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Serilog; +using System; +using System.IO; + +namespace Microsoft.eShopOnContainers.Services.Ordering.API +{ + public class Program + { + public static readonly string Namespace = typeof(Program).Namespace; + public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); + + public static int Main(string[] args) + { + var configuration = GetConfiguration(); + + Log.Logger = CreateSerilogLogger(configuration); + + try + { + Log.Information("Configuring web host ({ApplicationContext})...", AppName); + var host = BuildWebHost(configuration, args); + + Log.Information("Starting web host ({ApplicationContext})...", AppName); + host.Run(); + + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName); + return 1; + } + finally + { + Log.CloseAndFlush(); + } + } + + private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) => + WebHost.CreateDefaultBuilder(args) + .CaptureStartupErrors(false) + .UseStartup() + .UseApplicationInsights() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseConfiguration(configuration) + .UseSerilog() + .Build(); + + private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) + { + var seqServerUrl = configuration["Serilog:SeqServerUrl"]; + var logstashUrl = configuration["Serilog:LogstashgUrl"]; + return new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.WithProperty("ApplicationContext", AppName) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) + .WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://logstash:8080" : logstashUrl) + .ReadFrom.Configuration(configuration) + .CreateLogger(); + } + + private static IConfiguration GetConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddEnvironmentVariables(); + + var config = builder.Build(); + + if (config.GetValue("UseVault", false)) + { + builder.AddAzureKeyVault( + $"https://{config["Vault:Name"]}.vault.azure.net/", + config["Vault:ClientId"], + config["Vault:ClientSecret"]); + } + + return builder.Build(); + } + } +} \ No newline at end of file diff --git a/src/Services/TenantACustomisations/Properties/launchSettings.json b/src/Services/TenantACustomisations/Properties/launchSettings.json new file mode 100644 index 000000000..09e073222 --- /dev/null +++ b/src/Services/TenantACustomisations/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:57040", + "sslPort": 44328 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "api/values", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "TenantACustomisations": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "api/values", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/src/Services/TenantACustomisations/Startup.cs b/src/Services/TenantACustomisations/Startup.cs new file mode 100644 index 000000000..69538d9c1 --- /dev/null +++ b/src/Services/TenantACustomisations/Startup.cs @@ -0,0 +1,411 @@ +namespace Microsoft.eShopOnContainers.Services.Ordering.API +{ + using AspNetCore.Http; + using Autofac; + using Autofac.Extensions.DependencyInjection; + using Microsoft.ApplicationInsights.Extensibility; + using Microsoft.ApplicationInsights.ServiceFabric; + using Microsoft.AspNetCore.Authentication.JwtBearer; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Mvc; + using Microsoft.Azure.ServiceBus; + using Microsoft.EntityFrameworkCore; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; + using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; + using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Logging; + using RabbitMQ.Client; + using Swashbuckle.AspNetCore.Swagger; + using System; + using System.Collections.Generic; + using System.Data.Common; + using System.IdentityModel.Tokens.Jwt; + using System.Reflection; + using HealthChecks.UI.Client; + using Microsoft.AspNetCore.Diagnostics.HealthChecks; + using Microsoft.Extensions.Diagnostics.HealthChecks; + using TenantACustomisations.Infrastructure.Filters; + + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public IServiceProvider ConfigureServices(IServiceCollection services) + { + services.AddApplicationInsights(Configuration) + .AddCustomMvc() + .AddHealthChecks(Configuration) + .AddCustomDbContext(Configuration) + .AddCustomSwagger(Configuration) + .AddCustomIntegrations(Configuration) + .AddCustomConfiguration(Configuration) + .AddEventBus(Configuration) + .AddCustomAuthentication(Configuration); + + //configure autofac + + var container = new ContainerBuilder(); + container.Populate(services); + + container.RegisterModule(new MediatorModule()); + container.RegisterModule(new ApplicationModule(Configuration["ConnectionString"])); + + return new AutofacServiceProvider(container.Build()); + } + + + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + { + //loggerFactory.AddAzureWebAppDiagnostics(); + //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + + var pathBase = Configuration["PATH_BASE"]; + if (!string.IsNullOrEmpty(pathBase)) + { + loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); + app.UsePathBase(pathBase); + } + + app.UseCors("CorsPolicy"); + + app.UseHealthChecks("/liveness", new HealthCheckOptions + { + Predicate = r => r.Name.Contains("self") + }); + + app.UseHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + + ConfigureAuth(app); + + app.UseMvcWithDefaultRoute(); + + app.UseSwagger() + .UseSwaggerUI(c => + { + c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Ordering.API V1"); + c.OAuthClientId("orderingswaggerui"); + c.OAuthAppName("Ordering Swagger UI"); + }); + + ConfigureEventBus(app); + } + + + private void ConfigureEventBus(IApplicationBuilder app) + { + var eventBus = app.ApplicationServices.GetRequiredService(); + + //eventBus.Subscribe>(); + } + + protected virtual void ConfigureAuth(IApplicationBuilder app) + { + if (Configuration.GetValue("UseLoadTest")) + { + app.UseMiddleware(); + } + + app.UseAuthentication(); + } + } + + static class CustomExtensionsMethods + { + public static IServiceCollection AddApplicationInsights(this IServiceCollection services, IConfiguration configuration) + { + services.AddApplicationInsightsTelemetry(configuration); + var orchestratorType = configuration.GetValue("OrchestratorType"); + + if (orchestratorType?.ToUpper() == "K8S") + { + // Enable K8s telemetry initializer + services.AddApplicationInsightsKubernetesEnricher(); + } + if (orchestratorType?.ToUpper() == "SF") + { + // Enable SF telemetry initializer + services.AddSingleton((serviceProvider) => + new FabricTelemetryInitializer()); + } + + return services; + } + + public static IServiceCollection AddCustomMvc(this IServiceCollection services) + { + // Add framework services. + services.AddMvc(options => + { + options.Filters.Add(typeof(HttpGlobalExceptionFilter)); + }) + .SetCompatibilityVersion(CompatibilityVersion.Version_2_2) + .AddControllersAsServices(); //Injecting Controllers themselves thru DI + //For further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services + + services.AddCors(options => + { + options.AddPolicy("CorsPolicy", + builder => builder + .SetIsOriginAllowed((host) => true) + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials()); + }); + + return services; + } + + public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) + { + var hcBuilder = services.AddHealthChecks(); + + hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); + + hcBuilder + .AddSqlServer( + configuration["ConnectionString"], + name: "OrderingDB-check", + tags: new string[] { "orderingdb" }); + + if (configuration.GetValue("AzureServiceBusEnabled")) + { + hcBuilder + .AddAzureServiceBusTopic( + configuration["EventBusConnection"], + topicName: "eshop_event_bus", + name: "ordering-servicebus-check", + tags: new string[] { "servicebus" }); + } + else + { + hcBuilder + .AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "ordering-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); + } + + return services; + } + + public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) + { + //services.AddEntityFrameworkSqlServer() + // .AddDbContext(options => + // { + // options.UseSqlServer(configuration["ConnectionString"], + // sqlServerOptionsAction: sqlOptions => + // { + // sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); + // sqlOptions.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); + // }); + // }, + // ServiceLifetime.Scoped //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request) + // ); + + services.AddDbContext(options => + { + options.UseSqlServer(configuration["ConnectionString"], + sqlServerOptionsAction: sqlOptions => + { + sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); + //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + sqlOptions.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); + }); + }); + + return services; + } + + public static IServiceCollection AddCustomSwagger(this IServiceCollection services, IConfiguration configuration) + { + services.AddSwaggerGen(options => + { + options.DescribeAllEnumsAsStrings(); + options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info + { + Title = "Ordering HTTP API", + Version = "v1", + Description = "The Ordering Service HTTP API", + TermsOfService = "Terms Of Service" + }); + + options.AddSecurityDefinition("oauth2", new OAuth2Scheme + { + Type = "oauth2", + Flow = "implicit", + AuthorizationUrl = $"{configuration.GetValue("IdentityUrlExternal")}/connect/authorize", + TokenUrl = $"{configuration.GetValue("IdentityUrlExternal")}/connect/token", + Scopes = new Dictionary() + { + { "orders", "Ordering API" } + } + }); + + options.OperationFilter(); + }); + + return services; + } + + public static IServiceCollection AddCustomIntegrations(this IServiceCollection services, IConfiguration configuration) + { + services.AddSingleton(); + //services.AddTransient(); + services.AddTransient>( + sp => (DbConnection c) => new IntegrationEventLogService(c)); + + //services.AddTransient(); + + if (configuration.GetValue("AzureServiceBusEnabled")) + { + services.AddSingleton(sp => + { + var logger = sp.GetRequiredService>(); + + var serviceBusConnectionString = configuration["EventBusConnection"]; + var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); + + return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger); + }); + } + else + { + services.AddSingleton(sp => + { + var logger = sp.GetRequiredService>(); + + + var factory = new ConnectionFactory() + { + HostName = configuration["EventBusConnection"], + DispatchConsumersAsync = true + }; + + if (!string.IsNullOrEmpty(configuration["EventBusUserName"])) + { + factory.UserName = configuration["EventBusUserName"]; + } + + if (!string.IsNullOrEmpty(configuration["EventBusPassword"])) + { + factory.Password = configuration["EventBusPassword"]; + } + + var retryCount = 5; + if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) + { + retryCount = int.Parse(configuration["EventBusRetryCount"]); + } + + return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); + }); + } + + return services; + } + + public static IServiceCollection AddCustomConfiguration(this IServiceCollection services, IConfiguration configuration) + { + services.AddOptions(); + //services.Configure(configuration); + services.Configure(options => + { + options.InvalidModelStateResponseFactory = context => + { + var problemDetails = new ValidationProblemDetails(context.ModelState) + { + Instance = context.HttpContext.Request.Path, + Status = StatusCodes.Status400BadRequest, + Detail = "Please refer to the errors property for additional details." + }; + + return new BadRequestObjectResult(problemDetails) + { + ContentTypes = { "application/problem+json", "application/problem+xml" } + }; + }; + }); + + return services; + } + + public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration) + { + var subscriptionClientName = configuration["SubscriptionClientName"]; + + if (configuration.GetValue("AzureServiceBusEnabled")) + { + services.AddSingleton(sp => + { + var serviceBusPersisterConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + + return new EventBusServiceBus(serviceBusPersisterConnection, logger, + eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); + }); + } + else + { + services.AddSingleton(sp => + { + var rabbitMQPersistentConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + + var retryCount = 5; + if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) + { + retryCount = int.Parse(configuration["EventBusRetryCount"]); + } + + return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); + }); + } + + services.AddSingleton(); + + return services; + } + + public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) + { + // prevent from mapping "sub" claim to nameidentifier. + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); + + var identityUrl = configuration.GetValue("IdentityUrl"); + + services.AddAuthentication(options => + { + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + + }).AddJwtBearer(options => + { + options.Authority = identityUrl; + options.RequireHttpsMetadata = false; + options.Audience = "orders"; + }); + + return services; + } + } +} diff --git a/src/Services/TenantACustomisations/TenantACustomisations.csproj b/src/Services/TenantACustomisations/TenantACustomisations.csproj new file mode 100644 index 000000000..1e6b1036b --- /dev/null +++ b/src/Services/TenantACustomisations/TenantACustomisations.csproj @@ -0,0 +1,61 @@ + + + + netcoreapp2.2 + InProcess + Linux + ..\eShopOnContainersCustomised + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Services/TenantACustomisations/appsettings.Development.json b/src/Services/TenantACustomisations/appsettings.Development.json new file mode 100644 index 000000000..e203e9407 --- /dev/null +++ b/src/Services/TenantACustomisations/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/src/Services/TenantACustomisations/appsettings.json b/src/Services/TenantACustomisations/appsettings.json new file mode 100644 index 000000000..def9159a7 --- /dev/null +++ b/src/Services/TenantACustomisations/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*" +}