diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln
index d9c994954..edd18d523 100644
--- a/eShopOnContainers-ServicesAndWebApps.sln
+++ b/eShopOnContainers-ServicesAndWebApps.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
-VisualStudioVersion = 15.0.26430.12
+VisualStudioVersion = 15.0.26430.6
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F6-4D07-B109-DA28AD288A63}"
EndProject
@@ -76,6 +76,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Health
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventBus.Tests", "src\BuildingBlocks\EventBus\EventBus.Tests\EventBus.Tests.csproj", "{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataProtection", "DataProtection", "{88B22DBB-AA8F-4290-A454-2C109352C345}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataProtection", "src\BuildingBlocks\DataProtection\DataProtection\DataProtection.csproj", "{23A33F9B-7672-426D-ACF9-FF8436ADC81A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@@ -1002,6 +1006,54 @@ Global
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x64.Build.0 = Release|Any CPU
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x86.ActiveCfg = Release|Any CPU
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x86.Build.0 = Release|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|ARM.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|ARM.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|x64.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|x64.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|x86.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|x86.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|ARM.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|x64.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|x86.Build.0 = Debug|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|ARM.ActiveCfg = Release|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|ARM.Build.0 = Release|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|iPhone.Build.0 = Release|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|x64.ActiveCfg = Release|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|x64.Build.0 = Release|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|x86.ActiveCfg = Release|Any CPU
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1038,5 +1090,7 @@ Global
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5} = {A81ECBC2-6B00-4DCD-8388-469174033379}
{4BD76717-3102-4969-8C2C-BAAA3F0263B6} = {A81ECBC2-6B00-4DCD-8388-469174033379}
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
+ {88B22DBB-AA8F-4290-A454-2C109352C345} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
+ {23A33F9B-7672-426D-ACF9-FF8436ADC81A} = {88B22DBB-AA8F-4290-A454-2C109352C345}
EndGlobalSection
EndGlobal
diff --git a/k8s/deploy.ps1 b/k8s/deploy.ps1
index 0619dd8e8..9930474ba 100644
--- a/k8s/deploy.ps1
+++ b/k8s/deploy.ps1
@@ -58,7 +58,7 @@ ExecKube -cmd 'delete configmap urls'
# start sql, rabbitmq, frontend deploymentsExecKube -cmd 'delete configmap config-files'
ExecKube -cmd 'create configmap config-files --from-file=nginx-conf=nginx.conf'
ExecKube -cmd 'label configmap config-files app=eshop'
-ExecKube -cmd 'create -f sql-data.yaml -f basket-data.yaml -f rabbitmq.yaml -f services.yaml -f frontend.yaml'
+ExecKube -cmd 'create -f sql-data.yaml -f basket-data.yaml -f keystore-data.yaml -f rabbitmq.yaml -f services.yaml -f frontend.yaml'
# building and publishing docker images not necessary when deploying through CI VSTS
if(-not $deployCI) {
diff --git a/k8s/deployments.yaml b/k8s/deployments.yaml
index a0b1df135..62352e2ef 100644
--- a/k8s/deployments.yaml
+++ b/k8s/deployments.yaml
@@ -69,7 +69,6 @@ kind: Deployment
metadata:
name: identity
spec:
- replicas: 3
paused: true
template:
metadata:
@@ -86,6 +85,10 @@ spec:
value: http://0.0.0.0:80/identity
- name: ConnectionStrings__DefaultConnection
value: "Server=sql-data;Initial Catalog=Microsoft.eShopOnContainers.Services.IdentityDb;User Id=sa;Password=Pass@word"
+ - name: DPConnectionString
+ value: keystore-data
+ - name: IsClusterEnv
+ value: 'True'
- name: MvcClient
valueFrom:
configMapKeyRef:
@@ -153,6 +156,10 @@ spec:
env:
- name: ASPNETCORE_URLS
value: http://0.0.0.0:80/webmvc
+ - name: DPConnectionString
+ value: keystore-data
+ - name: IsClusterEnv
+ value: 'True'
- name: BasketUrl
valueFrom:
configMapKeyRef:
@@ -256,6 +263,10 @@ spec:
env:
- name: ASPNETCORE_URLS
value: http://0.0.0.0:80
+ - name: DPConnectionString
+ value: keystore-data
+ - name: IsClusterEnv
+ value: 'True'
- name: BasketUrl
valueFrom:
configMapKeyRef:
diff --git a/k8s/keystore-data.yaml b/k8s/keystore-data.yaml
new file mode 100644
index 000000000..3340cce35
--- /dev/null
+++ b/k8s/keystore-data.yaml
@@ -0,0 +1,29 @@
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app: eshop
+ component: keystore-data
+ name: keystore-data
+spec:
+ ports:
+ - port: 6379
+ selector:
+ app: eshop
+ component: keystore-data
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: keystore-data
+spec:
+ template:
+ metadata:
+ labels:
+ app: eshop
+ component: keystore-data
+ spec:
+ containers:
+ - name: keystore-data
+ image: redis:3.2-alpine
+
diff --git a/src/BuildingBlocks/DataProtection/DataProtection/DataProtection.csproj b/src/BuildingBlocks/DataProtection/DataProtection/DataProtection.csproj
new file mode 100644
index 000000000..bfe61a85a
--- /dev/null
+++ b/src/BuildingBlocks/DataProtection/DataProtection/DataProtection.csproj
@@ -0,0 +1,13 @@
+
+
+
+ netcoreapp1.1
+ Microsoft.eShopOnContainers.BuildingBlocks
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Services/Identity/Identity.API/Extensions/DataProtectionBuilderExtensions.cs b/src/BuildingBlocks/DataProtection/DataProtection/DataProtectionBuilderExtensions.cs
similarity index 93%
rename from src/Services/Identity/Identity.API/Extensions/DataProtectionBuilderExtensions.cs
rename to src/BuildingBlocks/DataProtection/DataProtection/DataProtectionBuilderExtensions.cs
index 11fc8342e..3db776b9a 100644
--- a/src/Services/Identity/Identity.API/Extensions/DataProtectionBuilderExtensions.cs
+++ b/src/BuildingBlocks/DataProtection/DataProtection/DataProtectionBuilderExtensions.cs
@@ -1,13 +1,11 @@
-namespace DataProtectionExtensions
+namespace Microsoft.eShopOnContainers.BuildingBlocks
{
- using System;
- using System.Linq;
- using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.Repositories;
- using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
+ using System;
+ using System.Linq;
using System.Net;
///
@@ -46,10 +44,11 @@
{
throw new ArgumentException("Redis connection string may not be empty.", nameof(redisConnectionString));
}
-
+
var ips = Dns.GetHostAddressesAsync(redisConnectionString).Result;
- return builder.Use(ServiceDescriptor.Singleton(services => new RedisXmlRepository(ips.First().ToString(), services.GetRequiredService>())));
+ return builder.Use(ServiceDescriptor.Singleton(services =>
+ new RedisXmlRepository(ips.First().ToString(), services.GetRequiredService>())));
}
///
diff --git a/src/Services/Identity/Identity.API/Extensions/RedisXmlRepository.cs b/src/BuildingBlocks/DataProtection/DataProtection/RedisXmlRepository.cs
similarity index 95%
rename from src/Services/Identity/Identity.API/Extensions/RedisXmlRepository.cs
rename to src/BuildingBlocks/DataProtection/DataProtection/RedisXmlRepository.cs
index ebb36c1b7..f5a903b65 100644
--- a/src/Services/Identity/Identity.API/Extensions/RedisXmlRepository.cs
+++ b/src/BuildingBlocks/DataProtection/DataProtection/RedisXmlRepository.cs
@@ -1,17 +1,13 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Xml.Linq;
-using Microsoft.AspNetCore.DataProtection.Repositories;
-using Microsoft.Extensions.Logging;
-using StackExchange.Redis;
-
-namespace DataProtectionExtensions
+namespace Microsoft.eShopOnContainers.BuildingBlocks
{
+ using Microsoft.AspNetCore.DataProtection.Repositories;
+ using Microsoft.Extensions.Logging;
+ using StackExchange.Redis;
+ using System;
+ using System.Collections.Generic;
+ using System.Text.RegularExpressions;
+ using System.Xml.Linq;
+
///
/// Key repository that stores XML encrypted keys in a Redis distributed cache.
///
@@ -211,4 +207,4 @@ namespace DataProtectionExtensions
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Services/Identity/Identity.API/Identity.API.csproj b/src/Services/Identity/Identity.API/Identity.API.csproj
index b3e499c6b..c27a51593 100644
--- a/src/Services/Identity/Identity.API/Identity.API.csproj
+++ b/src/Services/Identity/Identity.API/Identity.API.csproj
@@ -39,7 +39,6 @@
-
@@ -59,6 +58,7 @@
+
@@ -70,4 +70,9 @@
+
+
+
+
+
diff --git a/src/Services/Identity/Identity.API/Startup.cs b/src/Services/Identity/Identity.API/Startup.cs
index 98ed8578d..c5f72af13 100644
--- a/src/Services/Identity/Identity.API/Startup.cs
+++ b/src/Services/Identity/Identity.API/Startup.cs
@@ -1,29 +1,27 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
+using Identity.API.Certificate;
+using Identity.API.Configuration;
+using Identity.API.Data;
+using Identity.API.Models;
+using Identity.API.Services;
+using IdentityServer4.EntityFramework.DbContexts;
+using IdentityServer4.EntityFramework.Mappers;
+using IdentityServer4.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
+using Microsoft.eShopOnContainers.BuildingBlocks;
+using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Identity.API.Data;
-using Identity.API.Models;
-using Identity.API.Services;
-using Identity.API.Configuration;
-using IdentityServer4.Services;
-using System.Threading;
-using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
-using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.HealthChecks;
-using Identity.API.Certificate;
-using DataProtectionExtensions;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
-using IdentityServer4.Test;
-using IdentityServer4.EntityFramework.DbContexts;
-using IdentityServer4.EntityFramework.Mappers;
+using System.Threading.Tasks;
namespace eShopOnContainers.Identity
{
@@ -39,7 +37,7 @@ namespace eShopOnContainers.Identity
if (env.IsDevelopment())
{
// For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
- //builder.AddUserSecrets();
+ builder.AddUserSecrets();
}
builder.AddEnvironmentVariables();
@@ -63,10 +61,14 @@ namespace eShopOnContainers.Identity
services.AddMvc();
- services.AddDataProtection(opts =>
+ if (Configuration.GetValue("IsClusterEnv") == bool.TrueString)
{
- opts.ApplicationDiscriminator = "eshop.identity";
- });//.PersistKeysToRedis("basket.data");
+ services.AddDataProtection(opts =>
+ {
+ opts.ApplicationDiscriminator = "eshop.identity";
+ })
+ .PersistKeysToRedis(Configuration["DPConnectionString"]);
+ }
services.AddHealthChecks(checks =>
{
@@ -138,15 +140,15 @@ namespace eShopOnContainers.Identity
template: "{controller=Home}/{action=Index}/{id?}");
});
- InitializeDatabase(app);
+ // Store idsrv grant config into db
+ InitializeGrantStoreAndConfiguration(app).Wait();
//Seed Data
var hasher = new PasswordHasher();
- new ApplicationContextSeed(hasher).SeedAsync(app, loggerFactory)
- .Wait();
+ new ApplicationContextSeed(hasher).SeedAsync(app, loggerFactory).Wait();
}
- private void InitializeDatabase(IApplicationBuilder app)
+ private async Task InitializeGrantStoreAndConfiguration(IApplicationBuilder app)
{
//callbacks urls from config:
Dictionary clientUrls = new Dictionary();
@@ -164,27 +166,27 @@ namespace eShopOnContainers.Identity
{
foreach (var client in Config.GetClients(clientUrls))
{
- context.Clients.Add(client.ToEntity());
+ await context.Clients.AddAsync(client.ToEntity());
}
- context.SaveChanges();
+ await context.SaveChangesAsync();
}
if (!context.IdentityResources.Any())
{
foreach (var resource in Config.GetResources())
{
- context.IdentityResources.Add(resource.ToEntity());
+ await context.IdentityResources.AddAsync(resource.ToEntity());
}
- context.SaveChanges();
+ await context.SaveChangesAsync();
}
if (!context.ApiResources.Any())
{
foreach (var api in Config.GetApis())
{
- context.ApiResources.Add(api.ToEntity());
+ await context.ApiResources.AddAsync(api.ToEntity());
}
- context.SaveChanges();
+ await context.SaveChangesAsync();
}
}
}
diff --git a/src/Services/Identity/Identity.API/appsettings.json b/src/Services/Identity/Identity.API/appsettings.json
index c188cb721..557bac0c3 100644
--- a/src/Services/Identity/Identity.API/appsettings.json
+++ b/src/Services/Identity/Identity.API/appsettings.json
@@ -2,9 +2,10 @@
"ConnectionStrings": {
"DefaultConnection": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.IdentityDb;User Id=sa;Password=Pass@word;"
},
+ "IsClusterEnv": "False",
"MvcClient": "http://localhost:5100",
"SpaClient": "http://localhost:5104",
- "XamarinCallback": "http://localhost:5105/xamarincallback",
+ "XamarinCallback": "http://localhost:5105/xamarincallback",
"Logging": {
"IncludeScopes": false,
"LogLevel": {
diff --git a/src/Web/WebMVC/Controllers/AccountController.cs b/src/Web/WebMVC/Controllers/AccountController.cs
index 531c6f547..a38207d58 100644
--- a/src/Web/WebMVC/Controllers/AccountController.cs
+++ b/src/Web/WebMVC/Controllers/AccountController.cs
@@ -24,8 +24,6 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
var user = User as ClaimsPrincipal;
var token = await HttpContext.Authentication.GetTokenAsync("access_token");
- //TODO - Not retrieving AccessToken yet
- //var token = user.FindFirst("access_token");
if (token != null)
{
ViewData["access_token"] = token;
diff --git a/src/Web/WebMVC/Startup.cs b/src/Web/WebMVC/Startup.cs
index 1a6fc240b..f119e1d8e 100644
--- a/src/Web/WebMVC/Startup.cs
+++ b/src/Web/WebMVC/Startup.cs
@@ -1,22 +1,17 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.eShopOnContainers.BuildingBlocks;
+using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
+using Microsoft.eShopOnContainers.WebMVC.Infrastructure;
+using Microsoft.eShopOnContainers.WebMVC.Services;
+using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.HealthChecks;
using Microsoft.Extensions.Logging;
-using Microsoft.eShopOnContainers.WebMVC.ViewModels;
-using Microsoft.eShopOnContainers.WebMVC.Services;
+using System;
using System.IdentityModel.Tokens.Jwt;
-using Microsoft.IdentityModel.Tokens;
-using Microsoft.AspNetCore.Http;
-using System.Threading;
-using Microsoft.Extensions.Options;
-using Microsoft.Extensions.HealthChecks;
-using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
-using Microsoft.eShopOnContainers.WebMVC.Infrastructure;
namespace Microsoft.eShopOnContainers.WebMVC
{
@@ -46,12 +41,15 @@ namespace Microsoft.eShopOnContainers.WebMVC
{
services.AddMvc();
- services.AddDataProtection(opts =>
+ if (Configuration.GetValue("IsClusterEnv") == bool.TrueString)
{
- opts.ApplicationDiscriminator = "eshop.webmvc";
- });
+ services.AddDataProtection(opts =>
+ {
+ opts.ApplicationDiscriminator = "eshop.webmvc";
+ })
+ .PersistKeysToRedis(Configuration["DPConnectionString"]);
+ }
-
services.Configure(Configuration);
services.AddHealthChecks(checks =>
diff --git a/src/Web/WebMVC/WebMVC.csproj b/src/Web/WebMVC/WebMVC.csproj
index 9686de6f0..a9507394c 100644
--- a/src/Web/WebMVC/WebMVC.csproj
+++ b/src/Web/WebMVC/WebMVC.csproj
@@ -54,6 +54,7 @@
+
diff --git a/src/Web/WebMVC/appsettings.json b/src/Web/WebMVC/appsettings.json
index 796bc972f..4cb9ec7b8 100644
--- a/src/Web/WebMVC/appsettings.json
+++ b/src/Web/WebMVC/appsettings.json
@@ -4,6 +4,7 @@
"BasketUrl": "http://localhost:5103",
"IdentityUrl": "http://localhost:5105",
"CallBackUrl": "http://localhost:5100/",
+ "IsClusterEnv": "True",
"UseResilientHttp": "True",
"Logging": {
"IncludeScopes": false,
diff --git a/src/Web/WebMVC/wwwroot/css/site.min.css b/src/Web/WebMVC/wwwroot/css/site.min.css
index 2a4a53b47..4d03fa783 100644
--- a/src/Web/WebMVC/wwwroot/css/site.min.css
+++ b/src/Web/WebMVC/wwwroot/css/site.min.css
@@ -1 +1 @@
-.esh-app-footer{background-color:#000;border-top:1px solid #eee;margin-top:2.5rem;padding-bottom:2.5rem;padding-top:2.5rem;width:100%}.esh-app-footer-brand{height:50px;width:230px}.esh-app-footer-text{color:#83d01b;line-height:50px;text-align:right;width:100%}@font-face{font-family:Montserrat;font-weight:400;src:url("../fonts/Montserrat-Regular.eot?") format("eot"),url("../fonts/Montserrat-Regular.woff") format("woff"),url("../fonts/Montserrat-Regular.ttf") format("truetype"),url("../fonts/Montserrat-Regular.svg#Montserrat") format("svg")}@font-face{font-family:Montserrat;font-weight:700;src:url("../fonts/Montserrat-Bold.eot?") format("eot"),url("../fonts/Montserrat-Bold.woff") format("woff"),url("../fonts/Montserrat-Bold.ttf") format("truetype"),url("../fonts/Montserrat-Bold.svg#Montserrat") format("svg")}html,body{font-family:Montserrat,sans-serif;font-size:16px;font-weight:400;z-index:10}*,*::after,*::before{box-sizing:border-box}.preloading{color:#00a69c;display:block;font-size:1.5rem;left:50%;position:fixed;top:50%;transform:translate(-50%,-50%)}select::-ms-expand{display:none}@media screen and (min-width:992px){.form-input{max-width:360px;width:360px}}.form-input{border-radius:0;height:45px;padding:10px}.form-input-small{max-width:100px !important}.form-input-medium{width:150px !important}.alert{padding-left:0}.alert-danger{background-color:transparent;border:0;color:#fb0d0d;font-size:12px}a,a:active,a:hover,a:visited{color:#000;text-decoration:none;transition:color .35s}a:hover,a:active{color:#75b918;transition:color .35s}.esh-pager-wrapper{padding-top:1rem;text-align:center}.esh-pager-item{margin:0 5vw}.esh-pager-item--navigable{display:inline-block;cursor:pointer}.esh-pager-item--navigable.is-disabled{opacity:0;pointer-events:none}.esh-pager-item--navigable:hover{color:#83d01b}@media screen and (max-width:1280px){.esh-pager-item{font-size:.85rem}}@media screen and (max-width:1024px){.esh-pager-item{margin:0 4vw}}.esh-identity{line-height:3rem;position:relative;text-align:right}.esh-identity-section{display:inline-block;width:100%}.esh-identity-name{display:inline-block}.esh-identity-name--upper{text-transform:uppercase}@media screen and (max-width:768px){.esh-identity-name{font-size:.85rem}}.esh-identity-image{display:inline-block}.esh-identity-drop{background:#fff;height:0;min-width:14rem;right:0;overflow:hidden;padding:.5rem;position:absolute;top:2.5rem;transition:height .35s}.esh-identity:hover .esh-identity-drop{border:1px solid #eee;height:7rem;transition:height .35s}.esh-identity-item{cursor:pointer;display:block;transition:color .35s}.esh-identity-item:hover{color:#75b918;transition:color .35s}.esh-header{background-color:#00a69c;height:4rem}.esh-header-back{color:rgba(255,255,255,.5) !important;line-height:4rem;text-transform:uppercase;text-decoration:none;transition:color .35s}.esh-header-back:hover{color:#fff !important;transition:color .35s}.esh-orders{min-height:80vh;overflow-x:hidden}.esh-orders-header{background-color:#00a69c;height:4rem}.esh-orders-back{color:rgba(255,255,255,.4);line-height:4rem;text-transform:uppercase;text-decoration:none;transition:color .35s}.esh-orders-back:hover{color:#fff;transition:color .35s}.esh-orders-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders-title{text-transform:uppercase}.esh-orders-items{height:2rem;line-height:2rem;position:relative}.esh-orders-items:nth-of-type(2n+1):before{background-color:#eef;content:'';height:100%;left:0;margin-left:-100vw;position:absolute;top:0;width:200vw;z-index:-1}.esh-orders-item{font-weight:300}.esh-orders-item--hover{opacity:0;pointer-events:none}.esh-orders-items:hover .esh-orders-item--hover{opacity:1;pointer-events:all}.esh-orders-link{color:#83d01b;text-decoration:none;transition:color .35s}.esh-orders-link:hover{color:#75b918;transition:color .35s}.esh-orders_new{min-height:80vh}.esh-orders_new-header{background-color:#00a69c;height:4rem}.esh-orders_new-back{color:rgba(255,255,255,.4);line-height:4rem;text-decoration:none;text-transform:uppercase;transition:color .35s}.esh-orders_new-back:hover{color:#fff;transition:color .35s}.esh-orders_new-section{padding:1rem 0}.esh-orders_new-section--right{text-align:right}.esh-orders_new-placeOrder{background-color:#83d01b;border:0;border-radius:0;color:#fff;display:inline-block;font-size:1rem;font-weight:400;margin-top:1rem;padding:1rem 1.5rem;text-align:center;text-transform:uppercase;transition:all .35s}.esh-orders_new-placeOrder:hover{background-color:#4a760f;transition:all .35s}.esh-orders_new-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders_new-title{font-size:1.25rem;text-transform:uppercase}.esh-orders_new-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-orders_new-items--border:last-of-type{border-color:transparent}.esh-orders_new-item{font-size:1rem;font-weight:300}.esh-orders_new-item--middle{line-height:8rem}@media screen and (max-width:768px){.esh-orders_new-item--middle{line-height:1rem}}.esh-orders_new-item--mark{color:#83d01b}.esh-orders_new-image{height:8rem}.esh-orders_detail{min-height:80vh}.esh-orders_detail-section{padding:1rem 0}.esh-orders_detail-section--right{text-align:right}.esh-orders_detail-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders_detail-title{text-transform:uppercase}.esh-orders_detail-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-orders_detail-items--border:last-of-type{border-color:transparent}.esh-orders_detail-item{font-size:1rem;font-weight:300}.esh-orders_detail-item--middle{line-height:8rem}@media screen and (max-width:768px){.esh-orders_detail-item--middle{line-height:1rem}}.esh-orders_detail-item--mark{color:#83d01b}.esh-orders_detail-image{height:8rem}.esh-catalog-hero{background-image:url("../images/main_banner.png");background-size:cover;height:260px;width:100%}.esh-catalog-title{position:relative;top:74.28571px}.esh-catalog-filters{background-color:#00a69c;height:65px}.esh-catalog-filter{background-color:transparent;border-color:#00d9cc;color:#fff;cursor:pointer;margin-right:1rem;margin-top:.5rem;outline-color:#83d01b;padding-bottom:0;padding-left:.5rem;padding-right:.5rem;padding-top:1.5rem;min-width:140px;-webkit-appearance:none}.esh-catalog-filter option{background-color:#00a69c}.esh-catalog-label{display:inline-block;position:relative;z-index:0}.esh-catalog-label::before{color:rgba(255,255,255,.5);content:attr(data-title);font-size:.65rem;margin-top:.65rem;margin-left:.5rem;position:absolute;text-transform:uppercase;z-index:1}.esh-catalog-label::after{background-image:url("../images/arrow-down.png");height:7px;content:'';position:absolute;right:1.5rem;top:2.5rem;width:10px;z-index:1}.esh-catalog-send{background-color:#83d01b;color:#fff;cursor:pointer;font-size:1rem;transform:translateY(.5rem);padding:.5rem;transition:all .35s}.esh-catalog-send:hover{background-color:#4a760f;transition:all .35s}.esh-catalog-items{margin-top:1rem}.esh-catalog-item{text-align:center;margin-bottom:1.5rem;width:33%;display:inline-block;float:none !important}@media screen and (max-width:1024px){.esh-catalog-item{width:50%}}@media screen and (max-width:768px){.esh-catalog-item{width:100%}}.esh-catalog-thumbnail{max-width:370px;width:100%}.esh-catalog-button{background-color:#83d01b;border:none;color:#fff;cursor:pointer;font-size:1rem;height:3rem;margin-top:1rem;transition:all .35s;width:80%}.esh-catalog-button.is-disabled{opacity:.5;pointer-events:none}.esh-catalog-button:hover{background-color:#4a760f;transition:all .35s}.esh-catalog-name{font-size:1rem;font-weight:300;margin-top:.5rem;text-align:center;text-transform:uppercase}.esh-catalog-price{text-align:center;font-weight:900;font-size:28px}.esh-catalog-price::before{content:'$'}.esh-basket{min-height:80vh}.esh-basket-titles{padding-bottom:1rem;padding-top:2rem}.esh-basket-titles--clean{padding-bottom:0;padding-top:0}.esh-basket-title{text-transform:uppercase}.esh-basket-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-basket-items--border:last-of-type{border-color:transparent}.esh-basket-item{font-size:1rem;font-weight:300}.esh-basket-item--middle{line-height:8rem}@media screen and (max-width:1024px){.esh-basket-item--middle{line-height:1rem}}.esh-basket-item--mark{color:#00a69c}.esh-basket-image{height:8rem}.esh-basket-input{line-height:1rem;width:100%}.esh-basket-checkout{border:none;border-radius:0;background-color:#83d01b;color:#fff;display:inline-block;font-size:1rem;font-weight:400;margin-top:1rem;padding:1rem 1.5rem;text-align:center;text-transform:uppercase;transition:all .35s}.esh-basket-checkout:hover{background-color:#4a760f;transition:all .35s}.esh-basket-margin12{margin-left:12px}.esh-basketstatus{cursor:pointer;display:inline-block;float:right;position:relative;transition:all .35s}.esh-basketstatus.is-disabled{opacity:.5;pointer-events:none}.esh-basketstatus-image{height:36px;margin-top:.5rem}.esh-basketstatus-badge{background-color:#83d01b;border-radius:50%;color:#fff;display:block;height:1.5rem;left:50%;position:absolute;text-align:center;top:0;transform:translateX(-38%);transition:all .35s;width:1.5rem}.esh-basketstatus:hover .esh-basketstatus-badge{background-color:transparent;color:#75b918;transition:all .35s}
\ No newline at end of file
+.esh-app-footer{background-color:#000;border-top:1px solid #eee;margin-top:2.5rem;padding-bottom:2.5rem;padding-top:2.5rem;width:100%}.esh-app-footer-brand{height:50px;width:230px}.esh-app-footer-text{color:#83d01b;line-height:50px;text-align:right;width:100%}@font-face{font-family:Montserrat;font-weight:400;src:url("../fonts/Montserrat-Regular.eot?") format("eot"),url("../fonts/Montserrat-Regular.woff") format("woff"),url("../fonts/Montserrat-Regular.ttf") format("truetype"),url("../fonts/Montserrat-Regular.svg#Montserrat") format("svg")}@font-face{font-family:Montserrat;font-weight:700;src:url("../fonts/Montserrat-Bold.eot?") format("eot"),url("../fonts/Montserrat-Bold.woff") format("woff"),url("../fonts/Montserrat-Bold.ttf") format("truetype"),url("../fonts/Montserrat-Bold.svg#Montserrat") format("svg")}html,body{font-family:Montserrat,sans-serif;font-size:16px;font-weight:400;z-index:10}*,*::after,*::before{box-sizing:border-box}.preloading{color:#00a69c;display:block;font-size:1.5rem;left:50%;position:fixed;top:50%;transform:translate(-50%,-50%)}select::-ms-expand{display:none}@media screen and (min-width:992px){.form-input{max-width:360px;width:360px}}.form-input{border-radius:0;height:45px;padding:10px}.form-input-small{max-width:100px !important}.form-input-medium{width:150px !important}.alert{padding-left:0}.alert-danger{background-color:transparent;border:0;color:#fb0d0d;font-size:12px}a,a:active,a:hover,a:visited{color:#000;text-decoration:none;transition:color .35s}a:hover,a:active{color:#75b918;transition:color .35s}.esh-basket{min-height:80vh}.esh-basket-titles{padding-bottom:1rem;padding-top:2rem}.esh-basket-titles--clean{padding-bottom:0;padding-top:0}.esh-basket-title{text-transform:uppercase}.esh-basket-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-basket-items--border:last-of-type{border-color:transparent}.esh-basket-item{font-size:1rem;font-weight:300}.esh-basket-item--middle{line-height:8rem}@media screen and (max-width:1024px){.esh-basket-item--middle{line-height:1rem}}.esh-basket-item--mark{color:#00a69c}.esh-basket-image{height:8rem}.esh-basket-input{line-height:1rem;width:100%}.esh-basket-checkout{border:none;border-radius:0;background-color:#83d01b;color:#fff;display:inline-block;font-size:1rem;font-weight:400;margin-top:1rem;padding:1rem 1.5rem;text-align:center;text-transform:uppercase;transition:all .35s}.esh-basket-checkout:hover{background-color:#4a760f;transition:all .35s}.esh-basket-margin12{margin-left:12px}.esh-basketstatus{cursor:pointer;display:inline-block;float:right;position:relative;transition:all .35s}.esh-basketstatus.is-disabled{opacity:.5;pointer-events:none}.esh-basketstatus-image{height:36px;margin-top:.5rem}.esh-basketstatus-badge{background-color:#83d01b;border-radius:50%;color:#fff;display:block;height:1.5rem;left:50%;position:absolute;text-align:center;top:0;transform:translateX(-38%);transition:all .35s;width:1.5rem}.esh-basketstatus:hover .esh-basketstatus-badge{background-color:transparent;color:#75b918;transition:all .35s}.esh-catalog-hero{background-image:url("../images/main_banner.png");background-size:cover;height:260px;width:100%}.esh-catalog-title{position:relative;top:74.28571px}.esh-catalog-filters{background-color:#00a69c;height:65px}.esh-catalog-filter{background-color:transparent;border-color:#00d9cc;color:#fff;cursor:pointer;margin-right:1rem;margin-top:.5rem;outline-color:#83d01b;padding-bottom:0;padding-left:.5rem;padding-right:.5rem;padding-top:1.5rem;min-width:140px;-webkit-appearance:none}.esh-catalog-filter option{background-color:#00a69c}.esh-catalog-label{display:inline-block;position:relative;z-index:0}.esh-catalog-label::before{color:rgba(255,255,255,.5);content:attr(data-title);font-size:.65rem;margin-top:.65rem;margin-left:.5rem;position:absolute;text-transform:uppercase;z-index:1}.esh-catalog-label::after{background-image:url("../images/arrow-down.png");height:7px;content:'';position:absolute;right:1.5rem;top:2.5rem;width:10px;z-index:1}.esh-catalog-send{background-color:#83d01b;color:#fff;cursor:pointer;font-size:1rem;transform:translateY(.5rem);padding:.5rem;transition:all .35s}.esh-catalog-send:hover{background-color:#4a760f;transition:all .35s}.esh-catalog-items{margin-top:1rem}.esh-catalog-item{text-align:center;margin-bottom:1.5rem;width:33%;display:inline-block;float:none !important}@media screen and (max-width:1024px){.esh-catalog-item{width:50%}}@media screen and (max-width:768px){.esh-catalog-item{width:100%}}.esh-catalog-thumbnail{max-width:370px;width:100%}.esh-catalog-button{background-color:#83d01b;border:none;color:#fff;cursor:pointer;font-size:1rem;height:3rem;margin-top:1rem;transition:all .35s;width:80%}.esh-catalog-button.is-disabled{opacity:.5;pointer-events:none}.esh-catalog-button:hover{background-color:#4a760f;transition:all .35s}.esh-catalog-name{font-size:1rem;font-weight:300;margin-top:.5rem;text-align:center;text-transform:uppercase}.esh-catalog-price{text-align:center;font-weight:900;font-size:28px}.esh-catalog-price::before{content:'$'}.esh-orders{min-height:80vh;overflow-x:hidden}.esh-orders-header{background-color:#00a69c;height:4rem}.esh-orders-back{color:rgba(255,255,255,.4);line-height:4rem;text-transform:uppercase;text-decoration:none;transition:color .35s}.esh-orders-back:hover{color:#fff;transition:color .35s}.esh-orders-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders-title{text-transform:uppercase}.esh-orders-items{height:2rem;line-height:2rem;position:relative}.esh-orders-items:nth-of-type(2n+1):before{background-color:#eef;content:'';height:100%;left:0;margin-left:-100vw;position:absolute;top:0;width:200vw;z-index:-1}.esh-orders-item{font-weight:300}.esh-orders-item--hover{opacity:0;pointer-events:none}.esh-orders-items:hover .esh-orders-item--hover{opacity:1;pointer-events:all}.esh-orders-link{color:#83d01b;text-decoration:none;transition:color .35s}.esh-orders-link:hover{color:#75b918;transition:color .35s}.esh-orders_detail{min-height:80vh}.esh-orders_detail-section{padding:1rem 0}.esh-orders_detail-section--right{text-align:right}.esh-orders_detail-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders_detail-title{text-transform:uppercase}.esh-orders_detail-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-orders_detail-items--border:last-of-type{border-color:transparent}.esh-orders_detail-item{font-size:1rem;font-weight:300}.esh-orders_detail-item--middle{line-height:8rem}@media screen and (max-width:768px){.esh-orders_detail-item--middle{line-height:1rem}}.esh-orders_detail-item--mark{color:#83d01b}.esh-orders_detail-image{height:8rem}.esh-orders_new{min-height:80vh}.esh-orders_new-header{background-color:#00a69c;height:4rem}.esh-orders_new-back{color:rgba(255,255,255,.4);line-height:4rem;text-decoration:none;text-transform:uppercase;transition:color .35s}.esh-orders_new-back:hover{color:#fff;transition:color .35s}.esh-orders_new-section{padding:1rem 0}.esh-orders_new-section--right{text-align:right}.esh-orders_new-placeOrder{background-color:#83d01b;border:0;border-radius:0;color:#fff;display:inline-block;font-size:1rem;font-weight:400;margin-top:1rem;padding:1rem 1.5rem;text-align:center;text-transform:uppercase;transition:all .35s}.esh-orders_new-placeOrder:hover{background-color:#4a760f;transition:all .35s}.esh-orders_new-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders_new-title{font-size:1.25rem;text-transform:uppercase}.esh-orders_new-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-orders_new-items--border:last-of-type{border-color:transparent}.esh-orders_new-item{font-size:1rem;font-weight:300}.esh-orders_new-item--middle{line-height:8rem}@media screen and (max-width:768px){.esh-orders_new-item--middle{line-height:1rem}}.esh-orders_new-item--mark{color:#83d01b}.esh-orders_new-image{height:8rem}.esh-header{background-color:#00a69c;height:4rem}.esh-header-back{color:rgba(255,255,255,.5) !important;line-height:4rem;text-transform:uppercase;text-decoration:none;transition:color .35s}.esh-header-back:hover{color:#fff !important;transition:color .35s}.esh-identity{line-height:3rem;position:relative;text-align:right}.esh-identity-section{display:inline-block;width:100%}.esh-identity-name{display:inline-block}.esh-identity-name--upper{text-transform:uppercase}@media screen and (max-width:768px){.esh-identity-name{font-size:.85rem}}.esh-identity-image{display:inline-block}.esh-identity-drop{background:#fff;height:0;min-width:14rem;right:0;overflow:hidden;padding:.5rem;position:absolute;top:2.5rem;transition:height .35s}.esh-identity:hover .esh-identity-drop{border:1px solid #eee;height:7rem;transition:height .35s}.esh-identity-item{cursor:pointer;display:block;transition:color .35s}.esh-identity-item:hover{color:#75b918;transition:color .35s}.esh-pager-wrapper{padding-top:1rem;text-align:center}.esh-pager-item{margin:0 5vw}.esh-pager-item--navigable{display:inline-block;cursor:pointer}.esh-pager-item--navigable.is-disabled{opacity:0;pointer-events:none}.esh-pager-item--navigable:hover{color:#83d01b}@media screen and (max-width:1280px){.esh-pager-item{font-size:.85rem}}@media screen and (max-width:1024px){.esh-pager-item{margin:0 4vw}}
\ No newline at end of file
diff --git a/src/Web/WebSPA/Startup.cs b/src/Web/WebSPA/Startup.cs
index 53476266a..d9e9d093b 100644
--- a/src/Web/WebSPA/Startup.cs
+++ b/src/Web/WebSPA/Startup.cs
@@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.HealthChecks;
using Newtonsoft.Json.Serialization;
using eShopOnContainers.WebSPA;
+using Microsoft.eShopOnContainers.BuildingBlocks;
namespace eShopConContainers.WebSPA
{
@@ -59,10 +60,14 @@ namespace eShopConContainers.WebSPA
services.Configure(Configuration);
- services.AddDataProtection(opts =>
+ if (Configuration.GetValue("IsClusterEnv") == bool.TrueString)
{
- opts.ApplicationDiscriminator = "eshop.webspa";
- });
+ services.AddDataProtection(opts =>
+ {
+ opts.ApplicationDiscriminator = "eshop.webspa";
+ })
+ .PersistKeysToRedis(Configuration["DPConnectionString"]);
+ }
services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
diff --git a/src/Web/WebSPA/WebSPA.csproj b/src/Web/WebSPA/WebSPA.csproj
index 6f8678a74..90f2905dd 100644
--- a/src/Web/WebSPA/WebSPA.csproj
+++ b/src/Web/WebSPA/WebSPA.csproj
@@ -79,6 +79,7 @@
-->
+