Add views project
This commit is contained in:
parent
56a56c6c38
commit
1e63d7e830
|
@ -1,21 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BlueWest\BlueWest.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="obj\Debug\net6.0\apphost.exe" />
|
||||
<Content Include="obj\Debug\net6.0\BlueWest.Api.Gateway.csproj.FileListAbsolute.txt" />
|
||||
<Content Include="obj\rider.project.restore.info" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,18 +0,0 @@
|
|||
FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
|
||||
WORKDIR /app
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
|
||||
WORKDIR /src
|
||||
COPY ["BlueWest.Api.Gateway/BlueWest.Api.Gateway.csproj", "BlueWest.Api.Gateway/"]
|
||||
RUN dotnet restore "BlueWest.Api.Gateway/BlueWest.Api.Gateway.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/BlueWest.Api.Gateway"
|
||||
RUN dotnet build "BlueWest.Api.Gateway.csproj" -c Release -o /app/build
|
||||
|
||||
FROM build AS publish
|
||||
RUN dotnet publish "BlueWest.Api.Gateway.csproj" -c Release -o /app/publish
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "BlueWest.Api.Gateway.dll"]
|
|
@ -1,11 +0,0 @@
|
|||
// See https://aka.ms/new-console-template for more information
|
||||
|
||||
using BlueWest.Core;
|
||||
using BlueWest.Tools;
|
||||
|
||||
EventManager EventManager = new EventManager(new Dictionary<Type, List<EventListenerBase>>(10000));
|
||||
ThreadServer _threadServer;
|
||||
|
||||
System.Threading.Thread.Sleep(2500);
|
||||
_threadServer = new ThreadServer(EventManager);
|
||||
_threadServer.Init();
|
|
@ -32,30 +32,16 @@
|
|||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="Context\Templates\AddToTemplate.csx" />
|
||||
<AdditionalFiles Include="Context\Templates\UpdateTemplate.csx" />
|
||||
<AdditionalFiles Include="Context\Templates\GetOneByTemplate.csx" />
|
||||
<AdditionalFiles Include="Context\Templates\GetManyTemplate.csx" />
|
||||
<AdditionalFiles Include="Context\Templates\GetOneTemplate.csx" />
|
||||
<AdditionalFiles Include="Context\Templates\GetListTemplate.csx" />
|
||||
<AdditionalFiles Include="Context\Templates\AddToListTemplate.csx" />
|
||||
<AdditionalFiles Include="Context\Templates\GetOneFromListTemplate.csx" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BlueWest.Data.Application\BlueWest.Data.Application.csproj" />
|
||||
<ProjectReference Include="..\BlueWest.Data.Capital\BlueWest.Data.Capital.csproj" />
|
||||
<ProjectReference Include="..\BlueWest.Domain\BlueWest.Domain.csproj" />
|
||||
<ProjectReference Include="..\BlueWest\BlueWest.csproj" />
|
||||
<ProjectReference Include="..\include\BlueWest.EfMethods\src\BlueWest.EfMethods\BlueWest.EfMethods.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="obj\rider.project.restore.info" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\include\BlueWest.EfMethods\src\BlueWest.EfMethods\BlueWest.EfMethods.props" />
|
||||
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
namespace BlueWest.WebApi.Configuration
|
||||
{
|
||||
internal class BlueWestConnectionString
|
||||
{
|
||||
public string Redis { get; set; }
|
||||
public string MySql { get; set; }
|
||||
}
|
||||
internal class ConnectionStringDocker : BlueWestConnectionString { }
|
||||
internal class ConnectionStringNoDocker : BlueWestConnectionString { }
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -12,6 +12,8 @@ namespace BlueWest.WebApi.Controllers;
|
|||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
|
||||
[EnableCors(Constants.CorsPolicyName)]
|
||||
[ServiceFilter(typeof(SessionAuthorizationFilter))]
|
||||
|
||||
public class ApplicationController : ControllerBase
|
||||
{
|
||||
[HttpGet]
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using BlueWest.Domain;
|
||||
using BlueWest.Data;
|
||||
using BlueWest.WebApi.Context;
|
||||
using BlueWest.WebApi.Context.Users;
|
||||
|
@ -16,6 +17,7 @@ namespace BlueWest.WebApi.Controllers
|
|||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
|
||||
[EnableCors(Constants.CorsPolicyName)]
|
||||
[ServiceFilter(typeof(SessionAuthorizationFilter))]
|
||||
|
||||
public class ApplicationUserController : ControllerBase
|
||||
{
|
||||
|
|
|
@ -56,7 +56,7 @@ namespace BlueWest.WebApi.Controllers;
|
|||
[HttpPost("login")]
|
||||
public async Task<ActionResult<IdentityResult>> GetSessionToken(LoginRequest loginViewModel)
|
||||
{
|
||||
var (success, sessionToken, identity) = await _authManager.GetSessionTokenId(loginViewModel);
|
||||
var (success, sessionToken, _) = await _authManager.GetSessionTokenIdByLoginRequest(loginViewModel);
|
||||
|
||||
if (success)
|
||||
{
|
||||
|
@ -65,25 +65,6 @@ namespace BlueWest.WebApi.Controllers;
|
|||
}
|
||||
return Problem();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a bearer token
|
||||
/// </summary>
|
||||
/// <param name="loginViewModel"></param>
|
||||
/// <returns></returns>
|
||||
[AllowAnonymous]
|
||||
[HttpPost("bearer")]
|
||||
public async Task<ActionResult<IdentityResult>> GetBearerBySessionId(string sessionId)
|
||||
{
|
||||
var (success, bearer) = await _authManager.GetBearerTokenBySessionTokenId(sessionId);
|
||||
|
||||
if (success)
|
||||
{
|
||||
return Ok(new {bearer});
|
||||
|
||||
}
|
||||
return new UnauthorizedObjectResult(new {message = "The provided sessionId didn't return a valid Token."});
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using BlueWest.Domain;
|
||||
using BlueWest.Data;
|
||||
using BlueWest.WebApi.EF;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
@ -20,6 +14,8 @@ namespace BlueWest.WebApi.Controllers
|
|||
[Route("[controller]")]
|
||||
[Authorize(Policy = SessionConstants.ApiNamePolicy)]
|
||||
[EnableCors(Constants.CorsPolicyName)]
|
||||
[ServiceFilter(typeof(SessionAuthorizationFilter))]
|
||||
|
||||
// [Authorize(Roles = "Administrator")]
|
||||
public class CountryController : ControllerBase
|
||||
{
|
||||
|
@ -38,6 +34,11 @@ namespace BlueWest.WebApi.Controllers
|
|||
|
||||
#endregion
|
||||
|
||||
public override AcceptedAtActionResult AcceptedAtAction(string actionName, string controllerName)
|
||||
{
|
||||
return base.AcceptedAtAction(actionName, controllerName);
|
||||
}
|
||||
|
||||
#region GetCountries
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using BlueWest.Domain;
|
||||
using BlueWest.Data;
|
||||
using BlueWest.WebApi.EF;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
@ -20,6 +17,7 @@ namespace BlueWest.WebApi.Controllers
|
|||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
|
||||
[EnableCors(Constants.CorsPolicyName)]
|
||||
[ServiceFilter(typeof(SessionAuthorizationFilter))]
|
||||
|
||||
// [Authorize(Roles = "Administrator")]
|
||||
public partial class CurrencyController : ControllerBase
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
using BlueWest.Domain;
|
||||
using BlueWest.Data;
|
||||
using BlueWest.WebApi.EF;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
@ -19,6 +19,7 @@ namespace BlueWest.WebApi.Controllers;
|
|||
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
|
||||
//[Authorize(Roles = "Administrator")]
|
||||
[EnableCors(Constants.CorsPolicyName)]
|
||||
[ServiceFilter(typeof(SessionAuthorizationFilter))]
|
||||
|
||||
public class FinanceController : ControllerBase
|
||||
{
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using BlueWest.Domain;
|
||||
using BlueWest.Domain;
|
||||
using BlueWest.Data;
|
||||
using BlueWest.WebApi.EF;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BlueWest.Domain;
|
||||
using BlueWest.Domain;
|
||||
using BlueWest.Tools;
|
||||
using BlueWest.WebApi.Context;
|
||||
using BlueWest.WebApi.EF;
|
||||
|
||||
namespace BlueWest.WebApi.Interfaces
|
||||
{
|
|
@ -0,0 +1,21 @@
|
|||
syntax = "proto3";
|
||||
|
||||
option csharp_namespace = "BlueWest.Streaming";
|
||||
|
||||
package greet;
|
||||
|
||||
// The greeting service definition.
|
||||
service Greeter {
|
||||
// Sends a greeting
|
||||
rpc SayHello (HelloRequest) returns (HelloReply);
|
||||
}
|
||||
|
||||
// The request message containing the user's name.
|
||||
message HelloRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
// The response message containing the greetings.
|
||||
message HelloReply {
|
||||
string message = 1;
|
||||
}
|
|
@ -10,13 +10,6 @@ namespace BlueWest.WebApi
|
|||
/// </summary>
|
||||
public interface ISessionCache : IHostedService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a Bearer By Access Token Id
|
||||
/// </summary>
|
||||
/// <param name="sessionTokenId"></param>
|
||||
/// <returns></returns>
|
||||
Task<string> GetBearerByAccessTokenId(string sessionTokenId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Session Token by Id.
|
||||
/// </summary>
|
||||
|
@ -29,13 +22,28 @@ namespace BlueWest.WebApi
|
|||
/// <param name="token"></param>
|
||||
Task AddSessionToken(SessionToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Check for validity of the session
|
||||
/// </summary>
|
||||
/// <param name="sessionTokenId"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> IsSessionValidAsync(string sessionTokenId);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the session is valid
|
||||
/// </summary>
|
||||
/// <param name="sessionTokenId"></param>
|
||||
/// <returns></returns>
|
||||
bool IsSessionValid(string sessionTokenId);
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Save Cache
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task SaveAsync();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Save Cache
|
||||
/// </summary>
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
namespace BlueWest.WebApi.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Use this filter where an authorized session is need to navigate in the frontend.
|
||||
/// </summary>
|
||||
public class SessionAuthorizationFilter :
|
||||
IAsyncAuthorizationFilter,
|
||||
IAuthorizationFilter
|
||||
{
|
||||
private ISessionCache _sessionCache { get;}
|
||||
/// <summary>
|
||||
/// Session Authorization filter
|
||||
/// </summary>
|
||||
/// <param name="sessionCache"></param>
|
||||
public SessionAuthorizationFilter(ISessionCache sessionCache)
|
||||
{
|
||||
_sessionCache = sessionCache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Empty Attribute Constructor
|
||||
/// </summary>
|
||||
public SessionAuthorizationFilter() { }
|
||||
|
||||
/// <summary>
|
||||
/// First verification executed before each request
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
public void OnAuthorization(AuthorizationFilterContext context)
|
||||
{
|
||||
var sessionToken = context.HttpContext.Request.Headers.GetSessionTokenHeader();
|
||||
|
||||
if (sessionToken == String.Empty)
|
||||
{
|
||||
context.Result = new ForbidResult("Invalid header");
|
||||
}
|
||||
|
||||
var sessionIsValid = _sessionCache.IsSessionValid(sessionToken);
|
||||
|
||||
if (!sessionIsValid)
|
||||
{
|
||||
context.Result = new ForbidResult("Session is not valid.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// First verification executed before each request
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
||||
{
|
||||
var sessionToken = context.HttpContext.Request.Headers.GetSessionTokenHeader();
|
||||
|
||||
if (sessionToken == String.Empty)
|
||||
{
|
||||
context.Result = new ForbidResult("Invalid header");
|
||||
}
|
||||
|
||||
var sessionIsValid = await _sessionCache.IsSessionValidAsync(sessionToken);
|
||||
|
||||
if (!sessionIsValid)
|
||||
{
|
||||
context.Result = new ForbidResult("Session is not valid.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -5,8 +5,10 @@ namespace BlueWest.WebApi
|
|||
internal static class SessionConstants
|
||||
{
|
||||
|
||||
public static TimeSpan DefaultValidForSpan = TimeSpan.FromHours(24);
|
||||
public static TimeSpan DefaultSessionMaxAge = TimeSpan.FromHours(24);
|
||||
public const string ApiNamePolicy = "ApiUser";
|
||||
public const string SessionTokenHeaderName = "x-bw2-auth";
|
||||
public const string CookieDomain = "http://localhost:5173";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace BlueWest.WebApi.Controllers
|
||||
{
|
||||
public static class SessionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Check for the session token header
|
||||
/// </summary>
|
||||
/// <param name="headerDictionary"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetSessionTokenHeader(this IHeaderDictionary headerDictionary)
|
||||
{
|
||||
if (headerDictionary.ContainsKey(SessionConstants.SessionTokenHeaderName))
|
||||
{
|
||||
return headerDictionary[SessionConstants.SessionTokenHeaderName];
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,15 +12,15 @@ namespace BlueWest.WebApi.Session
|
|||
/// <summary>
|
||||
/// Session Provider Context
|
||||
/// </summary>
|
||||
public sealed class SessionDataService : IHostedService, ISessionCache
|
||||
public sealed class SessionManager : ISessionCache
|
||||
{
|
||||
private readonly RedisConnectionProvider _provider;
|
||||
private RedisCollection<SessionToken> _sessionTokens;
|
||||
private readonly RedisCollection<SessionToken> _sessionTokens;
|
||||
/// <summary>
|
||||
/// Index Creation Device
|
||||
/// </summary>
|
||||
/// <param name="provider">Redis connection</param>
|
||||
public SessionDataService(
|
||||
public SessionManager(
|
||||
RedisConnectionProvider provider)
|
||||
{
|
||||
_provider = provider;
|
||||
|
@ -30,7 +30,7 @@ namespace BlueWest.WebApi.Session
|
|||
/// <summary>
|
||||
/// Empty constructor
|
||||
/// </summary>
|
||||
public SessionDataService() { }
|
||||
public SessionManager() { }
|
||||
|
||||
/// <summary>
|
||||
/// Get a session token by the respective Id.
|
||||
|
@ -58,6 +58,7 @@ namespace BlueWest.WebApi.Session
|
|||
await _sessionTokens.SaveAsync();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Save session data
|
||||
/// </summary>
|
||||
|
@ -65,32 +66,52 @@ namespace BlueWest.WebApi.Session
|
|||
{
|
||||
_sessionTokens.Save();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Bearer By Access Token Id
|
||||
/// </summary>
|
||||
/// <param name="sessionTokenId"></param>
|
||||
public async Task<string> GetBearerByAccessTokenId(string sessionTokenId)
|
||||
public async Task<bool> IsSessionValidAsync(string sessionTokenId)
|
||||
{
|
||||
var accessToken = await _sessionTokens.Where(t => t.Id == sessionTokenId)
|
||||
var accessToken = await _sessionTokens
|
||||
.Where(t => t.Id == sessionTokenId)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (accessToken == null) return string.Empty;
|
||||
|
||||
if (accessToken == null) return false;
|
||||
|
||||
if (accessToken.IsValid)
|
||||
{
|
||||
var createdDate = DateTime.UnixEpoch.AddMilliseconds(accessToken.CreatedDate);
|
||||
if (createdDate.AddMilliseconds(accessToken.ValidFor) < DateTime.Now)
|
||||
{
|
||||
accessToken.IsValid = false;
|
||||
}
|
||||
// Check if it's not expired
|
||||
var expirationDate = accessToken.CreatedDate + accessToken.ValidFor;
|
||||
var timeNow = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
if (expirationDate >= timeNow) return accessToken.IsValid;
|
||||
accessToken.IsValid = false;
|
||||
await _sessionTokens.SaveAsync();
|
||||
}
|
||||
|
||||
await _sessionTokens.SaveAsync();
|
||||
|
||||
return accessToken.IsValid ? accessToken.AccessToken : string.Empty;
|
||||
return accessToken.IsValid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a session is valid
|
||||
/// </summary>
|
||||
/// <param name="sessionTokenId"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsSessionValid(string sessionTokenId)
|
||||
{
|
||||
var accessToken = _sessionTokens
|
||||
.FirstOrDefault(t => t.Id == sessionTokenId);
|
||||
|
||||
if (accessToken == null) return false;
|
||||
|
||||
if (!accessToken.IsValid) return accessToken.IsValid;
|
||||
|
||||
accessToken.Validate();
|
||||
_sessionTokens.Save();
|
||||
|
||||
return accessToken.IsValid;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
|
@ -73,7 +73,7 @@ namespace BlueWest.WebApi
|
|||
|
||||
services.AddSession(options =>
|
||||
{
|
||||
options.Cookie.Domain = "http://localhost:5173";
|
||||
options.Cookie.Domain = SessionConstants.CookieDomain;
|
||||
options.Cookie.HttpOnly = true;
|
||||
});
|
||||
services
|
||||
|
@ -161,7 +161,8 @@ namespace BlueWest.WebApi
|
|||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException("config.json doesn't specify a valid database. Use mysql or sqlite.");
|
||||
services.PrepareMySqlDatabasePool(_configuration, _environment, configuration);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BlueWest.Domain;
|
||||
using BlueWest.Domain;
|
||||
using BlueWest.Cryptography;
|
||||
using BlueWest.WebApi.Context;
|
||||
using BlueWest.WebApi.Configuration;
|
||||
using BlueWest.WebApi.Context.Users;
|
||||
using BlueWest.WebApi.EF;
|
||||
using BlueWest.WebApi.Session;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
|
@ -152,8 +153,8 @@ namespace BlueWest.WebApi
|
|||
.AddSingleton(new RedisConnectionProvider(connectionString.Redis))
|
||||
.AddScoped<IJwtTokenHandler, JwtTokenHandler>()
|
||||
.AddScoped<IJwtFactory, JwtFactory>()
|
||||
.AddHostedService<SessionDataService>()
|
||||
.AddSingleton<ISessionCache, SessionDataService>()
|
||||
.AddHostedService<SessionManager>()
|
||||
.AddSingleton<ISessionCache, SessionManager>()
|
||||
.AddScoped<UserRepository>()
|
||||
.AddScoped<IUserManager, ApplicationUserManager>()
|
||||
.AddScoped<IAuthManager, AuthManager>()
|
||||
|
@ -211,7 +212,7 @@ namespace BlueWest.WebApi
|
|||
{
|
||||
options.Cookie.SameSite = SameSiteMode.Lax;
|
||||
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
|
||||
options.Cookie.MaxAge = TimeSpan.FromDays(1);
|
||||
options.Cookie.MaxAge = SessionConstants.DefaultSessionMaxAge;
|
||||
options.LoginPath = "/api/auth/logincookie";
|
||||
options.LogoutPath = "/api/auth/logout";
|
||||
})
|
||||
|
@ -231,7 +232,7 @@ namespace BlueWest.WebApi
|
|||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -272,11 +273,3 @@ namespace BlueWest.WebApi
|
|||
}
|
||||
|
||||
|
||||
internal class BlueWestConnectionString
|
||||
{
|
||||
public string Redis { get; set; }
|
||||
public string MySql { get; set; }
|
||||
}
|
||||
internal class ConnectionStringDocker : BlueWestConnectionString { }
|
||||
internal class ConnectionStringNoDocker : BlueWestConnectionString { }
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using BlueWest.Data.Application;
|
||||
|
||||
namespace BlueWest.WebApi.Context.Users;
|
||||
|
||||
public static class AuthConsts
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper object to return a negative callback
|
||||
/// </summary>
|
||||
public static (bool, SessionTokenUnique, ClaimsIdentity) NegativeToken => (false, null, null);
|
||||
|
||||
public static (bool, SessionTokenUnique, ClaimsIdentity) OkAuth(SessionTokenUnique sessionTokenUnique, ClaimsIdentity claimsIdentity, bool success = true) => (success, sessionTokenUnique, claimsIdentity);
|
||||
|
||||
|
||||
}
|
|
@ -3,9 +3,9 @@ using System.Security.Claims;
|
|||
using System.Threading.Tasks;
|
||||
using BlueWest.Cryptography;
|
||||
using BlueWest.Data.Application;
|
||||
using Duende.IdentityServer.Extensions;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using static BlueWest.WebApi.Context.Users.AuthConsts;
|
||||
|
||||
namespace BlueWest.WebApi.Context.Users
|
||||
{
|
||||
|
@ -40,8 +40,7 @@ namespace BlueWest.WebApi.Context.Users
|
|||
var uuid = loginRequest.GetUuid();
|
||||
var hashUuid = GetHashFromUuid(uuid);
|
||||
var sessionToken = await _sessionCache.GetSessionTokenByIdAsync(hashUuid);
|
||||
if (sessionToken != null) return sessionToken;
|
||||
return null;
|
||||
return sessionToken;
|
||||
}
|
||||
|
||||
private string GetHashFromUuid(string uuid)
|
||||
|
@ -51,61 +50,58 @@ namespace BlueWest.WebApi.Context.Users
|
|||
|
||||
private SessionToken GetNewSessionToken(LoginRequest loginRequest, ApplicationUser user, string token)
|
||||
{
|
||||
long unixTime = ((DateTimeOffset)DateTimeOffset.Now).ToUnixTimeMilliseconds();;
|
||||
long timeNow = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
|
||||
var newToken = new SessionToken
|
||||
{
|
||||
Id = GetHashFromUuid(loginRequest.GetUuid()),
|
||||
UserId = user.Id,
|
||||
CreatedDate = unixTime,
|
||||
CreatedDate = timeNow,
|
||||
IsValid = true,
|
||||
ValidFor = SessionConstants.DefaultSessionMaxAge.Milliseconds,
|
||||
AccessToken = token
|
||||
};
|
||||
|
||||
return newToken;
|
||||
}
|
||||
|
||||
public bool SessionTokenIsValid(SessionToken token)
|
||||
{
|
||||
var nowMilliseconds = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
var hasChanges = token.Validate();
|
||||
|
||||
var isExpired = token.CreatedDate + token.ValidFor > nowMilliseconds;
|
||||
|
||||
if (isExpired)
|
||||
if (hasChanges)
|
||||
{
|
||||
token.IsValid = false;
|
||||
_sessionCache.SaveAsync();
|
||||
}
|
||||
|
||||
return token.IsValid;
|
||||
}
|
||||
|
||||
public async Task<(bool, string, ClaimsIdentity)> GetSessionTokenId(LoginRequest loginRequest)
|
||||
public async Task<(bool, SessionTokenUnique, ClaimsIdentity)> GetSessionTokenIdByLoginRequest(LoginRequest loginRequest)
|
||||
{
|
||||
var user = await _userManager.FindByEmailAsync(loginRequest.Email);
|
||||
|
||||
if (user == null) return (false, string.Empty, null);
|
||||
if (user == null) return NegativeToken;
|
||||
|
||||
if (!await _userManager.CheckPasswordAsync(user, loginRequest.Password)) return (false, string.Empty, null);
|
||||
if (!await _userManager.CheckPasswordAsync(user, loginRequest.Password)) return NegativeToken;
|
||||
|
||||
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);
|
||||
identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));
|
||||
|
||||
// Session
|
||||
var sessionToken = await GetSessionToken(loginRequest);
|
||||
|
||||
if (sessionToken != null)
|
||||
if (sessionToken == null || !SessionTokenIsValid(sessionToken))
|
||||
{
|
||||
if (SessionTokenIsValid(sessionToken))
|
||||
{
|
||||
return (true, sessionToken.Id, identity);
|
||||
}
|
||||
var (success, bearerToken) = await GenerateBearerToken(identity, user);
|
||||
var newSessionToken = GetNewSessionToken(loginRequest, user, bearerToken);
|
||||
await _sessionCache.AddSessionToken(newSessionToken);
|
||||
var tokenUnique = new SessionTokenUnique(newSessionToken);
|
||||
|
||||
return OkAuth(tokenUnique, identity, success);
|
||||
}
|
||||
|
||||
var (success, bearerToken) = await GenerateBearerToken(identity, user);
|
||||
|
||||
var newSessionToken = GetNewSessionToken(loginRequest, user, bearerToken);
|
||||
await _sessionCache.AddSessionToken(newSessionToken);
|
||||
|
||||
return (success, newSessionToken.Id, identity);
|
||||
var response = new SessionTokenUnique(sessionToken);
|
||||
return OkAuth(response, identity);
|
||||
}
|
||||
|
||||
private async Task<(bool, string)> GenerateBearerToken(ClaimsIdentity identity, ApplicationUser user)
|
||||
|
@ -117,39 +113,18 @@ namespace BlueWest.WebApi.Context.Users
|
|||
}
|
||||
|
||||
|
||||
public async Task<(bool, string)> GetBearerTokenBySessionTokenId(string sessionId)
|
||||
{
|
||||
if (sessionId.IsNullOrEmpty()) return (false, string.Empty);
|
||||
var bearer = await _sessionCache.GetBearerByAccessTokenId(sessionId);
|
||||
return (bearer != string.Empty, bearer);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> VerifyLoginByEmailAsync(string email, string password)
|
||||
{
|
||||
var user = await _userManager.FindByEmailAsync(email);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return await _userManager.CheckPasswordAsync(user, password);
|
||||
return user != null && await _userManager.CheckPasswordAsync(user, password);
|
||||
}
|
||||
|
||||
private RegisterViewModel FromSignupToUser(RegisterViewModel signupDto)
|
||||
{
|
||||
var pwd = signupDto.Password;
|
||||
var hash = _hasher.CreateHash(pwd, BaseCryptoItem.HashAlgorithm.SHA3_512);
|
||||
signupDto.Password = hash;
|
||||
return signupDto;
|
||||
}
|
||||
|
||||
public async Task<IdentityResult> CreateUserAsync(RegisterViewModel userSignupDto)
|
||||
{
|
||||
RegisterViewModel userToCreate = FromSignupToUser(userSignupDto);
|
||||
return await _userManager.CreateAsync(userToCreate.ToUser());
|
||||
userSignupDto.Password = _hasher.CreateHash(userSignupDto.Password, BaseCryptoItem.HashAlgorithm.SHA3_512);;
|
||||
var newUser = userSignupDto.ToUser();
|
||||
return await _userManager.CreateAsync(newUser);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -39,7 +39,7 @@ internal class JwtIssuerOptions
|
|||
/// <summary>
|
||||
/// Set the timespan the token will be valid for (default is 120 min)
|
||||
/// </summary>
|
||||
public TimeSpan ValidFor { get; set; } = SessionConstants.DefaultValidForSpan;
|
||||
public TimeSpan ValidFor { get; set; } = SessionConstants.DefaultSessionMaxAge;
|
||||
|
||||
/// <summary>
|
||||
/// "jti" (JWT ID) Claim (default ID is a GUID)
|
||||
|
|
|
@ -23,13 +23,7 @@ public interface IAuthManager
|
|||
/// </summary>
|
||||
/// <param name="loginRequest"></param>
|
||||
/// <returns></returns>
|
||||
public Task<(bool, string, ClaimsIdentity)> GetSessionTokenId(LoginRequest loginRequest);
|
||||
public Task<(bool, SessionTokenUnique, ClaimsIdentity)> GetSessionTokenIdByLoginRequest(LoginRequest loginRequest);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a valid bearer token by the session id
|
||||
/// </summary>
|
||||
/// <param name="sessionId"></param>
|
||||
/// <returns></returns>
|
||||
Task<(bool, string)> GetBearerTokenBySessionTokenId(string sessionId);
|
||||
|
||||
}
|
|
@ -2,15 +2,7 @@ namespace BlueWest.WebApi.Context.Users;
|
|||
|
||||
public static class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// AdminRoleName
|
||||
/// </summary>
|
||||
public const string AdminRoleName = "Admin";
|
||||
/// <summary>
|
||||
/// UserRoleName
|
||||
/// </summary>
|
||||
public const string UserRoleName = "User";
|
||||
public const string ExpectatorRoleName = "Expectator";
|
||||
|
||||
/// <summary>
|
||||
/// JwtClaimIdentifiers
|
||||
/// </summary>
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BlueWest.WebApi.Context.Users;
|
||||
|
||||
public class RoleManager : RoleManager<ApplicationRole>
|
||||
{
|
||||
public RoleManager(
|
||||
IRoleStore<ApplicationRole> store,
|
||||
IEnumerable<IRoleValidator<ApplicationRole>> roleValidators,
|
||||
ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors,
|
||||
ILogger<RoleManager<ApplicationRole>> logger) : base(store, roleValidators, keyNormalizer, errors, logger)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BlueWest.Domain;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
"MySQL": "server=localhost;user=blueuser;password=dXjw127124dJ;database=bluedb;",
|
||||
"Redis": "redis://localhost:6379"
|
||||
},
|
||||
"REDIS_CONNECTION_STRING": "redis://redis:6379",
|
||||
"AuthSettings": {
|
||||
"SecretKey": "iJWHDmHLpUA283sqsfhqGbMRdRj1PVkH"
|
||||
},
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"mode": "no-docker",
|
||||
"database": "sqlite",
|
||||
"environment": "dev"
|
||||
"database": "mysql"
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
**/.dockerignore
|
||||
**/.env
|
||||
**/.git
|
||||
**/.gitignore
|
||||
**/.project
|
||||
**/.settings
|
||||
**/.toolstarget
|
||||
**/.vs
|
||||
**/.vscode
|
||||
**/.idea
|
||||
**/*.*proj.user
|
||||
**/*.dbmdl
|
||||
**/*.jfm
|
||||
**/azds.yaml
|
||||
**/bin
|
||||
**/charts
|
||||
**/docker-compose*
|
||||
**/Dockerfile*
|
||||
**/node_modules
|
||||
**/npm-debug.log
|
||||
**/obj
|
||||
**/secrets.dev.yaml
|
||||
**/values.dev.yaml
|
||||
LICENSE
|
||||
README.md
|
|
@ -1,18 +0,0 @@
|
|||
FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
|
||||
WORKDIR /app
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
|
||||
WORKDIR /src
|
||||
COPY ["BlueWest.Console/BlueWest.Console.csproj", "BlueWest.Console/"]
|
||||
RUN dotnet restore "BlueWest.Console/BlueWest.Console.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/BlueWest.Console"
|
||||
RUN dotnet build "BlueWest.Console.csproj" -c Release -o /app/build
|
||||
|
||||
FROM build AS publish
|
||||
RUN dotnet publish "BlueWest.Console.csproj" -c Release -o /app/publish
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "BlueWest.Console.dll"]
|
|
@ -1,25 +0,0 @@
|
|||
using System.Globalization;
|
||||
|
||||
/*var s = CultureInfo.GetCultures(CultureTypes.AllCultures);
|
||||
|
||||
foreach (var culture in s)
|
||||
{
|
||||
var actualCulture = culture;
|
||||
Console.WriteLine(actualCulture);
|
||||
|
||||
}*/
|
||||
|
||||
async Task TaskReceiver(Func<Task> task)
|
||||
{
|
||||
Console.WriteLine("Hello");
|
||||
await task.Invoke();
|
||||
}
|
||||
|
||||
async Task Task2()
|
||||
{
|
||||
Console.WriteLine("World");
|
||||
}
|
||||
|
||||
var f = Task2;
|
||||
|
||||
await TaskReceiver(f);
|
|
@ -11,6 +11,8 @@ namespace BlueWest.WebApi.Context.Users
|
|||
Token = token;
|
||||
ExpiresIn = expiresIn;
|
||||
}
|
||||
|
||||
void Validate() {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace BlueWest.Data.Application
|
|||
[IgnoreMemberMapTo]
|
||||
[Indexed] public string Id { get; set; }
|
||||
|
||||
[Indexed] public int ValidFor { get; set;}
|
||||
[Indexed] public long ValidFor { get; set;}
|
||||
|
||||
[Indexed] public bool IsValid { get; set; }
|
||||
|
||||
|
@ -25,6 +25,18 @@ namespace BlueWest.Data.Application
|
|||
[Indexed] public string AccessToken { get; set; }
|
||||
|
||||
|
||||
public bool Validate()
|
||||
{
|
||||
var expirationDate = CreatedDate + ValidFor;
|
||||
var timeNow = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
if (expirationDate < timeNow)
|
||||
{
|
||||
IsValid = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace BlueWest.Data.Application
|
|||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public int ValidFor { get; set;}
|
||||
public long ValidFor { get; set;}
|
||||
public long CreatedDate { get; set; }
|
||||
public string UserId { get; set; }
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace BlueWest.Data.Geography
|
|||
public double Latitude { get; set; }
|
||||
public double Longitude { get; set; }
|
||||
public Country Country { get; set; }
|
||||
public TimeSpan CreatedTime { get; set; }
|
||||
public long CreatedTime { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BlueWest.Data.Capital\BlueWest.Data.Capital.csproj" />
|
||||
<ProjectReference Include="..\include\BlueWest.MapTo\src\BlueWest.MapTo\BlueWest.MapTo.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="obj\rider.project.restore.info" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,11 +1,10 @@
|
|||
using BlueWest.Data;
|
||||
using BlueWest.EfMethods;
|
||||
using BlueWest.Domain.Model;
|
||||
using BlueWest.WebApi.Context.Users;
|
||||
using BlueWest.WebApi.EF.Model;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace BlueWest.WebApi.Context
|
||||
namespace BlueWest.Domain
|
||||
{
|
||||
/// <summary>
|
||||
/// Application User Db Context
|
|
@ -0,0 +1,60 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
<RootNamespace>BlueWest.Domain</RootNamespace>
|
||||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PublishDependencyDocumentationFiles>true</PublishDependencyDocumentationFiles>
|
||||
<AnalysisLevel>preview</AnalysisLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Grpc.AspNetCore" Version="2.49.0-pre1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.ApiAuthorization.IdentityServer" Version="6.0.8" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="6.0.8" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authorization.Policy" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.8">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.8">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.2-mauipre.1.22102.15" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="6.0.2-mauipre.1.22054.8" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BlueWest.Data.Application\BlueWest.Data.Application.csproj" />
|
||||
<ProjectReference Include="..\BlueWest.Data.Capital\BlueWest.Data.Capital.csproj" />
|
||||
<ProjectReference Include="..\include\BlueWest.EfMethods\src\BlueWest.EfMethods\BlueWest.EfMethods.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="obj\rider.project.restore.info" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="Templates\AddToListTemplate.csx" />
|
||||
<AdditionalFiles Include="Templates\AddToTemplate.csx" />
|
||||
<AdditionalFiles Include="Templates\GetListTemplate.csx" />
|
||||
<AdditionalFiles Include="Templates\GetManyTemplate.csx" />
|
||||
<AdditionalFiles Include="Templates\GetOneByTemplate.csx" />
|
||||
<AdditionalFiles Include="Templates\GetOneFromListTemplate.csx" />
|
||||
<AdditionalFiles Include="Templates\GetOneTemplate.csx" />
|
||||
<AdditionalFiles Include="Templates\UpdateTemplate.csx" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\include\BlueWest.EfMethods\src\BlueWest.EfMethods\BlueWest.EfMethods.props" />
|
||||
|
||||
|
||||
</Project>
|
|
@ -1,10 +1,10 @@
|
|||
|
||||
using BlueWest.Data;
|
||||
using BlueWest.WebApi.EF.Model;
|
||||
using BlueWest.Domain.Model;
|
||||
using BlueWest.EfMethods;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace BlueWest.WebApi.EF
|
||||
namespace BlueWest.Domain
|
||||
{
|
||||
/// <summary>
|
||||
/// Context for accessing company data
|
|
@ -1,11 +1,11 @@
|
|||
using BlueWest.Data;
|
||||
using BlueWest.WebApi.EF.Model;
|
||||
using BlueWest.EfMethods;
|
||||
using BlueWest.WebApi.Context.Extensions;
|
||||
using BlueWest.Domain.Model;
|
||||
using BlueWest.Domain.Extensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using BlueWest.EfMethods;
|
||||
|
||||
namespace BlueWest.WebApi.EF
|
||||
namespace BlueWest.Domain
|
||||
{
|
||||
|
||||
/// <summary>
|
|
@ -2,7 +2,7 @@ using System.Collections.Generic;
|
|||
using BlueWest.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace BlueWest.WebApi.Context.Extensions
|
||||
namespace BlueWest.Domain.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Code first model builder
|
|
@ -3,7 +3,7 @@ using BlueWest.Data.Application;
|
|||
using BlueWest.WebApi.Context.Users;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace BlueWest.WebApi.EF.Model
|
||||
namespace BlueWest.Domain.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Database model configuration extensions
|
|
@ -1,8 +1,8 @@
|
|||
using BlueWest.Data;
|
||||
using BlueWest.WebApi.EF.Model;
|
||||
using BlueWest.Domain.Model;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace BlueWest.WebApi.EF
|
||||
namespace BlueWest.Domain
|
||||
{
|
||||
/// <summary>
|
||||
/// Finance operations table
|
|
@ -1,10 +1,9 @@
|
|||
using BlueWest.Data;
|
||||
using BlueWest.WebApi.Context.Users;
|
||||
using BlueWest.WebApi.EF.Model;
|
||||
using BlueWest.Domain.Model;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace BlueWest.WebApi.EF
|
||||
namespace BlueWest.Domain
|
||||
{
|
||||
|
||||
/// <summary>
|
|
@ -1,9 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
|
@ -1,8 +0,0 @@
|
|||
namespace BlueWest.Posts
|
||||
{
|
||||
public class PostType
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<RootNamespace>BlueWest.Views</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BlueWest.Domain\BlueWest.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,24 @@
|
|||
using System.Diagnostics;
|
||||
using BlueWest.Views.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BlueWest.Views.Controllers
|
||||
{
|
||||
public class CapitalController : Controller
|
||||
{
|
||||
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error()
|
||||
{
|
||||
return View(new ErrorViewModel {RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using BlueWest.Views.Models;
|
||||
|
||||
namespace BlueWest.Views.Controllers;
|
||||
|
||||
public class HomeController : Controller
|
||||
{
|
||||
private readonly ILogger<HomeController> _logger;
|
||||
|
||||
public HomeController(ILogger<HomeController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult Privacy()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error()
|
||||
{
|
||||
return View(new ErrorViewModel {RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BlueWest.Views.Controllers
|
||||
{
|
||||
public class SystemController : Controller
|
||||
{
|
||||
private readonly ILogger<SystemController> _logger;
|
||||
|
||||
public SystemController(ILogger<SystemController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult Users()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult Roles()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
|
||||
WORKDIR /app
|
||||
EXPOSE 80
|
||||
EXPOSE 443
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
|
||||
WORKDIR /src
|
||||
COPY ["BlueWest.Views/BlueWest.Views.csproj", "BlueWest.Views/"]
|
||||
RUN dotnet restore "BlueWest.Views/BlueWest.Views.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/BlueWest.Views"
|
||||
RUN dotnet build "BlueWest.Views.csproj" -c Release -o /app/build
|
||||
|
||||
FROM build AS publish
|
||||
RUN dotnet publish "BlueWest.Views.csproj" -c Release -o /app/publish
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "BlueWest.Views.dll"]
|
|
@ -0,0 +1,8 @@
|
|||
namespace BlueWest.Views.Models;
|
||||
|
||||
public class ErrorViewModel
|
||||
{
|
||||
public string? RequestId { get; set; }
|
||||
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace BlueWest.Views.Models
|
||||
{
|
||||
public class SystemPageModel
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace BlueWest.Views.Models
|
||||
{
|
||||
public class UserPageModel : PageModel
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddControllersWithViews();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller=Home}/{action=Index}/{id?}");
|
||||
|
||||
app.Run();
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:51856",
|
||||
"sslPort": 44359
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"BlueWest.Views": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:7022;http://localhost:5204",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Capital</h1>
|
||||
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
|
||||
</div>
|
|
@ -0,0 +1,8 @@
|
|||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<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>
|
||||
</div>
|
|
@ -0,0 +1,6 @@
|
|||
@{
|
||||
ViewData["Title"] = "Privacy Policy";
|
||||
}
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
|
||||
<p>Use this page to detail your site's privacy policy.</p>
|
|
@ -0,0 +1,25 @@
|
|||
@model ErrorViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Error";
|
||||
}
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if (Model.ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@Model.RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
|
@ -0,0 +1 @@
|
|||
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">BlueWest.Views</a>
|
|
@ -0,0 +1,74 @@
|
|||
<!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="~/BlueWest.Views.styles.css" asp-append-version="true"/>
|
||||
|
||||
<style>
|
||||
|
||||
@@font-face {
|
||||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
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');
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body class="desktop touch body--dark" style="--q-primary:#414141;">
|
||||
<div id="q-app">
|
||||
|
||||
|
||||
<div class="wrap s-7IPF32Wcq3s8"><ul class="_toastContainer s-lp0GEKiD9Hz6">
|
||||
</ul></div>
|
||||
<div class="q-layout q-layout--standard s-7IPF32Wcq3s8" id="app-entry"><header class="q-header q-layout__section--marginal fixed-top bg-primary text-white s-7IPF32Wcq3s8"><div class="q-tabs row no-wrap items-center q-tabs--not-scrollable q-tabs--horizontal q-tabs__arrows--inside s-7IPF32Wcq3s8"><div class="q-tabs__content row no-wrap items-center self-stretch hide-scrollbar relative-position q-tabs__content--align-left s-7IPF32Wcq3s8"><div></div>
|
||||
<a class="q-tab relative-position self-stretch flex flex-center text-center q-tab--inactive q-focusable q-hoverable cursor-pointer " href="/system"><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">System</div></div>
|
||||
<div class="q-tab__indicator absolute-bottom"></div></a><div></div>
|
||||
<a class="q-tab relative-position self-stretch flex flex-center text-center q-tab--inactive q-focusable q-hoverable cursor-pointer " href="/capital"><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">Capital</div></div>
|
||||
<div class="q-tab__indicator absolute-bottom"></div></a></div>
|
||||
</div>
|
||||
<div class="q-layout__shadow absolute-full overflow-hidden no-pointer-events s-7IPF32Wcq3s8"></div></header>
|
||||
<div class="q-drawer-container s-7IPF32Wcq3s8"><div class="q-drawer__opener fixed-right s-7IPF32Wcq3s8"></div>
|
||||
<div class="fullscreen q-drawer__backdrop hidden s-7IPF32Wcq3s8" style="background-color: rgba(0, 0, 0, 0);"></div>
|
||||
<aside class="q-drawer q-drawer--right q-drawer--bordered q-drawer--dark q-dark q-layout--prevent-focus fixed q-drawer--on-top q-drawer--mobile q-drawer--top-padding s-7IPF32Wcq3s8" style="width: 300px; transform: translateX(300px);"><div show-if="" class="q-drawer__content fit scroll s-7IPF32Wcq3s8"></div></aside></div>
|
||||
|
||||
<div class="q-page-container s-7IPF32Wcq3s8" style="padding-top: 48px; padding-bottom: 49px;">
|
||||
@RenderBody()
|
||||
<footer class="q-footer q-layout__section--marginal absolute-bottom q-footer--bordered text-secondary s-7IPF32Wcq3s8">
|
||||
<div class="q-tabs row no-wrap items-center q-tabs--not-scrollable q-tabs--horizontal q-tabs__arrows--inside text-white s-7IPF32Wcq3s8">
|
||||
<div class="q-tabs__content row no-wrap items-center self-stretch hide-scrollbar relative-position q-tabs__content--align-center s-7IPF32Wcq3s8">
|
||||
<div style="margin-right: 1rem;" class="s-7IPF32Wcq3s8"><a href="#/geo" class="router-link-active router-link-exact-active s-7IPF32Wcq3s8"><i class="q-icon text-white notranslate material-icons s-7IPF32Wcq3s8" style="font-size: 24px;">data_usage</i></a></div>
|
||||
<a href="/system" 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">System</div></div>
|
||||
<div class="q-tab__indicator absolute-bottom text-transparent"></div></a>
|
||||
<a href="/system/users" 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">Users</div></div>
|
||||
<div class="q-tab__indicator absolute-bottom text-transparent"></div></a>
|
||||
<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-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">Roles</div></div>
|
||||
<div class="q-tab__indicator absolute-bottom text-transparent"></div></a>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
.material-icons {
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
word-wrap: normal;
|
||||
white-space: nowrap;
|
||||
direction: ltr;
|
||||
|
||||
/* Support for all WebKit browsers. */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
/* Support for Safari and Chrome. */
|
||||
text-rendering: optimizeLegibility;
|
||||
|
||||
/* Support for Firefox. */
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
/* Support for IE. */
|
||||
font-feature-settings: 'liga';
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
|
||||
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
|
|
@ -0,0 +1,8 @@
|
|||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">System</h1>
|
||||
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
|
||||
</div>
|
|
@ -0,0 +1,8 @@
|
|||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Roles</h1>
|
||||
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
|
||||
</div>
|
|
@ -0,0 +1,8 @@
|
|||
@{
|
||||
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>
|
|
@ -0,0 +1,3 @@
|
|||
@using BlueWest.Views
|
||||
@using BlueWest.Views.Models
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
|
@ -0,0 +1,3 @@
|
|||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
html {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin-bottom: 60px;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
|
@ -0,0 +1,4 @@
|
|||
// Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
||||
// for details on configuring this project to bundle and minify static web assets.
|
||||
|
||||
// Write your JavaScript code.
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2011-2021 Twitter, Inc.
|
||||
Copyright (c) 2011-2021 The Bootstrap Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,427 @@
|
|||
/*!
|
||||
* Bootstrap Reboot v5.1.0 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2021 The Bootstrap Authors
|
||||
* Copyright 2011-2021 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
:root {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: var(--bs-body-font-family);
|
||||
font-size: var(--bs-body-font-size);
|
||||
font-weight: var(--bs-body-font-weight);
|
||||
line-height: var(--bs-body-line-height);
|
||||
color: var(--bs-body-color);
|
||||
text-align: var(--bs-body-text-align);
|
||||
background-color: var(--bs-body-bg);
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 1rem 0;
|
||||
color: inherit;
|
||||
background-color: currentColor;
|
||||
border: 0;
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
hr:not([size]) {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
h6, h5, h4, h3, h2, h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: calc(1.375rem + 1.5vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: calc(1.325rem + 0.9vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: calc(1.3rem + 0.6vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h3 {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h4 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title],
|
||||
abbr[data-bs-original-title] {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
-webkit-text-decoration-skip-ink: none;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
mark {
|
||||
padding: 0.2em;
|
||||
background-color: #fcf8e3;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 0.75em;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0d6efd;
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
color: #0a58ca;
|
||||
}
|
||||
|
||||
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-size: 1em;
|
||||
direction: ltr /* rtl:ignore */;
|
||||
unicode-bidi: bidi-override;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
pre code {
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 0.875em;
|
||||
color: #d63384;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
a > code {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
kbd {
|
||||
padding: 0.2rem 0.4rem;
|
||||
font-size: 0.875em;
|
||||
color: #fff;
|
||||
background-color: #212529;
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
kbd kbd {
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img,
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
color: #6c757d;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
text-align: -webkit-match-parent;
|
||||
}
|
||||
|
||||
thead,
|
||||
tbody,
|
||||
tfoot,
|
||||
tr,
|
||||
td,
|
||||
th {
|
||||
border-color: inherit;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus:not(:focus-visible) {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
[role=button] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
select:disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[list]::-webkit-calendar-picker-indicator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
button,
|
||||
[type=button],
|
||||
[type=reset],
|
||||
[type=submit] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
button:not(:disabled),
|
||||
[type=button]:not(:disabled),
|
||||
[type=reset]:not(:disabled),
|
||||
[type=submit]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
float: left;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
line-height: inherit;
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
legend {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
legend + * {
|
||||
clear: left;
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit-fields-wrapper,
|
||||
::-webkit-datetime-edit-text,
|
||||
::-webkit-datetime-edit-minute,
|
||||
::-webkit-datetime-edit-hour-field,
|
||||
::-webkit-datetime-edit-day-field,
|
||||
::-webkit-datetime-edit-month-field,
|
||||
::-webkit-datetime-edit-year-field {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-inner-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type=search] {
|
||||
outline-offset: -2px;
|
||||
-webkit-appearance: textfield;
|
||||
}
|
||||
|
||||
/* rtl:raw:
|
||||
[type="tel"],
|
||||
[type="url"],
|
||||
[type="email"],
|
||||
[type="number"] {
|
||||
direction: ltr;
|
||||
}
|
||||
*/
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::file-selector-button {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue