Use Razor to present view
This commit is contained in:
parent
961ed87266
commit
29eb91c3b1
|
@ -3,6 +3,7 @@ using BlueWest.Data.Auth;
|
|||
using BlueWest.Data.Auth.Context.Users;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -35,13 +36,13 @@ namespace BlueWest.WebApi.Controllers;
|
|||
/// <summary>
|
||||
/// Signup user
|
||||
/// </summary>
|
||||
/// <param name="registerViewModel"></param>
|
||||
/// <param name="registerRequest"></param>
|
||||
/// <returns></returns>
|
||||
[AllowAnonymous]
|
||||
[HttpPost("register")]
|
||||
public async Task<ActionResult<IdentityResult>> SignupUserAsync(RegisterViewModel registerViewModel)
|
||||
public async Task<ActionResult<IdentityResult>> SignupUserAsync(RegisterRequest registerRequest)
|
||||
{
|
||||
return await _authManager.CreateUserAsync(registerViewModel);
|
||||
return await _authManager.CreateUserAsync(registerRequest);
|
||||
}
|
||||
|
||||
|
||||
|
@ -55,7 +56,7 @@ namespace BlueWest.WebApi.Controllers;
|
|||
[HttpPost("login")]
|
||||
public async Task<ActionResult<IdentityResult>> GetSessionToken(LoginRequest loginViewModel)
|
||||
{
|
||||
var (success, sessionToken, _) = await _authManager.GetSessionTokenIdByLoginRequest(loginViewModel);
|
||||
var (success, sessionToken, _) = await _authManager.GetSessionTokenIdByLoginRequest(loginViewModel, JwtBearerDefaults.AuthenticationScheme);
|
||||
|
||||
if (success)
|
||||
{
|
||||
|
|
|
@ -54,7 +54,14 @@ namespace BlueWest.WebApi
|
|||
.AllowCredentials();
|
||||
});
|
||||
});
|
||||
|
||||
services.AddDistributedMemoryCache();
|
||||
|
||||
services.AddSession(options =>
|
||||
{
|
||||
options.Cookie.Name = ".BlueWest.Session";
|
||||
options.Cookie.Domain = SessionConstants.CookieDomain;
|
||||
options.Cookie.HttpOnly = true;
|
||||
});
|
||||
services
|
||||
.AddResponseCaching()
|
||||
.AddControllers(options =>
|
||||
|
@ -71,12 +78,8 @@ namespace BlueWest.WebApi
|
|||
{
|
||||
builder.AddSimpleConsole();
|
||||
});
|
||||
|
||||
services.AddSession(options =>
|
||||
{
|
||||
options.Cookie.Domain = SessionConstants.CookieDomain;
|
||||
options.Cookie.HttpOnly = true;
|
||||
});
|
||||
|
||||
|
||||
services
|
||||
.AddSwaggerGen(options =>
|
||||
{
|
||||
|
@ -136,20 +139,15 @@ namespace BlueWest.WebApi
|
|||
);
|
||||
*/
|
||||
|
||||
|
||||
IConfigurationRoot configuration = new ConfigurationBuilder()
|
||||
.AddJsonFile("config.json")
|
||||
.Build();
|
||||
|
||||
|
||||
services
|
||||
.AddSingleton<EventManager>();
|
||||
|
||||
|
||||
services.AddAuthServerServices( _configuration, _environment, configuration);
|
||||
services.AddAuthServerServices( _configuration, _environment);
|
||||
services.AddScoped<ExchangeInterface>();
|
||||
|
||||
services.PrepareMySqlDatabasePool(_configuration, _environment, configuration);
|
||||
services.PrepareMySqlDatabasePool(_configuration, _environment);
|
||||
|
||||
|
||||
|
||||
|
@ -180,6 +178,7 @@ namespace BlueWest.WebApi
|
|||
|
||||
app.UseRouting();
|
||||
app.UseCors(MyAllowSpecificOrigins);
|
||||
app.UseSession();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseEndpoints(endpoints =>
|
||||
|
|
|
@ -32,23 +32,23 @@ namespace BlueWest.WebApi
|
|||
{
|
||||
private static MySqlServerVersion GetMySqlServerVersion(int major, int minor, int build) => new (new Version(major, minor, build));
|
||||
|
||||
private static BlueWestConnectionString GetConnectionString(this IConfigurationRoot configurationRoot)
|
||||
private static string GetConnectionString(this IConfiguration configurationRoot, string db)
|
||||
{
|
||||
// Docker / No-Docker
|
||||
var startupMode = configurationRoot["mode"];
|
||||
|
||||
if (startupMode == "docker")
|
||||
{
|
||||
var config = configurationRoot.Get<ConnectionStringDocker>();
|
||||
var config = configurationRoot.GetSection("ConnectionStringDocker")[db];
|
||||
return config;
|
||||
}
|
||||
else
|
||||
{
|
||||
var config = configurationRoot.Get<ConnectionStringNoDocker>();
|
||||
var config = configurationRoot.GetSection("ConnectionStringNoDocker")[db];
|
||||
return config;
|
||||
}
|
||||
|
||||
return null;
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -60,14 +60,13 @@ namespace BlueWest.WebApi
|
|||
private static DbContextOptionsBuilder GetMySqlSettings(
|
||||
this DbContextOptionsBuilder optionsBuilder,
|
||||
IConfiguration configuration,
|
||||
IConfigurationRoot configurationRoot,
|
||||
IWebHostEnvironment environment)
|
||||
{
|
||||
var sqlVersion = GetMySqlServerVersion(8, 0, 11);
|
||||
|
||||
// Docker / No-Docker
|
||||
|
||||
string mySqlConnectionString = configurationRoot.GetConnectionString().MySql;
|
||||
|
||||
var mySqlConnectionString = configuration.GetConnectionString("MySQL");
|
||||
|
||||
if (mySqlConnectionString == string.Empty)
|
||||
{
|
||||
|
@ -106,25 +105,25 @@ namespace BlueWest.WebApi
|
|||
/// <param name="environment"></param>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection PrepareMySqlDatabasePool(this IServiceCollection serviceCollection,
|
||||
IConfiguration configuration, IWebHostEnvironment environment, IConfigurationRoot configurationRoot)
|
||||
IConfiguration configuration, IWebHostEnvironment environment)
|
||||
{
|
||||
return serviceCollection
|
||||
.AddDbContextPool<UserDbContext>(options =>
|
||||
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||
options.GetMySqlSettings(configuration, environment))
|
||||
.AddDbContextPool<CountryDbContext>(options =>
|
||||
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||
options.GetMySqlSettings(configuration, environment))
|
||||
.AddDbContextPool<FinanceDbContext>(options =>
|
||||
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||
options.GetMySqlSettings(configuration, environment))
|
||||
.AddDbContextPool<CompanyDbContext>(options =>
|
||||
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||
options.GetMySqlSettings(configuration, environment))
|
||||
.AddDbContextPool<ApplicationUserDbContext>(options =>
|
||||
options.GetMySqlSettings(configuration, configurationRoot, environment));
|
||||
options.GetMySqlSettings(configuration, environment));
|
||||
}
|
||||
|
||||
internal static IServiceCollection AddAuthServerServices(this IServiceCollection services, IConfiguration configuration , IWebHostEnvironment environment, IConfigurationRoot configurationRoot)
|
||||
internal static IServiceCollection AddAuthServerServices(this IServiceCollection services, IConfiguration configuration , IWebHostEnvironment environment)
|
||||
{
|
||||
|
||||
var connectionString = configurationRoot.GetConnectionString();
|
||||
var connectionString = configuration.GetConnectionString("Redis");
|
||||
|
||||
if (connectionString == null)
|
||||
{
|
||||
|
@ -132,7 +131,7 @@ namespace BlueWest.WebApi
|
|||
}
|
||||
|
||||
services
|
||||
.AddSingleton(new RedisConnectionProvider(connectionString.Redis))
|
||||
.AddSingleton(new RedisConnectionProvider(connectionString))
|
||||
.AddScoped<IJwtTokenHandler, JwtTokenHandler>()
|
||||
.AddScoped<IJwtFactory, JwtFactory>()
|
||||
.AddHostedService<SessionManager>()
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"mode": "no-docker",
|
||||
"database": "mysql"
|
||||
}
|
|
@ -44,4 +44,12 @@
|
|||
<ProjectReference Include="..\BlueWest.Domain\BlueWest.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="appsettings.json">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
namespace BlueWest.Data.Auth;
|
||||
|
||||
public class Class1
|
||||
{
|
||||
}
|
|
@ -1,12 +1,16 @@
|
|||
namespace BlueWest.WebApi.Configuration
|
||||
{
|
||||
public class BlueWestConnectionString
|
||||
public class ConnectionStringDocker
|
||||
{
|
||||
public string Redis { get; set; }
|
||||
public string MySql { get; set; }
|
||||
}
|
||||
public class ConnectionStringDocker : BlueWestConnectionString { }
|
||||
public class ConnectionStringNoDocker : BlueWestConnectionString { }
|
||||
public class ConnectionStringNoDocker
|
||||
{
|
||||
public string Redis { get; set; }
|
||||
public string MySql { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace BlueWest.Data.Auth.Context.Users
|
|||
public string Password { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Uuid { get; set; }
|
||||
public string Uuid { get ; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets Uuid for this login request
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace BlueWest.Data.Auth.Context.Users;
|
|||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class RegisterViewModel
|
||||
public class RegisterRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Email
|
|
@ -5,10 +5,17 @@ namespace BlueWest.Data.Auth
|
|||
public static class SessionConstants
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Max age for the Session
|
||||
/// </summary>
|
||||
public static TimeSpan DefaultSessionMaxAge = TimeSpan.FromHours(24);
|
||||
/// <summary>
|
||||
/// API User policy Key
|
||||
/// </summary>
|
||||
public const string ApiNamePolicy = "ApiUser";
|
||||
public const string SessionTokenHeaderName = "x-bw2-auth";
|
||||
public const string CookieDomain = "http://localhost:5173";
|
||||
public const string CookieDomain = "https://localhost:7022";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,12 +3,16 @@ using System.Security.Claims;
|
|||
using BlueWest.Cryptography;
|
||||
using BlueWest.Data.Application;
|
||||
using BlueWest.Data.Application.Users;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using static BlueWest.Data.Auth.Context.Users.AuthConsts;
|
||||
|
||||
namespace BlueWest.Data.Auth.Context.Users
|
||||
{
|
||||
/// <summary>
|
||||
/// Authentication Manager for the Application Users
|
||||
/// </summary>
|
||||
public class AuthManager : IAuthManager
|
||||
{
|
||||
private readonly ApplicationUserManager _userManager;
|
||||
|
@ -48,7 +52,7 @@ namespace BlueWest.Data.Auth.Context.Users
|
|||
return _hasher.CreateHash(uuid, BaseCryptoItem.HashAlgorithm.SHA2_512);
|
||||
}
|
||||
|
||||
private SessionToken GetNewSessionToken(LoginRequest loginRequest, ApplicationUser user, string token)
|
||||
private SessionToken GetNewSessionToken(LoginRequest loginRequest, ApplicationUser user)
|
||||
{
|
||||
long timeNow = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
|
||||
|
@ -58,8 +62,7 @@ namespace BlueWest.Data.Auth.Context.Users
|
|||
UserId = user.Id,
|
||||
CreatedDate = timeNow,
|
||||
IsValid = true,
|
||||
ValidFor = SessionConstants.DefaultSessionMaxAge.Milliseconds,
|
||||
AccessToken = token
|
||||
ValidFor = SessionConstants.DefaultSessionMaxAge.Milliseconds
|
||||
};
|
||||
|
||||
return newToken;
|
||||
|
@ -77,7 +80,7 @@ namespace BlueWest.Data.Auth.Context.Users
|
|||
return token.IsValid;
|
||||
}
|
||||
|
||||
public async Task<(bool, SessionTokenUnique, ClaimsIdentity)> GetSessionTokenIdByLoginRequest(LoginRequest loginRequest)
|
||||
public async Task<(bool, SessionTokenUnique, ClaimsIdentity)> GetSessionTokenIdByLoginRequest(LoginRequest loginRequest, string authenticationType = JwtBearerDefaults.AuthenticationScheme)
|
||||
{
|
||||
var user = await _userManager.FindByEmailAsync(loginRequest.Email);
|
||||
|
||||
|
@ -85,24 +88,55 @@ namespace BlueWest.Data.Auth.Context.Users
|
|||
|
||||
if (!await _userManager.CheckPasswordAsync(user, loginRequest.Password)) return NegativeToken;
|
||||
|
||||
var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);
|
||||
var identity = new ClaimsIdentity(authenticationType);
|
||||
identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));
|
||||
identity.AddClaim(new Claim(ClaimTypes.MobilePhone, user.PhoneNumber));
|
||||
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
||||
|
||||
|
||||
var sessionToken = await GetSessionToken(loginRequest);
|
||||
|
||||
if (sessionToken == null || !SessionTokenIsValid(sessionToken))
|
||||
{
|
||||
var (success, bearerToken) = await GenerateBearerToken(identity, user);
|
||||
var newSessionToken = GetNewSessionToken(loginRequest, user, bearerToken);
|
||||
var newSessionToken = GetNewSessionToken(loginRequest, user);
|
||||
await _sessionCache.AddSessionToken(newSessionToken);
|
||||
var tokenUnique = new SessionTokenUnique(newSessionToken);
|
||||
|
||||
return OkAuth(tokenUnique, identity, success);
|
||||
}
|
||||
|
||||
var response = new SessionTokenUnique(sessionToken);
|
||||
return OkAuth(response, identity);
|
||||
}
|
||||
|
||||
public async Task<(bool, SessionTokenUnique, ClaimsIdentity)> GetSessionTokenIdByLoginRequestViaCookie(LoginRequest loginRequest, string authenticationType = CookieAuthenticationDefaults.AuthenticationScheme)
|
||||
{
|
||||
var user = await _userManager.FindByEmailAsync(loginRequest.Email);
|
||||
|
||||
if (user == null) return NegativeToken;
|
||||
|
||||
if (!await _userManager.CheckPasswordAsync(user, loginRequest.Password)) return NegativeToken;
|
||||
|
||||
var identity = new ClaimsIdentity(authenticationType);
|
||||
identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));
|
||||
identity.AddClaim(new Claim(ClaimTypes.MobilePhone, user.PhoneNumber));
|
||||
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
||||
|
||||
|
||||
var sessionToken = await GetSessionToken(loginRequest);
|
||||
|
||||
if (sessionToken == null || !SessionTokenIsValid(sessionToken))
|
||||
{
|
||||
var newSessionToken = GetNewSessionToken(loginRequest, user);
|
||||
await _sessionCache.AddSessionToken(newSessionToken);
|
||||
var tokenUnique = new SessionTokenUnique(newSessionToken);
|
||||
return OkAuth(tokenUnique, identity);
|
||||
}
|
||||
|
||||
var response = new SessionTokenUnique(sessionToken);
|
||||
return OkAuth(response, identity);
|
||||
}
|
||||
|
||||
|
||||
private async Task<(bool, string)> GenerateBearerToken(ClaimsIdentity identity, ApplicationUser user)
|
||||
{
|
||||
|
@ -113,6 +147,12 @@ namespace BlueWest.Data.Auth.Context.Users
|
|||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Verify Password
|
||||
/// </summary>
|
||||
/// <param name="email"></param>
|
||||
/// <param name="password"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> VerifyLoginByEmailAsync(string email, string password)
|
||||
{
|
||||
var user = await _userManager.FindByEmailAsync(email);
|
||||
|
@ -120,7 +160,12 @@ namespace BlueWest.Data.Auth.Context.Users
|
|||
}
|
||||
|
||||
|
||||
public async Task<IdentityResult> CreateUserAsync(RegisterViewModel userSignupDto)
|
||||
/// <summary>
|
||||
/// Create user
|
||||
/// </summary>
|
||||
/// <param name="userSignupDto"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<IdentityResult> CreateUserAsync(RegisterRequest userSignupDto)
|
||||
{
|
||||
userSignupDto.Password = _hasher.CreateHash(userSignupDto.Password, BaseCryptoItem.HashAlgorithm.SHA3_512);;
|
||||
var newUser = userSignupDto.ToUser();
|
||||
|
|
|
@ -14,16 +14,26 @@ public interface IAuthManager
|
|||
/// <summary>
|
||||
/// CreateUserAsync
|
||||
/// </summary>
|
||||
/// <param name="registerViewModel"></param>
|
||||
/// <param name="registerRequest"></param>
|
||||
/// <returns></returns>
|
||||
Task<IdentityResult> CreateUserAsync(RegisterViewModel registerViewModel);
|
||||
Task<IdentityResult> CreateUserAsync(RegisterRequest registerRequest);
|
||||
|
||||
/// <summary>
|
||||
/// Does Login
|
||||
/// </summary>
|
||||
/// <param name="loginRequest"></param>
|
||||
/// <returns></returns>
|
||||
public Task<(bool, SessionTokenUnique, ClaimsIdentity)> GetSessionTokenIdByLoginRequest(LoginRequest loginRequest);
|
||||
public Task<(bool, SessionTokenUnique, ClaimsIdentity)> GetSessionTokenIdByLoginRequest(LoginRequest loginRequest, string authenticationType);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Does Login
|
||||
/// </summary>
|
||||
/// <param name="loginRequest"></param>
|
||||
/// <returns></returns>
|
||||
public Task<(bool, SessionTokenUnique, ClaimsIdentity)> GetSessionTokenIdByLoginRequestViaCookie(LoginRequest loginRequest, string authenticationType);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
using System.Security.Claims;
|
||||
using BlueWest.Data.Application.Users;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
|
@ -6,6 +7,12 @@ namespace BlueWest.Data.Auth.Context.Users
|
|||
{
|
||||
public interface IUserManager
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="principal"></param>
|
||||
/// <returns></returns>
|
||||
Task<ApplicationUser> GetUserAsync(ClaimsPrincipal principal);
|
||||
/// <summary>
|
||||
/// Create user.
|
||||
/// </summary>
|
||||
|
@ -28,8 +35,8 @@ namespace BlueWest.Data.Auth.Context.Users
|
|||
/// <returns></returns>
|
||||
Task<ApplicationUser> FindByEmailAsync(string email);
|
||||
|
||||
|
||||
|
||||
string GetUserId(ClaimsPrincipal principal);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"ConnectionStringDocker": {
|
||||
"MySQL": "server=db;user=blueuser;password=dXjw127124dJ;database=bluedb;",
|
||||
"Redis": "redis://redisinstance:6379"
|
||||
},
|
||||
"ConnectionStringNoDocker": {
|
||||
"MySQL": "server=localhost;user=blueuser;password=dXjw127124dJ;database=bluedb;",
|
||||
"Redis": "redis://localhost:6379"
|
||||
},
|
||||
"AuthSettings": {
|
||||
"SecretKey": "iJWHDmHLpUA283sqsfhqGbMRdRj1PVkH"
|
||||
},
|
||||
"JwtIssuerOptions": {
|
||||
"Issuer": "SomeIssuer",
|
||||
"Audience": "http://localhost:5000"
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<SupportedPlatform Include="browser"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="6.0.3"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,3 +0,0 @@
|
|||
<div class="my-component">
|
||||
This component is defined in the <strong>BlueWest.Razor.Library</strong> library.
|
||||
</div>
|
|
@ -1,6 +0,0 @@
|
|||
.my-component {
|
||||
border: 2px dashed red;
|
||||
padding: 1em;
|
||||
margin: 1em 0;
|
||||
background-image: url('background.png');
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
using Microsoft.JSInterop;
|
||||
|
||||
namespace BlueWest.Razor.Library;
|
||||
|
||||
// This class provides an example of how JavaScript functionality can be wrapped
|
||||
// in a .NET class for easy consumption. The associated JavaScript module is
|
||||
// loaded on demand when first needed.
|
||||
//
|
||||
// This class can be registered as scoped DI service and then injected into Blazor
|
||||
// components for use.
|
||||
|
||||
public class ExampleJsInterop : IAsyncDisposable
|
||||
{
|
||||
private readonly Lazy<Task<IJSObjectReference>> moduleTask;
|
||||
|
||||
public ExampleJsInterop(IJSRuntime jsRuntime)
|
||||
{
|
||||
moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>(
|
||||
"import", "./_content/BlueWest.Razor.Library/exampleJsInterop.js").AsTask());
|
||||
}
|
||||
|
||||
public async ValueTask<string> Prompt(string message)
|
||||
{
|
||||
var module = await moduleTask.Value;
|
||||
return await module.InvokeAsync<string>("showPrompt", message);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (moduleTask.IsValueCreated)
|
||||
{
|
||||
var module = await moduleTask.Value;
|
||||
await module.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
@using Microsoft.AspNetCore.Components.Web
|
Binary file not shown.
Before Width: | Height: | Size: 378 B |
|
@ -1,6 +0,0 @@
|
|||
// This is a JavaScript module that is loaded on demand. It can export any number of
|
||||
// functions, and may import other JavaScript modules if required.
|
||||
|
||||
export function showPrompt(message) {
|
||||
return prompt(message, 'Type anything here');
|
||||
}
|
|
@ -11,8 +11,11 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNet.Mvc" Version="5.2.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.2.0" />
|
||||
<PackageReference Include="Redis.OM" Version="0.2.2" />
|
||||
<PackageReference Include="Redis.OM" Version="0.2.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.8">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BlueWest.Data.Auth\BlueWest.Data.Auth.csproj" />
|
||||
|
@ -81,7 +84,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="Views\Jobs\Index.cshtml" />
|
||||
<AdditionalFiles Include="Views\Data\Banks\Index.cshtml" />
|
||||
<AdditionalFiles Include="Views\Data\Currencies\Index.cshtml" />
|
||||
<AdditionalFiles Include="Views\Account\Index.cshtml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -90,6 +95,12 @@
|
|||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</None>
|
||||
<Content Remove="appsettings.json" />
|
||||
<None Include="appsettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<Content Remove="appsettings.Development.json" />
|
||||
<None Include="appsettings.Development.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -99,7 +110,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Pages" />
|
||||
<Folder Include="wwwroot\static\profile" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
using System.Diagnostics;
|
||||
using System.Web.Mvc;
|
||||
using BlueWest.Data.Auth.Context.Users;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using BlueWest.Views.Models;
|
||||
using BlueWest.Views.Utils;
|
||||
using Controller = Microsoft.AspNetCore.Mvc.Controller;
|
||||
|
||||
namespace BlueWest.Views.Controllers;
|
||||
|
||||
public class AccountController : UserController
|
||||
{
|
||||
private readonly ILogger<AccountController> _logger;
|
||||
|
||||
|
||||
public AccountController(ApplicationUserManager userManager, ILogger<AccountController> logger) : base(userManager, logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
}
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
await OnEveryAction();
|
||||
var user = await GetLoggedInUser();
|
||||
return View(user);
|
||||
}
|
||||
|
||||
public override void OnInitialization()
|
||||
{
|
||||
SetFooterMenu(LayoutCache.AccountRouteRecord.Children);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<IActionResult> Curriculums()
|
||||
{
|
||||
await OnEveryAction();
|
||||
|
||||
return View("Curriculums/Index");
|
||||
}
|
||||
|
||||
|
||||
public async Task<IActionResult> Change()
|
||||
{
|
||||
await OnEveryAction();
|
||||
return View("ChangePassword");
|
||||
}
|
||||
|
||||
|
||||
[Microsoft.AspNetCore.Mvc.Route("[controller]/upload")]
|
||||
public async Task<IActionResult> UploadCurriculum()
|
||||
{
|
||||
await OnEveryAction();
|
||||
return View("Curriculums/Upload");
|
||||
}
|
||||
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public async Task<IActionResult> Error()
|
||||
{
|
||||
return View(new ErrorViewModel {RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier});
|
||||
}
|
||||
|
||||
}
|
|
@ -1,18 +1,96 @@
|
|||
using BlueWest.Views.Utils;
|
||||
using System.Security.Claims;
|
||||
using BlueWest.Data.Auth;
|
||||
using BlueWest.Data.Auth.Context.Users;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Controller = Microsoft.AspNetCore.Mvc.Controller;
|
||||
|
||||
namespace BlueWest.Views.Controllers
|
||||
{
|
||||
[System.Web.Mvc.Route("/login")]
|
||||
|
||||
public class AuthController : Controller
|
||||
public class AuthController : UserController
|
||||
{
|
||||
|
||||
|
||||
private readonly IAuthManager _authManager;
|
||||
|
||||
public AuthController(ApplicationUserManager userManager, ILogger<AuthController> logger, IAuthManager authManager) : base(userManager, logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
_authManager = authManager;
|
||||
}
|
||||
|
||||
public IActionResult Index()
|
||||
{
|
||||
OnEveryAction();
|
||||
return View();
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[Microsoft.AspNetCore.Mvc.ActionName("LoginAction")]
|
||||
public async Task<IActionResult> LoginAction(LoginRequest loginRequest)
|
||||
{
|
||||
var (success, sessionToken, identity) =
|
||||
await _authManager.GetSessionTokenIdByLoginRequestViaCookie(loginRequest,
|
||||
CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
|
||||
|
||||
if (!success) return Redirect(AuthLoginRoute);
|
||||
|
||||
if (success)
|
||||
{
|
||||
await HttpContext.SignInAsync(
|
||||
CookieAuthenticationDefaults.AuthenticationScheme,
|
||||
new ClaimsPrincipal(identity),
|
||||
new AuthenticationProperties
|
||||
{
|
||||
IsPersistent = true,
|
||||
ExpiresUtc = DateTime.UtcNow.Add(SessionConstants.DefaultSessionMaxAge)
|
||||
});
|
||||
|
||||
HttpContext.Session.SetString("hello", "world");
|
||||
|
||||
return Redirect(RootLocation);
|
||||
}
|
||||
|
||||
return Redirect(RootLocation);
|
||||
}
|
||||
|
||||
public IActionResult Login()
|
||||
{
|
||||
this.HandleGlobalization();
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task<IActionResult> Logout()
|
||||
{
|
||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
return Redirect("/");
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<IActionResult> Signup()
|
||||
{
|
||||
await OnEveryAction();
|
||||
return View();
|
||||
}
|
||||
|
||||
|
||||
[Microsoft.AspNetCore.Mvc.ActionName("SignupAction")]
|
||||
public async Task<IActionResult> SignupAction(RegisterRequest registerRequest)
|
||||
{
|
||||
var result = await _authManager.CreateUserAsync(registerRequest);
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return RedirectToAction("Login");
|
||||
}
|
||||
|
||||
return RedirectToAction("Signup");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,17 +1,66 @@
|
|||
using System.Web.Mvc;
|
||||
using BlueWest.Data.Auth.Context.Users;
|
||||
using BlueWest.Views.Utils;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Controller = Microsoft.AspNetCore.Mvc.Controller;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace BlueWest.Views.Controllers
|
||||
{
|
||||
|
||||
public class DataController : Controller
|
||||
[Authorize]
|
||||
|
||||
public class DataController : UserController
|
||||
{
|
||||
public IActionResult Index()
|
||||
|
||||
public DataController(ApplicationUserManager userManager, ILogger<UserController> logger) : base(userManager,
|
||||
logger)
|
||||
{
|
||||
this.HandleGlobalization();
|
||||
ViewData[FooterMenuId] = LayoutCache.DataRoute.Children;
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
await OnEveryAction();
|
||||
return View();
|
||||
}
|
||||
|
||||
[ChildActionOnly]
|
||||
public async Task<IActionResult> Countries()
|
||||
{
|
||||
await OnEveryAction();
|
||||
return View("Countries/Index");
|
||||
}
|
||||
|
||||
[ChildActionOnly]
|
||||
|
||||
public async Task<IActionResult> Companies()
|
||||
{
|
||||
await OnEveryAction();
|
||||
return View("Companies/Index");
|
||||
}
|
||||
|
||||
[ChildActionOnly]
|
||||
|
||||
public async Task<IActionResult> Banks()
|
||||
{
|
||||
await OnEveryAction();
|
||||
return View("Companies/Index");
|
||||
}
|
||||
|
||||
[ChildActionOnly]
|
||||
|
||||
public async Task<IActionResult> Currencies()
|
||||
{
|
||||
await OnEveryAction();
|
||||
return View("Currencies/Index");
|
||||
}
|
||||
|
||||
public override void OnInitialization()
|
||||
{
|
||||
SetFooterMenu(LayoutCache.DataRouteRecord.Children);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,24 +1,38 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using BlueWest.Data.Auth.Context.Users;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using BlueWest.Views.Utils;
|
||||
using Duende.IdentityServer.Extensions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Controller = Microsoft.AspNetCore.Mvc.Controller;
|
||||
|
||||
namespace BlueWest.Views.Controllers;
|
||||
|
||||
[System.Web.Mvc.Route("/")]
|
||||
public class HomeController : Controller
|
||||
[System.Web.Mvc.Authorize]
|
||||
public class HomeController : UserController
|
||||
{
|
||||
private readonly ILogger<HomeController> _logger;
|
||||
|
||||
public HomeController(ILogger<HomeController> logger, IOptions<RequestLocalizationOptions> options)
|
||||
public HomeController(ApplicationUserManager userManager, ILogger<HomeController> logger) : base(userManager, logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IActionResult Index()
|
||||
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
this.HandleGlobalization();
|
||||
await OnEveryAction();
|
||||
|
||||
if (!User.IsAuthenticated())
|
||||
{
|
||||
return Redirect("/auth/login");
|
||||
}
|
||||
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
using System.Diagnostics;
|
||||
using System.Web.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using BlueWest.Views.Models;
|
||||
using BlueWest.Views.Utils;
|
||||
using Controller = Microsoft.AspNetCore.Mvc.Controller;
|
||||
|
||||
namespace BlueWest.Views.Controllers;
|
||||
|
||||
public class JobsController : Controller
|
||||
{
|
||||
private readonly ILogger<JobsController> _logger;
|
||||
|
||||
public JobsController(ILogger<JobsController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IActionResult Index()
|
||||
{
|
||||
this.HandleGlobalization();
|
||||
ViewData["Title"] = "Home Page";
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error()
|
||||
{
|
||||
return View(new ErrorViewModel {RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier});
|
||||
}
|
||||
}
|
|
@ -1,29 +1,49 @@
|
|||
using BlueWest.Data.Auth.Context.Users;
|
||||
using BlueWest.Views.Utils;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BlueWest.Views.Controllers
|
||||
{
|
||||
public class SystemController : Controller
|
||||
public class SystemController : UserController
|
||||
{
|
||||
private readonly ILogger<SystemController> _logger;
|
||||
|
||||
public SystemController(ILogger<SystemController> logger)
|
||||
public SystemController(ApplicationUserManager userManager, ILogger<SystemController> logger) : base(userManager, logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
|
||||
public IActionResult Index()
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
this.HandleGlobalization();
|
||||
ViewData[FooterMenuId] = LayoutCache.SystemRoute.Children;
|
||||
|
||||
await OnEveryAction();
|
||||
return View();
|
||||
}
|
||||
|
||||
public override void OnInitialization()
|
||||
{
|
||||
SetFooterMenu(LayoutCache.SystemRoute.Children);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Users()
|
||||
{
|
||||
await OnEveryAction();
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult Users()
|
||||
public async Task<IActionResult> Logs()
|
||||
{
|
||||
await OnEveryAction();
|
||||
return View();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Roles()
|
||||
{
|
||||
await OnEveryAction();
|
||||
return View("Roles/Index");
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using BlueWest.Data.Application.Users;
|
||||
using BlueWest.Data.Auth.Context.Users;
|
||||
using BlueWest.Views.Models;
|
||||
using BlueWest.Views.Utils;
|
||||
using Duende.IdentityServer.Extensions;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BlueWest.Views.Controllers;
|
||||
|
||||
public class UserController : Controller
|
||||
{
|
||||
protected ILogger<UserController> _logger;
|
||||
protected ApplicationUserManager _userManager;
|
||||
|
||||
private List<RouteRecord> _footerMenu;
|
||||
|
||||
public UserController(ApplicationUserManager userManager, ILogger<UserController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_userManager = userManager;
|
||||
_footerMenu = new List<RouteRecord>();
|
||||
}
|
||||
|
||||
public async Task OnEveryAction()
|
||||
{
|
||||
HandleGlobalization();
|
||||
SetFooterMenuViewData();
|
||||
await SetUserProfileViewData();
|
||||
OnInitialization();
|
||||
SetFooterMenuViewData();
|
||||
}
|
||||
|
||||
public virtual void OnInitialization()
|
||||
{
|
||||
}
|
||||
|
||||
protected void SetFooterMenuViewData()
|
||||
{
|
||||
ViewData[FooterMenuViewDataId] = _footerMenu;
|
||||
}
|
||||
|
||||
public void SetFooterMenu(List<RouteRecord> routeRecords)
|
||||
{
|
||||
_footerMenu = routeRecords;
|
||||
}
|
||||
|
||||
public async Task SetUserProfileViewData()
|
||||
{
|
||||
if (!ViewData.ContainsKey(UserViewDataId))
|
||||
{
|
||||
ViewData[UserViewDataId] = await GetLoggedInUser();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ApplicationUserUnique> GetLoggedInUser()
|
||||
{
|
||||
if (User.IsAuthenticated())
|
||||
{
|
||||
ApplicationUser? user = await _userManager.GetUserAsync(User);
|
||||
return new ApplicationUserUnique(user);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public IpInformation ExtractIpInformation(IPAddress? ipAddress)
|
||||
{
|
||||
string ipAddressString = "";
|
||||
IpType ipType = IpType.Unknown;
|
||||
|
||||
if (ipAddress != null)
|
||||
{
|
||||
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
ipType = IpType.Ipv4;
|
||||
ipAddressString = ipAddress.MapToIPv4().ToString();
|
||||
}
|
||||
|
||||
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
ipType = IpType.Ipv6;
|
||||
ipAddressString = ipAddress
|
||||
.MapToIPv6()
|
||||
.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new IpInformation(ipType, ipAddressString);
|
||||
}
|
||||
|
||||
public void HandleGlobalization()
|
||||
{
|
||||
var requestCultureFeature = HttpContext.Features.Get<IRequestCultureFeature>();
|
||||
string currentCultureName = DefaultCultureName;
|
||||
if (requestCultureFeature != null)
|
||||
{
|
||||
currentCultureName = requestCultureFeature.RequestCulture.Culture.Name;
|
||||
}
|
||||
|
||||
ViewData[LanguageViewStorage] = currentCultureName;
|
||||
IPAddress? remoteIpAddress = Request.HttpContext.Connection.RemoteIpAddress;
|
||||
IpInformation ipInformation = ExtractIpInformation(Request.HttpContext.Connection.RemoteIpAddress);
|
||||
|
||||
|
||||
ViewData[IpViewStorage] = ipInformation;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
namespace BlueWest.Views.Languages;
|
||||
|
||||
public static class SiteContent
|
||||
{
|
||||
internal static readonly Dictionary<string, Dictionary<string, string>> RouteTitle =
|
||||
new Dictionary<string, Dictionary<string, string>>
|
||||
{
|
||||
{
|
||||
RootKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Inicio"},
|
||||
{"eng", "Home"},
|
||||
{"en-gb", "Home"}
|
||||
}
|
||||
},
|
||||
{
|
||||
SystemKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Sistema"},
|
||||
{"eng", "System"},
|
||||
{"en-gb", "System"},
|
||||
}
|
||||
},
|
||||
{
|
||||
DataKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Dados"},
|
||||
{"eng", "Data"},
|
||||
{"en-gb", "Data"}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
RolesKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Tipos de Utilizador"},
|
||||
{"en-gb", "Roles"},
|
||||
{"eng", "Roles"}
|
||||
}
|
||||
},
|
||||
{
|
||||
ApplicationUsersKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Utilizadores da Aplicação"},
|
||||
{"en-gb", "Users"},
|
||||
{"eng", "Users"}
|
||||
}
|
||||
},
|
||||
{
|
||||
LogsKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Logs"},
|
||||
{"en-gb", "logs"},
|
||||
{"eng", "logs"}
|
||||
}
|
||||
},
|
||||
{
|
||||
SettingsKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Personalização"},
|
||||
{"eng", "Settings"},
|
||||
{"en-gb", "Settings"},
|
||||
}
|
||||
},
|
||||
{
|
||||
CompaniesKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Empresas"},
|
||||
{"eng", "Companies"},
|
||||
{"en-gb", "Companies"},
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
IndustriesKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Indústrias"},
|
||||
{"en-gb", "Industries"},
|
||||
{"eng", "Industries"},
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
CurrenciesKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Moedas"},
|
||||
{"en-gb", "Currencies"},
|
||||
{"eng", "Currencies"},
|
||||
}
|
||||
},
|
||||
{
|
||||
CountriesKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Países"},
|
||||
{"eng", "Countries"},
|
||||
{"en-gb", "Countries"}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
BanksKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Bancos"},
|
||||
{"eng", "Banks"},
|
||||
{"en-gb", "Banks"},
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
DataUsersKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Utilizadores"},
|
||||
{"eng", "Users"},
|
||||
{"en-gb", "Users"},
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
AccountKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Perfil"},
|
||||
{"eng", "Account"},
|
||||
{"en-gb", "Account"},
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
ChangePasswordKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Alterar Senha"},
|
||||
{"eng", "Change Password"},
|
||||
{"en-gb", "Change Password"},
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace BlueWest.Views.Models;
|
||||
|
||||
public record IpInformation
|
||||
(
|
||||
IpType IpType,
|
||||
string Address
|
||||
);
|
|
@ -0,0 +1,6 @@
|
|||
namespace BlueWest.Views.Models;
|
||||
public enum IpType
|
||||
{
|
||||
Ipv6, Ipv4, Unknown
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ namespace BlueWest.WebApi
|
|||
MainHost = CreateHostBuilder(args)
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.Build();
|
||||
|
||||
|
||||
MainHost.Run();
|
||||
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
"BlueWest.Views": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "https://localhost:7022;http://localhost:5204",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
|
|
|
@ -42,7 +42,8 @@ public sealed class SessionManager : ISessionCache
|
|||
/// <returns></returns>
|
||||
public async Task<SessionToken> GetSessionTokenByIdAsync(string tokenId)
|
||||
{
|
||||
return await _sessionTokens.Where(x => x.Id == tokenId)
|
||||
return
|
||||
await _sessionTokens.Where(x => x.Id == tokenId)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
|
|
|
@ -30,10 +30,6 @@ public class Startup
|
|||
services.AddSingleton<LayoutCache>();
|
||||
// Add services to the container.
|
||||
|
||||
IConfigurationRoot configuration = new ConfigurationBuilder()
|
||||
.AddJsonFile("config.json")
|
||||
.Build();
|
||||
|
||||
services.Configure<CookiePolicyOptions>(options =>
|
||||
{
|
||||
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
|
||||
|
@ -45,19 +41,12 @@ public class Startup
|
|||
{
|
||||
builder.AddSimpleConsole();
|
||||
});
|
||||
|
||||
services.AddSession(options =>
|
||||
{
|
||||
options.Cookie.Domain = SessionConstants.CookieDomain;
|
||||
options.Cookie.HttpOnly = true;
|
||||
});
|
||||
|
||||
services.AddControllersWithViews(x => x.EnableEndpointRouting = false);
|
||||
|
||||
services.AddSession(options => options.IdleTimeout = TimeSpan.FromHours(8));
|
||||
|
||||
|
||||
services.AddAuthServerServices(_configuration, _environment, configuration);
|
||||
services.PrepareMySqlDatabasePool(_configuration, _environment, configuration);
|
||||
|
||||
services.AddAuthServerServices(_configuration, _environment);
|
||||
services.PrepareMySqlDatabasePool(_configuration, _environment);
|
||||
|
||||
}
|
||||
|
||||
|
@ -93,6 +82,7 @@ public class Startup
|
|||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseSession();
|
||||
|
||||
app.UseMvcWithDefaultRoute();
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ using BlueWest.Data.Application.Users;
|
|||
using BlueWest.Data.Auth;
|
||||
using BlueWest.Data.Auth.Context.Users;
|
||||
using BlueWest.Domain;
|
||||
using BlueWest.WebApi.Configuration;
|
||||
using BlueWest.Views.Utils;
|
||||
using BlueWest.WebApi.Context.Users;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
|
@ -17,142 +17,127 @@ namespace BlueWest.Views;
|
|||
|
||||
public static class StartupExtensions
|
||||
{
|
||||
private static MySqlServerVersion GetMySqlServerVersion(int major, int minor, int build) => new (new Version(major, minor, build));
|
||||
private static MySqlServerVersion GetMySqlServerVersion(int major, int minor, int build) =>
|
||||
new(new Version(major, minor, build));
|
||||
|
||||
|
||||
private static BlueWestConnectionString GetConnectionString(this IConfigurationRoot configurationRoot)
|
||||
private static string GetConnectionString(this IConfiguration configurationRoot, string db)
|
||||
{
|
||||
// Docker / No-Docker
|
||||
var startupMode = configurationRoot["mode"];
|
||||
|
||||
if (startupMode == "docker")
|
||||
|
||||
if (!string.IsNullOrEmpty(startupMode) && startupMode == "docker")
|
||||
{
|
||||
var config = configurationRoot.Get<ConnectionStringDocker>();
|
||||
var config = configurationRoot.GetSection("ConnectionStringDocker")[db];
|
||||
return config;
|
||||
}
|
||||
else
|
||||
{
|
||||
var config = configurationRoot.Get<ConnectionStringNoDocker>();
|
||||
return config;
|
||||
return configurationRoot.GetSection("ConnectionStringNoDocker")[db] ?? string.Empty;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
internal static IServiceCollection AddAuthServerServices(this IServiceCollection services, IConfiguration configuration , IWebHostEnvironment environment, IConfigurationRoot configurationRoot)
|
||||
|
||||
|
||||
private static bool IsDockerMode(this IConfiguration config)
|
||||
{
|
||||
var startupMode = config["mode"];
|
||||
return startupMode == "docker";
|
||||
}
|
||||
|
||||
internal static IServiceCollection AddAuthServerServices(this IServiceCollection services,
|
||||
IConfiguration configuration, IWebHostEnvironment environment)
|
||||
{
|
||||
var connectionString = configuration.GetConnectionString("Redis");
|
||||
|
||||
if (string.IsNullOrEmpty(connectionString))
|
||||
{
|
||||
throw new InvalidOperationException("Redis connection string is empty");
|
||||
}
|
||||
|
||||
|
||||
services.AddSession(options =>
|
||||
{
|
||||
options.Cookie.Domain = SessionConstants.CookieDomain;
|
||||
options.Cookie.HttpOnly = true;
|
||||
options.IdleTimeout = TimeSpan.FromHours(8);
|
||||
});
|
||||
|
||||
var connectionString = configurationRoot.GetConnectionString();
|
||||
services
|
||||
.AddSingleton(new RedisConnectionProvider(connectionString))
|
||||
.AddScoped<IJwtTokenHandler, JwtTokenHandler>()
|
||||
.AddScoped<IJwtFactory, JwtFactory>()
|
||||
.AddHostedService<SessionManager>()
|
||||
.AddSingleton<ISessionCache, SessionManager>()
|
||||
.AddScoped<UserRepository>()
|
||||
.AddScoped<IUserManager, ApplicationUserManager>()
|
||||
.AddScoped<IAuthManager, AuthManager>()
|
||||
.AddScoped<IHasher, Hasher>();
|
||||
|
||||
if (connectionString == null)
|
||||
// Database Context and Swagger
|
||||
|
||||
|
||||
// Register the ConfigurationBuilder instance of AuthSettings
|
||||
var authSettings = configuration.GetSection(nameof(AuthSettings));
|
||||
services.Configure<AuthSettings>(authSettings);
|
||||
var signingKey = new SymmetricSecurityKey
|
||||
(Encoding.ASCII.GetBytes(authSettings[nameof(AuthSettings.SecretKey)]));
|
||||
|
||||
// jwt wire up
|
||||
// Get options from app settings
|
||||
var jwtAppSettingOptions = configuration
|
||||
.GetSection(nameof(JwtIssuerOptions));
|
||||
|
||||
// Configure JwtIssuerOptions
|
||||
services.Configure<JwtIssuerOptions>(options =>
|
||||
{
|
||||
options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
|
||||
options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
|
||||
options.SigningCredentials = new SigningCredentials
|
||||
(signingKey, SecurityAlgorithms.HmacSha256);
|
||||
});
|
||||
|
||||
var tokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
|
||||
|
||||
ValidateAudience = true,
|
||||
ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
|
||||
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = signingKey,
|
||||
|
||||
RequireExpirationTime = false,
|
||||
ValidateLifetime = true,
|
||||
ClockSkew = TimeSpan.Zero
|
||||
};
|
||||
|
||||
services.AddAuthentication(options =>
|
||||
{
|
||||
throw new InvalidOperationException("Redis connection string is empty");
|
||||
}
|
||||
|
||||
services.AddDistributedRedisCache(options =>
|
||||
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddCookie(options =>
|
||||
{
|
||||
options.Configuration = connectionString.Redis;
|
||||
|
||||
options.LoginPath = Routes.AuthLoginRoute;
|
||||
options.LogoutPath = Routes.AuthLogoutRoute;
|
||||
});
|
||||
|
||||
services
|
||||
.AddSingleton(new RedisConnectionProvider(connectionString.Redis))
|
||||
.AddScoped<IJwtTokenHandler, JwtTokenHandler>()
|
||||
.AddScoped<IJwtFactory, JwtFactory>()
|
||||
.AddHostedService<SessionManager>()
|
||||
.AddSingleton<ISessionCache, SessionManager>()
|
||||
.AddScoped<UserRepository>()
|
||||
.AddScoped<IUserManager, ApplicationUserManager>()
|
||||
.AddScoped<IAuthManager, AuthManager>()
|
||||
.AddScoped<IHasher, Hasher>();
|
||||
|
||||
// Database Context and Swagger
|
||||
|
||||
// api user claim policy
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy(SessionConstants.ApiNamePolicy,
|
||||
policy => policy.RequireClaim(Data.Auth.Context.Users.Constants.JwtClaimIdentifiers.Rol,
|
||||
Data.Auth.Context.Users.Constants.JwtClaims.ApiAccess));
|
||||
});
|
||||
|
||||
// Register the ConfigurationBuilder instance of AuthSettings
|
||||
var authSettings = configuration.GetSection(nameof(AuthSettings));
|
||||
services.Configure<AuthSettings>(authSettings);
|
||||
var signingKey = new SymmetricSecurityKey
|
||||
(Encoding.ASCII.GetBytes(authSettings[nameof(AuthSettings.SecretKey)]));
|
||||
|
||||
// jwt wire up
|
||||
// Get options from app settings
|
||||
var jwtAppSettingOptions = configuration
|
||||
.GetSection(nameof(JwtIssuerOptions));
|
||||
|
||||
// Configure JwtIssuerOptions
|
||||
services.Configure<JwtIssuerOptions>(options =>
|
||||
{
|
||||
options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
|
||||
options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
|
||||
options.SigningCredentials = new SigningCredentials
|
||||
(signingKey, SecurityAlgorithms.HmacSha256);
|
||||
});
|
||||
|
||||
var tokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
|
||||
|
||||
ValidateAudience = true,
|
||||
ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
|
||||
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = signingKey,
|
||||
|
||||
RequireExpirationTime = false,
|
||||
ValidateLifetime = true,
|
||||
ClockSkew = TimeSpan.Zero
|
||||
};
|
||||
|
||||
services.AddAuthentication(options =>
|
||||
{
|
||||
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddCookie(options =>
|
||||
{
|
||||
options.LoginPath = "/";
|
||||
options.LogoutPath = "/logout";
|
||||
})
|
||||
.AddJwtBearer(configureOptions =>
|
||||
{
|
||||
configureOptions.ClaimsIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
|
||||
configureOptions.TokenValidationParameters = tokenValidationParameters;
|
||||
configureOptions.SaveToken = true;
|
||||
|
||||
configureOptions.Events = new JwtBearerEvents
|
||||
{
|
||||
OnAuthenticationFailed = context =>
|
||||
{
|
||||
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
|
||||
{
|
||||
context.Response.Headers.Add("Token-Expired", "true");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
// api user claim policy
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy(SessionConstants.ApiNamePolicy,
|
||||
policy => policy.RequireClaim(Data.Auth.Context.Users.Constants.JwtClaimIdentifiers.Rol,
|
||||
Data.Auth.Context.Users.Constants.JwtClaims.ApiAccess));
|
||||
|
||||
});
|
||||
|
||||
// add identity
|
||||
var identityBuilder = services.AddIdentityCore<ApplicationUser>(o =>
|
||||
// add identity
|
||||
var identityBuilder = services.AddIdentityCore<ApplicationUser>(o =>
|
||||
{
|
||||
o.User.RequireUniqueEmail = true;
|
||||
|
||||
|
||||
// configure identity options
|
||||
o.Password.RequireDigit = false;
|
||||
o.Password.RequireLowercase = false;
|
||||
|
@ -160,87 +145,82 @@ public static class StartupExtensions
|
|||
o.Password.RequireNonAlphanumeric = false;
|
||||
o.Password.RequiredLength = 6;
|
||||
})
|
||||
.AddUserManager<ApplicationUserManager>()
|
||||
.AddUserStore<UserRepository>();
|
||||
.AddUserManager<ApplicationUserManager>()
|
||||
.AddUserStore<UserRepository>();
|
||||
|
||||
identityBuilder = new IdentityBuilder(identityBuilder.UserType, typeof(ApplicationRole), identityBuilder.Services);
|
||||
identityBuilder
|
||||
.AddEntityFrameworkStores<ApplicationUserDbContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
identityBuilder =
|
||||
new IdentityBuilder(identityBuilder.UserType, typeof(ApplicationRole), identityBuilder.Services);
|
||||
identityBuilder
|
||||
.AddEntityFrameworkStores<ApplicationUserDbContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
return services;
|
||||
return services;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get MYSQL Connection String
|
||||
/// </summary>
|
||||
/// <param name="optionsBuilder"></param>
|
||||
/// <param name="configuration"></param>
|
||||
/// <param name="environment"></param>
|
||||
private static DbContextOptionsBuilder GetMySqlSettings(
|
||||
this DbContextOptionsBuilder optionsBuilder,
|
||||
IConfiguration configuration,
|
||||
IWebHostEnvironment environment)
|
||||
{
|
||||
var sqlVersion = GetMySqlServerVersion(8, 0, 11);
|
||||
|
||||
// Docker / No-Docker
|
||||
|
||||
|
||||
var mySqlConnectionString = configuration.GetConnectionString("MySQL");
|
||||
|
||||
if (mySqlConnectionString == string.Empty)
|
||||
{
|
||||
throw new InvalidOperationException("MySQL Connection string appears to be empty.");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get MYSQL Connection String
|
||||
/// </summary>
|
||||
/// <param name="optionsBuilder"></param>
|
||||
/// <param name="configuration"></param>
|
||||
/// <param name="environment"></param>
|
||||
private static DbContextOptionsBuilder GetMySqlSettings(
|
||||
this DbContextOptionsBuilder optionsBuilder,
|
||||
IConfiguration configuration,
|
||||
IConfigurationRoot configurationRoot,
|
||||
IWebHostEnvironment environment)
|
||||
{
|
||||
var sqlVersion = GetMySqlServerVersion(8, 0, 11);
|
||||
|
||||
// Docker / No-Docker
|
||||
|
||||
string mySqlConnectionString = configurationRoot.GetConnectionString().MySql;
|
||||
|
||||
if (mySqlConnectionString == string.Empty)
|
||||
{
|
||||
throw new InvalidOperationException("Fatal error: MySQL Connection string is empty.");
|
||||
}
|
||||
|
||||
optionsBuilder
|
||||
.UseMySql(
|
||||
mySqlConnectionString,
|
||||
sqlVersion)
|
||||
.UseMySql(sqlVersion,
|
||||
builder => { builder.EnableRetryOnFailure(6, TimeSpan.FromSeconds(3), null); });
|
||||
|
||||
optionsBuilder
|
||||
.UseMySql(
|
||||
mySqlConnectionString,
|
||||
sqlVersion)
|
||||
.UseMySql(sqlVersion,
|
||||
builder =>
|
||||
{
|
||||
builder.EnableRetryOnFailure(6, TimeSpan.FromSeconds(3), null);
|
||||
});
|
||||
|
||||
// The following three options help with debugging, but should
|
||||
// be changed or removed for production.
|
||||
if (environment.IsDevelopment())
|
||||
{
|
||||
optionsBuilder
|
||||
.LogTo(Console.WriteLine, LogLevel.Information)
|
||||
.EnableSensitiveDataLogging()
|
||||
.EnableDetailedErrors();
|
||||
}
|
||||
|
||||
return optionsBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup database Contexts
|
||||
/// </summary>
|
||||
/// <param name="serviceCollection"></param>
|
||||
/// <param name="configuration"></param>
|
||||
/// <param name="environment"></param>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection PrepareMySqlDatabasePool(this IServiceCollection serviceCollection,
|
||||
IConfiguration configuration, IWebHostEnvironment environment, IConfigurationRoot configurationRoot)
|
||||
{
|
||||
return serviceCollection
|
||||
.AddDbContextPool<UserDbContext>(options =>
|
||||
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||
.AddDbContextPool<CountryDbContext>(options =>
|
||||
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||
.AddDbContextPool<FinanceDbContext>(options =>
|
||||
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||
.AddDbContextPool<CompanyDbContext>(options =>
|
||||
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||
.AddDbContextPool<ApplicationUserDbContext>(options =>
|
||||
options.GetMySqlSettings(configuration, configurationRoot, environment));
|
||||
}
|
||||
// The following three options help with debugging, but should
|
||||
// be changed or removed for production.
|
||||
if (environment.IsDevelopment())
|
||||
{
|
||||
optionsBuilder
|
||||
.LogTo(Console.WriteLine, LogLevel.Information)
|
||||
.EnableSensitiveDataLogging()
|
||||
.EnableDetailedErrors();
|
||||
}
|
||||
|
||||
return optionsBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup database Contexts
|
||||
/// </summary>
|
||||
/// <param name="serviceCollection"></param>
|
||||
/// <param name="configuration"></param>
|
||||
/// <param name="environment"></param>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection PrepareMySqlDatabasePool(this IServiceCollection serviceCollection,
|
||||
IConfiguration configuration, IWebHostEnvironment environment)
|
||||
{
|
||||
return serviceCollection
|
||||
.AddDbContextPool<UserDbContext>(options =>
|
||||
options.GetMySqlSettings(configuration, environment))
|
||||
.AddDbContextPool<CountryDbContext>(options =>
|
||||
options.GetMySqlSettings(configuration, environment))
|
||||
.AddDbContextPool<FinanceDbContext>(options =>
|
||||
options.GetMySqlSettings(configuration, environment))
|
||||
.AddDbContextPool<CompanyDbContext>(options =>
|
||||
options.GetMySqlSettings(configuration, environment))
|
||||
.AddDbContextPool<ApplicationUserDbContext>(options =>
|
||||
options.GetMySqlSettings(configuration, environment));
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BlueWest.Views.Utils;
|
||||
|
||||
public static class ControllerExtensions
|
||||
{
|
||||
public static void HandleGlobalization(this Controller controller)
|
||||
{
|
||||
var context = controller.HttpContext;
|
||||
var requestCultureFeature = context.Features.Get<IRequestCultureFeature>();
|
||||
var currentCultureName = Routes.DefaultCultureName;
|
||||
if (requestCultureFeature != null)
|
||||
{
|
||||
currentCultureName = requestCultureFeature.RequestCulture.Culture.Name;
|
||||
}
|
||||
|
||||
controller.ViewData[LanguageViewStorage] = currentCultureName;
|
||||
}
|
||||
|
||||
public static void HandlePageName(this Controller controller, string location)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using BlueWest.Data.Application.Users;
|
||||
using BlueWest.Data.Auth;
|
||||
using BlueWest.Views.Models;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
|
||||
namespace BlueWest.Views.Utils;
|
||||
|
||||
public static class DataUtil
|
||||
{
|
||||
public static ApplicationUserUnique GetUserViewData(this ViewDataDictionary viewData)
|
||||
{
|
||||
if (viewData[UserViewDataId] is ApplicationUserUnique user)
|
||||
{
|
||||
return user;
|
||||
}
|
||||
|
||||
return null!;
|
||||
}
|
||||
|
||||
public static IpInformation GetIpInformation(this ViewDataDictionary viewData)
|
||||
{
|
||||
if (viewData[IpViewStorage] is IpInformation ipInformation)
|
||||
{
|
||||
return ipInformation;
|
||||
}
|
||||
|
||||
return null!;
|
||||
}
|
||||
|
||||
public static string GetRootUrl(this ViewDataDictionary viewData)
|
||||
{
|
||||
return SessionConstants.CookieDomain;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
using System.Diagnostics;
|
||||
using BlueWest.Views.Controllers;
|
||||
using BlueWest.Views.Controllers.Data;
|
||||
using BlueWest.Views.Languages;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
|
||||
namespace BlueWest.Views.Utils;
|
||||
|
@ -8,6 +10,12 @@ internal class LayoutCache
|
|||
{
|
||||
#region Route Tree
|
||||
|
||||
private static readonly RouteRecord NonLoggedInRoot = new RouteRecord(
|
||||
AuthLoginRoute,
|
||||
AuthLoginKeyName,
|
||||
nameof(AuthController), new List<RouteRecord>());
|
||||
|
||||
|
||||
private static readonly RouteRecord Root = new RouteRecord(
|
||||
RootKeyName,
|
||||
RootLocation,
|
||||
|
@ -82,11 +90,14 @@ internal class LayoutCache
|
|||
ViewType.System
|
||||
),
|
||||
new RouteRecord(
|
||||
JobsKeyName,
|
||||
JobsRouteLocation,
|
||||
nameof(JobsController),
|
||||
new List<RouteRecord>(),
|
||||
ViewType.Jobs
|
||||
AccountKeyName,
|
||||
AccountRouteLocation,
|
||||
nameof(AccountController),
|
||||
new List<RouteRecord>()
|
||||
{
|
||||
new RouteRecord(ChangePasswordKeyName, ChangePasswordRouteLocation, nameof(AccountController), new List<RouteRecord>())
|
||||
},
|
||||
ViewType.Account
|
||||
),
|
||||
}, ViewType.Root);
|
||||
|
||||
|
@ -97,14 +108,11 @@ internal class LayoutCache
|
|||
internal static readonly RouteRecord SystemRoute =
|
||||
Root.Children.FirstOrDefault(x => x.ViewType == ViewType.System)!;
|
||||
|
||||
internal static readonly RouteRecord DataRoute =
|
||||
internal static readonly RouteRecord DataRouteRecord =
|
||||
Root.Children.FirstOrDefault(x => x.ViewType == ViewType.Data)!;
|
||||
|
||||
internal static readonly RouteRecord JobRoute =
|
||||
Root.Children.FirstOrDefault(x => x.ViewType == ViewType.Jobs)!;
|
||||
|
||||
internal static readonly RouteRecord ProfileRoute =
|
||||
Root.Children.FirstOrDefault(x => x.ViewType == ViewType.Profile)!;
|
||||
internal static readonly RouteRecord AccountRouteRecord =
|
||||
Root.Children.FirstOrDefault(x => x.ViewType == ViewType.Account)!;
|
||||
|
||||
#endregion Routing Utils
|
||||
|
||||
|
@ -112,13 +120,13 @@ internal class LayoutCache
|
|||
|
||||
internal static List<RouteView> GetDefaultFooterMenu(ViewDataDictionary dictionary)
|
||||
{
|
||||
var location = GetLocation(dictionary);
|
||||
var location = GetUserLanguage(dictionary);
|
||||
|
||||
var menu = LayoutCache
|
||||
.Root
|
||||
.Children;
|
||||
|
||||
if (dictionary[FooterMenuId] is List<RouteRecord> footerMenu)
|
||||
if (dictionary[FooterMenuViewDataId] is List<RouteRecord> footerMenu)
|
||||
{
|
||||
menu = footerMenu;
|
||||
}
|
||||
|
@ -126,12 +134,12 @@ internal class LayoutCache
|
|||
return menu
|
||||
.Select(x =>
|
||||
{
|
||||
if (Translation[x.routeKey].ContainsKey(location))
|
||||
if (SiteContent.RouteTitle[x.routeKey].ContainsKey(location))
|
||||
{
|
||||
return new RouteView(Translation[x.routeKey][location],
|
||||
return new RouteView(SiteContent.RouteTitle[x.routeKey][location],
|
||||
location);
|
||||
}
|
||||
return new RouteView(Translation[x.routeKey][DefaultCultureName],
|
||||
return new RouteView(SiteContent.RouteTitle[x.routeKey][DefaultCultureName],
|
||||
x.location);
|
||||
|
||||
})
|
||||
|
@ -140,9 +148,16 @@ internal class LayoutCache
|
|||
|
||||
}
|
||||
|
||||
internal static List<RouteView> GetDefaultHeaderMenu(ViewDataDictionary dictionary)
|
||||
internal static List<RouteView> GetDefaultHeaderMenu(ViewDataDictionary dictionary, bool userAuthenticated = false)
|
||||
{
|
||||
var location = GetLocation(dictionary);
|
||||
if (!userAuthenticated)
|
||||
{
|
||||
var menuToShow = new List<RouteView>();
|
||||
menuToShow.Add(new RouteView("Blue West", "/"));
|
||||
return menuToShow;
|
||||
}
|
||||
|
||||
var location = GetUserLanguage(dictionary);
|
||||
|
||||
var menu = LayoutCache
|
||||
.Root
|
||||
|
@ -156,20 +171,27 @@ internal class LayoutCache
|
|||
return menu
|
||||
.Select(x =>
|
||||
{
|
||||
if (Translation[x.routeKey].ContainsKey(location))
|
||||
if (SiteContent.RouteTitle[x.routeKey].ContainsKey(location))
|
||||
{
|
||||
return new RouteView(Translation[x.routeKey][location],
|
||||
return new RouteView(SiteContent.RouteTitle[x.routeKey][location],
|
||||
x.Location);
|
||||
}
|
||||
return new RouteView(Translation[x.routeKey][DefaultCultureName],
|
||||
return new RouteView(SiteContent.RouteTitle[x.routeKey][DefaultCultureName],
|
||||
x.location);
|
||||
|
||||
})
|
||||
|
||||
.ToList();
|
||||
}
|
||||
|
||||
internal static string GetLocation(ViewDataDictionary dictionary)
|
||||
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal static void IsDevMode(ref bool isDebug)
|
||||
{
|
||||
isDebug = true;
|
||||
}
|
||||
|
||||
internal static string GetUserLanguage(ViewDataDictionary dictionary)
|
||||
{
|
||||
return (dictionary[LanguageViewStorage] as string)!;
|
||||
}
|
||||
|
|
|
@ -6,185 +6,87 @@ namespace BlueWest.Views.Utils
|
|||
{
|
||||
public const string DefaultCultureName = "en-gb";
|
||||
|
||||
#region Layout Keys
|
||||
#region View Data Keys
|
||||
|
||||
internal const string FooterMenuId = "m1";
|
||||
internal const string FooterMenuViewDataId = "m1";
|
||||
internal const string UserViewDataId = "uvd";
|
||||
internal const string HeaderMenuId = "m2";
|
||||
internal const string LanguageViewStorage = "i80";
|
||||
internal const string LanguageViewStorage = "i81";
|
||||
internal const string IpViewStorage = "ip";
|
||||
internal const string AgentViewStorage = "ag2";
|
||||
|
||||
#endregion Layout Keys
|
||||
|
||||
#endregion View Data Keys
|
||||
|
||||
#region Route Keys
|
||||
|
||||
internal const string DataKeyName = "data";
|
||||
|
||||
internal const string RootKeyName = "root";
|
||||
internal const string RootLocation = "/";
|
||||
|
||||
internal const string SystemKeyName = "system";
|
||||
|
||||
|
||||
internal const string RootAuthRoute = "/auth";
|
||||
internal const string RootAuthKeyName = "auth";
|
||||
|
||||
internal const string AuthLoginRoute = $"{RootAuthRoute}/login";
|
||||
internal const string AuthLoginKeyName = "login";
|
||||
|
||||
internal const string AuthSignupRoute = $"{RootAuthRoute}/signup";
|
||||
internal const string AuthSignupKeyName = "signup";
|
||||
|
||||
|
||||
internal const string RolesLocation = $"{SystemRouteLocation}/roles";
|
||||
internal const string RolesKeyName = "roles";
|
||||
internal const string CompaniesKeyName = "companies";
|
||||
internal const string CountriesKeyName = "countries";
|
||||
internal const string CurrenciesKeyName = "currencies";
|
||||
internal const string IndustriesKeyName = "industries";
|
||||
internal const string DataUsersKeyName = "users";
|
||||
internal const string BanksKeyName = "banks";
|
||||
internal const string JobsKeyName = "jobs";
|
||||
internal const string SettingsKeyName = "settings";
|
||||
internal const string LogsKeyName = "logs";
|
||||
|
||||
internal const string ApplicationUsersLocation = $"{SystemRouteLocation}/users";
|
||||
internal const string ApplicationUsersKeyName = "app_users";
|
||||
|
||||
#endregion Route Keys
|
||||
// Translation Database Keys
|
||||
|
||||
#region Routes Data
|
||||
|
||||
// Routes
|
||||
internal const string RolesLocation = $"{SystemRouteLocation}/roles";
|
||||
internal const string ApplicationUsersLocation = $"{SystemRouteLocation}/users";
|
||||
internal const string LogsLocation = $"{SystemRouteLocation}/logs";
|
||||
internal const string LogsKeyName = "logs";
|
||||
|
||||
internal const string SettingsRouteLocation = $"{SystemRouteLocation}/settings";
|
||||
internal const string SettingsKeyName = "settings";
|
||||
|
||||
internal const string DataLocation = $"/data";
|
||||
internal const string DataKeyName = "data";
|
||||
|
||||
|
||||
// Banks
|
||||
|
||||
// Data users
|
||||
internal const string DataUsersLocation = "/data/users";
|
||||
internal const string JobsRouteLocation = "/jobs";
|
||||
internal const string DataUsersKeyName = "users";
|
||||
|
||||
internal const string SystemRouteLocation = $"/system";
|
||||
internal const string SystemKeyName = "system";
|
||||
|
||||
internal const string BanksKeyName = "banks";
|
||||
internal const string BanksLocation = $"{DataLocation}/banks";
|
||||
|
||||
internal const string CountriesLocation = $"{DataLocation}/countries";
|
||||
internal const string CountriesKeyName = "countries";
|
||||
|
||||
internal const string CurrenciesLocation = $"{DataLocation}/currencies";
|
||||
internal const string CurrenciesKeyName = "currencies";
|
||||
|
||||
internal const string CompaniesLocation = $"{DataLocation}/companies";
|
||||
internal const string IndustriesKeyName = "industries";
|
||||
internal const string IndustriesLocation = $"{DataLocation}/industries";
|
||||
|
||||
internal const string CompaniesLocation = $"{DataLocation}/companies";
|
||||
internal const string CompaniesKeyName = "companies";
|
||||
|
||||
|
||||
// Jobs
|
||||
internal const string Curriculums = "curriculums";
|
||||
internal const string Proposals = "proposals";
|
||||
internal const string Offers = "offers";
|
||||
internal const string Processes = "processes";
|
||||
internal const string AuthLogoutRoute = "/auth/logout";
|
||||
|
||||
// Account
|
||||
internal const string AccountRouteLocation = "/account";
|
||||
internal const string AccountKeyName = "profile";
|
||||
internal const string ChangePasswordKeyName = "changepwd";
|
||||
internal const string ChangePasswordRouteLocation = $"{AccountRouteLocation}/change";
|
||||
|
||||
#endregion Routes Data
|
||||
|
||||
internal static Dictionary<string, Dictionary<string, string>> Translation =
|
||||
new Dictionary<string, Dictionary<string, string>>
|
||||
{
|
||||
{
|
||||
RootKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Inicio"},
|
||||
{"eng", "Home"},
|
||||
{"en-gb", "Home"}
|
||||
}
|
||||
},
|
||||
{
|
||||
SystemKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Sistema"},
|
||||
{"eng", "System"},
|
||||
{"en-gb", "System"},
|
||||
}
|
||||
},
|
||||
{
|
||||
DataKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Dados"},
|
||||
{"eng", "Data"},
|
||||
{"en-gb", "Data"}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
RolesKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Tipos de Utilizador"},
|
||||
{"en-gb", "Roles"},
|
||||
{"eng", "Roles"}
|
||||
}
|
||||
},
|
||||
{
|
||||
ApplicationUsersKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Utilizadores da Aplicação"},
|
||||
{"en-gb", "Users"},
|
||||
{"eng", "Users"}
|
||||
}
|
||||
},
|
||||
{
|
||||
LogsKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Logs"},
|
||||
{"en-gb", "logs"},
|
||||
{"eng", "logs"}
|
||||
}
|
||||
},
|
||||
{
|
||||
SettingsKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Personalização"},
|
||||
{"eng", "Settings"},
|
||||
{"en-gb", "Settings"},
|
||||
}
|
||||
},
|
||||
{
|
||||
CompaniesKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Empresas"},
|
||||
{"eng", "Companies"},
|
||||
{"en-gb", "Companies"},
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
IndustriesKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Indústrias"},
|
||||
{"en-gb", "Industries"},
|
||||
{"eng", "Industries"},
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
CurrenciesKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Moedas"},
|
||||
{"en-gb", "Currencies"},
|
||||
{"eng", "Currencies"},
|
||||
}
|
||||
},
|
||||
{
|
||||
CountriesKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Países"},
|
||||
{"eng", "Countries"},
|
||||
{"en-gb", "Countries"}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
BanksKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Bancos"},
|
||||
{"eng", "Banks"},
|
||||
{"en-gb", "Banks"},
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
DataUsersKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Utilizadores"},
|
||||
{"eng", "Users"},
|
||||
{"en-gb", "Users"},
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
JobsKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Anúncios de trabalho"},
|
||||
{"eng", "Jobs"},
|
||||
{"en-gb", "Jobs"},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -6,8 +6,7 @@ namespace BlueWest.Views
|
|||
{
|
||||
System,
|
||||
Data,
|
||||
Jobs,
|
||||
Profile,
|
||||
Account,
|
||||
Root,
|
||||
Undefined
|
||||
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
@using BlueWest.Views.Utils
|
||||
@model BlueWest.Data.Application.Users.ApplicationUserUnique
|
||||
@{
|
||||
var user = @Model;
|
||||
}
|
||||
|
||||
<div class="q-ma-lg">
|
||||
<form method="POST" action="EditProfile" class="q-form q-gutter-md">
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Phone Number" name="name" type="name" value="@Model.Id" disabled="disabled">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Id</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" name="email" aria-label="Email" type="email" value="@user.Email">
|
||||
</div>
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Email address</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Phone Number" name="name" type="name" value="@Model.PhoneNumber">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Phone Number</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Phone Number" name="name" type="name" value="@Model.NormalizedEmail" disabled="disabled">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Normalized Email</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Phone Number" name="name" type="name" value="@Model.UserName">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Username</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Password Hash" name="name" type="name" value="@Model.PasswordHash" disabled="disabled">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Username</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<div>
|
||||
<button class="q-btn q-btn-item non-selectable no-outline q-btn--standard q-btn--rectangle bg-primary text-white q-btn--actionable q-focusable q-hoverable">
|
||||
<span class="q-focus-helper"></span>
|
||||
<span class="q-btn__content text-center col items-center q-anchor--skip justify-center row">
|
||||
<span class="block">Save</span>
|
||||
</span>
|
||||
</button>
|
||||
<button class="q-btn q-btn-item non-selectable no-outline q-btn--flat q-btn--rectangle text-primary q-btn--actionable q-focusable q-hoverable q-ml-sm" type="reset">
|
||||
<span class="q-focus-helper"></span>
|
||||
<span class="q-btn__content text-center col items-center q-anchor--skip justify-center row">
|
||||
<span class="block">Reset</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
@await Html.PartialAsync("_FooterMenu")
|
|
@ -0,0 +1,10 @@
|
|||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Curriculums Module</h1>
|
||||
</div>
|
||||
|
||||
|
||||
@Html.Partial("_FooterMenu");
|
|
@ -0,0 +1,111 @@
|
|||
@using BlueWest.Views.Utils
|
||||
@model BlueWest.Data.Application.Users.ApplicationUserUnique
|
||||
@{
|
||||
var user = @Model;
|
||||
}
|
||||
|
||||
<div class="q-ma-lg">
|
||||
<form method="POST" action="EditProfile" class="q-form q-gutter-md">
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Phone Number" name="name" type="name" value="@Model.Id" disabled="disabled">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Id</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" name="email" aria-label="Email" type="email" value="@user.Email">
|
||||
</div>
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Email address</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Phone Number" name="name" type="name" value="@Model.PhoneNumber">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Phone Number</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Phone Number" name="name" type="name" value="@Model.NormalizedEmail" disabled="disabled">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Normalized Email</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Phone Number" name="name" type="name" value="@Model.UserName">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Username</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Password Hash" name="name" type="name" value="@Model.PasswordHash" disabled="disabled">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Username</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<div>
|
||||
<button class="q-btn q-btn-item non-selectable no-outline q-btn--standard q-btn--rectangle bg-primary text-white q-btn--actionable q-focusable q-hoverable">
|
||||
<span class="q-focus-helper"></span>
|
||||
<span class="q-btn__content text-center col items-center q-anchor--skip justify-center row">
|
||||
<span class="block">Save</span>
|
||||
</span>
|
||||
</button>
|
||||
<button class="q-btn q-btn-item non-selectable no-outline q-btn--flat q-btn--rectangle text-primary q-btn--actionable q-focusable q-hoverable q-ml-sm" type="reset">
|
||||
<span class="q-focus-helper"></span>
|
||||
<span class="q-btn__content text-center col items-center q-anchor--skip justify-center row">
|
||||
<span class="block">Reset</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
@await Html.PartialAsync("_FooterMenu")
|
|
@ -2,5 +2,4 @@
|
|||
@using (Html.BeginForm()){
|
||||
@Html.LabelFor(x => x.Password, "Password")
|
||||
@Html.PasswordFor(x => x.Password);
|
||||
@Html.NameFor(x => x.Email);
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<div class="q-pa-md q-gutter-sm">
|
||||
<div class="row">
|
||||
<div class="col col-md-8">
|
||||
<div class="q-breadcrumbs">
|
||||
<div class="flex items-center justify-start q-gutter-sm">
|
||||
<div class="flex items-center text-primary">
|
||||
<a class="q-breadcrumbs__el q-link flex inline items-center relative-position q-link--focusable">
|
||||
<i class="q-icon notranslate material-icons q-breadcrumbs__el-icon q-breadcrumbs__el-icon--with-label">share_location</i>
|
||||
System
|
||||
</a>
|
||||
</div>
|
||||
<div class="q-breadcrumbs__separator">/</div>
|
||||
<div class="flex items-center q-breadcrumbs--last">
|
||||
<span class="q-breadcrumbs__el q-link flex inline items-center relative-position q-link--focusable">Login</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="new-user-container">
|
||||
<div class="q-pa-md" style="max-width: 400px;">
|
||||
<form method="POST" action="LoginAction" class="q-form q-gutter-md">
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" name="email" aria-label="Email" type="email">
|
||||
</div>
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Email address</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Password" name="password" type="password">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Password</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<div>
|
||||
<button class="q-btn q-btn-item non-selectable no-outline q-btn--standard q-btn--rectangle bg-primary text-white q-btn--actionable q-focusable q-hoverable">
|
||||
<span class="q-focus-helper"></span>
|
||||
<span class="q-btn__content text-center col items-center q-anchor--skip justify-center row">
|
||||
<span class="block">Login</span>
|
||||
</span>
|
||||
</button>
|
||||
<button class="q-btn q-btn-item non-selectable no-outline q-btn--flat q-btn--rectangle text-primary q-btn--actionable q-focusable q-hoverable q-ml-sm" type="reset">
|
||||
<span class="q-focus-helper"></span>
|
||||
<span class="q-btn__content text-center col items-center q-anchor--skip justify-center row">
|
||||
<span class="block">Reset</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,117 @@
|
|||
<div class="q-pa-md q-gutter-sm">
|
||||
<div class="row">
|
||||
<div class="col col-md-8">
|
||||
<div class="q-breadcrumbs">
|
||||
<div class="flex items-center justify-start q-gutter-sm">
|
||||
<div class="flex items-center text-primary">
|
||||
<a class="q-breadcrumbs__el q-link flex inline items-center relative-position q-link--focusable">
|
||||
<i class="q-icon notranslate material-icons q-breadcrumbs__el-icon q-breadcrumbs__el-icon--with-label">share_location</i>
|
||||
System
|
||||
</a>
|
||||
</div>
|
||||
<div class="q-breadcrumbs__separator">/</div>
|
||||
<div class="flex items-center q-breadcrumbs--last">
|
||||
<span class="q-breadcrumbs__el q-link flex inline items-center relative-position q-link--focusable">Login</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="new-user-container">
|
||||
<div class="q-pa-md" style="max-width: 400px;">
|
||||
<form method="POST" action="SignupAction" class="q-form q-gutter-md">
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" name="email" aria-label="Email" type="email">
|
||||
<div class="q-field__label no-pointer-events absolute ellipsis">Name</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Email address</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" name="username" aria-label="username" type="name">
|
||||
<div class="q-field__label no-pointer-events absolute ellipsis">Username</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Username</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" name="phoneNumber" aria-label="phoneNumber" type="phoneNumber">
|
||||
<div class="q-field__label no-pointer-events absolute ellipsis">Phone Number</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Phone Number</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Password" name="password" type="password">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Password</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="confirmPassword" name="confirmPassword" type="password">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Confirm Password</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
||||
<div>
|
||||
<button class="q-btn q-btn-item non-selectable no-outline q-btn--standard q-btn--rectangle bg-primary text-white q-btn--actionable q-focusable q-hoverable">
|
||||
<span class="q-focus-helper"></span>
|
||||
<span class="q-btn__content text-center col items-center q-anchor--skip justify-center row">
|
||||
<span class="block">Login</span>
|
||||
</span>
|
||||
</button>
|
||||
<button class="q-btn q-btn-item non-selectable no-outline q-btn--flat q-btn--rectangle text-primary q-btn--actionable q-focusable q-hoverable q-ml-sm" type="reset">
|
||||
<span class="q-focus-helper"></span>
|
||||
<span class="q-btn__content text-center col items-center q-anchor--skip justify-center row">
|
||||
<span class="block">Reset</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,6 @@
|
|||
<div class="text-center">
|
||||
<h1 class="display-4">Banks</h1>
|
||||
</div>
|
||||
|
||||
|
||||
@await Html.PartialAsync("_FooterMenu");
|
|
@ -1,11 +1,6 @@
|
|||
@using BlueWest.Views.Utils
|
||||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Companies Module</h1>
|
||||
</div>
|
||||
|
||||
|
||||
@Html.Partial("_FooterMenu");
|
||||
@await Html.PartialAsync("_FooterMenu");
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
@using BlueWest.Views.Utils
|
||||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Data Module</h1>
|
||||
</div>
|
||||
|
||||
|
||||
@Html.Partial("_FooterMenu");
|
||||
@await Html.PartialAsync("_FooterMenu");
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
@using BlueWest.Views.Utils
|
||||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Currencies Module</h1>
|
||||
</div>
|
||||
|
||||
|
||||
@await Html.PartialAsync("_FooterMenu");
|
|
@ -1,11 +1,7 @@
|
|||
@using BlueWest.Views.Utils
|
||||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Data Module</h1>
|
||||
</div>
|
||||
|
||||
|
||||
@Html.Partial("_FooterMenu");
|
||||
@await Html.PartialAsync("_FooterMenu");
|
||||
|
|
|
@ -1,14 +1,5 @@
|
|||
@using Microsoft.AspNetCore.Localization
|
||||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
|
||||
}
|
||||
|
||||
|
||||
<div class="text-center">
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Welcome</h1>
|
||||
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
|
||||
|
||||
<table class="table culture-table">
|
||||
<tr>
|
||||
<td>Date</td>
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
@using BlueWest.Views.Utils
|
||||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Login Module</h1>
|
||||
</div>
|
||||
|
||||
|
||||
@Html.Partial("_FooterMenu");
|
|
@ -1,10 +0,0 @@
|
|||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Jobs Module</h1>
|
||||
</div>
|
||||
|
||||
|
||||
@Html.Partial("_FooterMenu");
|
|
@ -1,59 +0,0 @@
|
|||
<footer class="q-footer q-layout__section--marginal absolute-bottom q-footer--bordered text-secondary">
|
||||
<div class="q-tabs row no-wrap items-center q-tabs--not-scrollable q-tabs--horizontal q-tabs__arrows--inside text-white">
|
||||
<div class="q-tabs__content row no-wrap items-center self-stretch hide-scrollbar relative-position q-tabs__content--align-center">
|
||||
<div style="margin-right: 1rem;">
|
||||
<a href="/geo" class="router-link-active router-link-exact-active">
|
||||
<i class="q-icon text-white notranslate material-icons" style="font-size: 24px;">data_usage</i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<a href="/system/roles" class="q-tab relative-position self-stretch flex flex-center text-center q-tab--inactive q-tab--no-caps q-focusable q-hoverable cursor-pointer">
|
||||
|
||||
<div class="q-tab__content self-stretch flex-center relative-position q-anchor--skip non-selectable column">
|
||||
<div class="q-tab__label">Roles</div>
|
||||
</div>
|
||||
<div class="q-tab__indicator absolute-bottom text-transparent">
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="/data/countries"
|
||||
class="q-tab relative-position self-stretch flex flex-center text-center q-tab--inactive q-tab--no-caps q-focusable q-hoverable cursor-pointer">
|
||||
<div class="q-focus-helper"></div>
|
||||
<div class="q-tab__content self-stretch flex-center relative-position q-anchor--skip non-selectable column">
|
||||
<div class="q-tab__label">Sessions</div>
|
||||
</div>
|
||||
<div class="q-tab__indicator absolute-bottom text-transparent"></div>
|
||||
</a><!--<FooterMenuItem>-->
|
||||
<a href="/data/currencies"
|
||||
class="q-tab relative-position self-stretch flex flex-center text-center q-tab--inactive q-tab--no-caps q-focusable q-hoverable cursor-pointer">
|
||||
<div class="q-focus-helper"></div>
|
||||
<div class="q-tab__content self-stretch flex-center relative-position q-anchor--skip non-selectable column">
|
||||
<div class="q-tab__label">Settings</div>
|
||||
</div>
|
||||
<div class="q-tab__indicator absolute-bottom text-transparent"></div>
|
||||
</a><!--<FooterMenuItem>-->
|
||||
<a href="/data/banks"
|
||||
class="q-tab relative-position self-stretch flex flex-center text-center q-tab--inactive q-tab--no-caps q-focusable q-hoverable cursor-pointer">
|
||||
<div class="q-focus-helper"></div>
|
||||
<div class="q-tab__content self-stretch flex-center relative-position q-anchor--skip non-selectable column">
|
||||
<div class="q-tab__label">Banks</div>
|
||||
</div>
|
||||
<div class="q-tab__indicator absolute-bottom text-transparent"></div>
|
||||
</a><!--<FooterMenuItem>-->
|
||||
<!--<FooterMenuItem>-->
|
||||
<button class="q-btn q-btn-item non-selectable no-outline q-btn--flat q-btn--round q-btn--actionable q-focusable q-hoverable q-btn--dense s-7IPF32Wcq3s8"
|
||||
type="button">
|
||||
<span class="q-focus-helper s-7IPF32Wcq3s8"></span>
|
||||
<span
|
||||
class="q-btn__content text-center col items-center q-anchor--skip justify-center row s-7IPF32Wcq3s8">
|
||||
<i
|
||||
class="q-icon notranslate material-icons s-7IPF32Wcq3s8">
|
||||
menu
|
||||
</i>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
|
@ -4,28 +4,33 @@
|
|||
var menu = LayoutCache.GetDefaultFooterMenu(ViewData);
|
||||
}
|
||||
|
||||
<footer class="q-footer q-layout__section--marginal absolute-bottom q-footer--bordered text-secondary">
|
||||
<div class="q-tabs row no-wrap items-center q-tabs--not-scrollable q-tabs--horizontal q-tabs__arrows--inside text-white">
|
||||
<div class="q-tabs__content row no-wrap items-center self-stretch hide-scrollbar relative-position q-tabs__content--align-center">
|
||||
|
||||
<div style="margin-right: 1rem;">
|
||||
<a href="/geo" class="router-link-active router-link-exact-active">
|
||||
<i class="q-icon text-white notranslate material-icons" style="font-size: 24px;">data_usage</i>
|
||||
</a>
|
||||
</div>
|
||||
@if (menu is {Count: > 0 })
|
||||
{
|
||||
@foreach (var record in menu)
|
||||
@if (menu.Count > 0)
|
||||
{
|
||||
<footer class="q-footer q-layout__section--marginal absolute-bottom q-footer--bordered text-secondary">
|
||||
<div class="q-tabs row no-wrap items-center q-tabs--not-scrollable q-tabs--horizontal q-tabs__arrows--inside text-white">
|
||||
<div class="q-tabs__content row no-wrap items-center self-stretch hide-scrollbar relative-position q-tabs__content--align-center">
|
||||
|
||||
<div style="margin-right: 1rem;">
|
||||
<a href="/geo" class="router-link-active router-link-exact-active">
|
||||
<i class="q-icon text-white notranslate material-icons" style="font-size: 24px;">data_usage</i>
|
||||
</a>
|
||||
</div>
|
||||
@if (menu is {Count: > 0 })
|
||||
{
|
||||
<a href="@record.Location" class="q-tab relative-position self-stretch flex flex-center text-center q-tab--inactive q-tab--no-caps q-focusable q-hoverable cursor-pointer">
|
||||
<div class="q-tab__content self-stretch flex-center relative-position q-anchor--skip non-selectable column">
|
||||
<div class="q-tab__label">@record.Name</div>
|
||||
</div>
|
||||
<div class="q-tab__indicator absolute-bottom text-transparent"></div>
|
||||
</a>
|
||||
@foreach (var record in menu)
|
||||
{
|
||||
<a href="@record.Location" class="q-tab relative-position self-stretch flex flex-center text-center q-tab--inactive q-tab--no-caps q-focusable q-hoverable cursor-pointer">
|
||||
<div class="q-tab__content self-stretch flex-center relative-position q-anchor--skip non-selectable column">
|
||||
<div class="q-tab__label">@record.Name</div>
|
||||
</div>
|
||||
<div class="q-tab__indicator absolute-bottom text-transparent"></div>
|
||||
</a>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</footer>
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
@using BlueWest.Views.Utils
|
||||
@using Duende.IdentityServer.Extensions
|
||||
@using BlueWest.Data.Auth
|
||||
@{
|
||||
Layout = null;
|
||||
var menu = LayoutCache.GetDefaultHeaderMenu(ViewData);
|
||||
var userAuthenticated = User.IsAuthenticated();
|
||||
var menu = LayoutCache.GetDefaultHeaderMenu(ViewData, userAuthenticated);
|
||||
var user = ViewData.GetUserViewData();
|
||||
var rootUrl = SessionConstants.CookieDomain;
|
||||
|
||||
}
|
||||
|
||||
<div class="q-tabs items-center row no-wrap items-center q-tabs--not-scrollable q-tabs--horizontal q-tabs__arrows--inside">
|
||||
|
@ -16,8 +22,21 @@
|
|||
</div>
|
||||
<div class="q-tab__indicator absolute-bottom"></div>
|
||||
</a>
|
||||
}
|
||||
}
|
||||
}
|
||||
@if (userAuthenticated && user != null)
|
||||
{
|
||||
<div class="q-chip row inline no-wrap items-center q-chip--dark q-dark">
|
||||
<div class="q-chip__content col row no-wrap items-center q-anchor--skip">
|
||||
<div class="q-avatar">
|
||||
<div class="q-avatar__content row flex-center overflow-hidden">
|
||||
<img src="@rootUrl/static/profile/boy-avatar.png">
|
||||
</div>
|
||||
</div> @user.Email
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
|
@ -1,33 +1,36 @@
|
|||
<!DOCTYPE html>
|
||||
@using BlueWest.Views.Utils
|
||||
@using BlueWest.Data.Auth
|
||||
@{
|
||||
var applicationUser = ViewData.GetUserViewData();
|
||||
var rootUrl = SessionConstants.CookieDomain;
|
||||
}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>@ViewData["Title"] - BlueWest.Views</title>
|
||||
<link rel="stylesheet" href="~/static/main.css"/>
|
||||
<link rel="stylesheet" href="@rootUrl/static/main.css"/>
|
||||
@*
|
||||
<link rel="stylesheet" href="~/BlueWest.Views.styles.css" asp-append-version="true"/>
|
||||
*@
|
||||
<link rel="preload" href="/static/material-icons/web-font/flUhRq6tzZclQEJ-Vdg-IuiaDsNa.woff" as="font" type="font/woff" crossorigin>
|
||||
<link rel="preload" href="/static/material-icons/web-font/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="@rootUrl/static/material-icons/web-font/flUhRq6tzZclQEJ-Vdg-IuiaDsNa.woff" as="font" type="font/woff" crossorigin>
|
||||
<link rel="preload" href="@rootUrl/static/material-icons/web-font/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<style>
|
||||
@@font-face {
|
||||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url("static/material-icons/web-font/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2") format('woff2'), url('static/material-icons/web-font/flUhRq6tzZclQEJ-Vdg-IuiaDsNa.woff') format('woff');
|
||||
src: url("@rootUrl/static/material-icons/web-font/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2") format('woff2'), url('static/material-icons/web-font/flUhRq6tzZclQEJ-Vdg-IuiaDsNa.woff') format('woff');
|
||||
font-display: swap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="desktop touch body--dark" style="--q-primary:#414141;">
|
||||
<div id="q-app">
|
||||
@RenderBody()
|
||||
|
||||
<div class="q-layout q-layout--standard" id="app-entry">
|
||||
|
||||
<header class="q-header q-layout__section--marginal fixed-top bg-primary text-white">
|
||||
@await Html.PartialAsync("_HeaderMenu")
|
||||
@await Html.PartialAsync("_HeaderMenu", applicationUser)
|
||||
</header>
|
||||
|
||||
<div class="q-page-container" style="padding-top: 48px; padding-bottom: 49px;">
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
@using BlueWest.Views.Utils
|
||||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">System Module</h1>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<div class="text-center">
|
||||
<h1 class="display-4">Users</h1>
|
||||
</div>
|
||||
|
||||
@await Html.PartialAsync("_FooterMenu");
|
|
@ -0,0 +1,6 @@
|
|||
<div class="text-center">
|
||||
<h1 class="display-4">Roles Module</h1>
|
||||
</div>
|
||||
|
||||
|
||||
@await Html.PartialAsync("_FooterMenu");
|
|
@ -1,10 +1,7 @@
|
|||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Users</h1>
|
||||
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
|
||||
</div>
|
||||
|
||||
@Html.Partial("~/Views/Shared/UsersMenu.cshtml");
|
||||
|
||||
@await Html.PartialAsync("_FooterMenu");
|
||||
|
|
|
@ -5,14 +5,15 @@
|
|||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"mode": "no-docker",
|
||||
"AllowedHosts": "*",
|
||||
"ConnectionStringDocker": {
|
||||
"MySQL": "server=db;user=blueuser;password=dXjw127124dJ;database=bluedb;",
|
||||
"Redis": "redis://redisinstance:6379"
|
||||
"Redis": "redis://redisinstance:6379",
|
||||
"MySQL": "server=db;user=blueuser;password=dXjw127124dJ;database=bluedb;"
|
||||
},
|
||||
"ConnectionStringNoDocker": {
|
||||
"MySQL": "server=localhost;user=blueuser;password=dXjw127124dJ;database=bluedb;",
|
||||
"Redis": "redis://localhost:6379"
|
||||
"Redis": "redis://localhost:6379",
|
||||
"MySQL": "server=localhost;user=blueuser;password=dXjw127124dJ;database=bluedb;"
|
||||
},
|
||||
"AuthSettings": {
|
||||
"SecretKey": "iJWHDmHLpUA283sqsfhqGbMRdRj1PVkH"
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"mode": "no-docker",
|
||||
"database": "mysql"
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 278 KiB |
|
@ -41,8 +41,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlueWest.Views", "BlueWest.
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlueWest.Domain", "BlueWest.Domain\BlueWest.Domain.csproj", "{1085FF6E-E568-441E-9A2D-23F50AB613AF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlueWest.Razor.Library", "BlueWest.Razor.Library\BlueWest.Razor.Library.csproj", "{CA6DF60F-B33E-4688-A4ED-4427B446E852}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlueWest.Data.Auth", "BlueWest.Data.Auth\BlueWest.Data.Auth.csproj", "{2998FE17-18AD-4888-A696-7F6340F8A543}"
|
||||
EndProject
|
||||
Global
|
||||
|
@ -95,10 +93,6 @@ Global
|
|||
{1085FF6E-E568-441E-9A2D-23F50AB613AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1085FF6E-E568-441E-9A2D-23F50AB613AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1085FF6E-E568-441E-9A2D-23F50AB613AF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CA6DF60F-B33E-4688-A4ED-4427B446E852}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CA6DF60F-B33E-4688-A4ED-4427B446E852}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CA6DF60F-B33E-4688-A4ED-4427B446E852}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CA6DF60F-B33E-4688-A4ED-4427B446E852}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2998FE17-18AD-4888-A696-7F6340F8A543}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2998FE17-18AD-4888-A696-7F6340F8A543}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2998FE17-18AD-4888-A696-7F6340F8A543}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
|
|
@ -4,8 +4,8 @@ services:
|
|||
container_name: BW1_MYSQL
|
||||
image: mysql/mysql-server:8.0
|
||||
environment:
|
||||
MYSQL_ROOT_HOST: localhost
|
||||
MYSQL_USER_HOST: localhost
|
||||
MYSQL_ROOT_HOST: '172.19.0.1'
|
||||
MYSQL_USER_HOST: '172.19.0.1'
|
||||
MYSQL_ROOT_PASSWORD: dXjw127124dJ
|
||||
MYSQL_USER: blueuser
|
||||
MYSQL_PASSWORD: dXjw127124dJ
|
||||
|
@ -14,14 +14,6 @@ services:
|
|||
- "3306:3306"
|
||||
volumes:
|
||||
- ./docker-entrypoint-initdb.d/:/docker-entrypoint-initdb.d/
|
||||
phpmyadmin:
|
||||
container_name: BW_PHPMYADMIN
|
||||
image: phpmyadmin/phpmyadmin
|
||||
ports:
|
||||
- 80:80
|
||||
environment:
|
||||
MYSQL_USERNAME: 'blueuser'
|
||||
MYSQL_ROOT_PASSWORD: 'dXjw127124dJ'
|
||||
redis-instance:
|
||||
image: "redislabs/redismod"
|
||||
ports:
|
||||
|
|
Loading…
Reference in New Issue