Initial version
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace EonaCat.gRPC.Api.Converters;
|
||||
|
||||
public class TrimStringConverter : System.Text.Json.Serialization.JsonConverter<string>
|
||||
{
|
||||
public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
=> reader.GetString()?.Trim();
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
|
||||
=> writer.WriteStringValue(value);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Bogus" Version="35.6.5" />
|
||||
<PackageReference Include="EonaCat.LogStack" Version="0.0.8" />
|
||||
<PackageReference Include="Grpc.AspNetCore" Version="2.80.0" />
|
||||
<PackageReference Include="Grpc.AspNetCore.Server.Reflection" Version="2.80.0" />
|
||||
<PackageReference Include="Grpc.Core" Version="2.46.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Grpc.JsonTranscoding" Version="10.0.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Grpc.Swagger" Version="0.10.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.9">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.19.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\EonaCat.gRPC.Proto\EonaCat.gRPC.Proto.csproj" />
|
||||
<ProjectReference Include="..\EonaCat.gRPC.Service\EonaCat.gRPC.Service.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,11 @@
|
||||
global using EonaCat.gRPC.Api.Helpers;
|
||||
global using EonaCat.gRPC.Api.Middleware.Interceptors;
|
||||
global using EonaCat.gRPC.Api.Services;
|
||||
global using Grpc.Core;
|
||||
global using Grpc.Core.Interceptors;
|
||||
global using Microsoft.Data.SqlClient;
|
||||
global using Microsoft.OpenApi.Models;
|
||||
global using EonaCat.gRPC.Core.Entities;
|
||||
global using EonaCat.gRPC.Core.Interfaces.Repositories;
|
||||
global using Microsoft.EntityFrameworkCore;
|
||||
global using EonaCat.Json;
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace EonaCat.gRPC.Api.Helpers;
|
||||
|
||||
public class AppSettings
|
||||
{
|
||||
public string Secret { get; set; } = null!;
|
||||
public int Validity { get; set; }
|
||||
public string Issuer { get; set; } = null!;
|
||||
public string Audience { get; set; } = null!;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace EonaCat.gRPC.Api.Helpers
|
||||
{
|
||||
public class CustomException : Exception
|
||||
{
|
||||
public CustomException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
namespace EonaCat.gRPC.Api.Helpers;
|
||||
|
||||
public static class CustomMapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Proto to Entity/DTO And Reverse
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <typeparam name="TDestination"></typeparam>
|
||||
/// <param name="src"></param>
|
||||
/// <returns>TDestination</returns>
|
||||
public static TDestination Map<TSource, TDestination>(TSource src) where TDestination : new()
|
||||
{
|
||||
try
|
||||
{
|
||||
var tDest = (TDestination)Activator.CreateInstance(typeof(TDestination))!;
|
||||
if (src == null)
|
||||
return tDest;
|
||||
var srcClassType = src.GetType();
|
||||
var srcProperties = srcClassType.GetProperties();
|
||||
foreach (var srcProperty in srcProperties)
|
||||
{
|
||||
var destPropertyInfo = tDest.GetType().GetProperty(srcProperty.Name);
|
||||
if (srcProperty.GetType() == destPropertyInfo?.GetType())
|
||||
destPropertyInfo.SetValue(tDest, srcProperty.GetValue(src, null), null);
|
||||
}
|
||||
return tDest;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception($"Unsupported mapping.\n{e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using ProtoBuf.Grpc;
|
||||
|
||||
namespace EonaCat.gRPC.Api.Helpers;
|
||||
|
||||
public static class ExceptionHelpers
|
||||
{
|
||||
public static RpcException Handle<T>(this Exception exception, ServerCallContext context, ILogger<T> logger, Guid correlationId) =>
|
||||
exception switch
|
||||
{
|
||||
TimeoutException timeoutException => HandleTimeoutException(timeoutException, context, logger, correlationId),
|
||||
SqlException sqlException => HandleSqlException(sqlException, context, logger, correlationId),
|
||||
RpcException rpcException => HandleRpcException(rpcException, logger, correlationId),
|
||||
_ => HandleDefault(exception, context, logger, correlationId)
|
||||
};
|
||||
|
||||
private static RpcException HandleTimeoutException<T>(TimeoutException exception, ServerCallContext context, ILogger<T> logger, Guid correlationId)
|
||||
{
|
||||
logger.LogError(exception, $"CorrelationId: {correlationId} - A timeout occurred");
|
||||
var status = new Status(StatusCode.Internal, "An external resource did not answer within the time limit");
|
||||
return new RpcException(status, CreateTrailers(correlationId));
|
||||
}
|
||||
|
||||
private static RpcException HandleSqlException<T>(SqlException exception, ServerCallContext context, ILogger<T> logger, Guid correlationId)
|
||||
{
|
||||
logger.LogError(exception, $"CorrelationId: {correlationId} - An SQL error occurred");
|
||||
Status status;
|
||||
if (exception.Number == -2)
|
||||
status = new Status(StatusCode.DeadlineExceeded, "SQL timeout");
|
||||
else
|
||||
status = new Status(StatusCode.Internal, "SQL error");
|
||||
return new RpcException(status, CreateTrailers(correlationId));
|
||||
}
|
||||
|
||||
private static RpcException HandleRpcException<T>(RpcException exception, ILogger<T> logger, Guid correlationId)
|
||||
{
|
||||
logger.LogError(exception, $"CorrelationId: {correlationId} - An error occurred");
|
||||
//var trailers = exception.Trailers;
|
||||
//var d = CreateTrailers(correlationId);
|
||||
//trailers.Add(d[0]);
|
||||
return new RpcException(new Status(exception.StatusCode, exception.Message), CreateTrailers(correlationId));
|
||||
}
|
||||
|
||||
private static RpcException HandleDefault<T>(Exception exception, ServerCallContext context, ILogger<T> logger, Guid correlationId)
|
||||
{
|
||||
logger.LogError(exception, $"CorrelationId: {correlationId} - An error occurred");
|
||||
return new RpcException(new Status(StatusCode.Internal, exception.Message), CreateTrailers(correlationId));
|
||||
}
|
||||
|
||||
private static Metadata CreateTrailers(Guid correlationId)
|
||||
{
|
||||
var trailers = new Metadata { { "CorrelationId", correlationId.ToString() } };
|
||||
return trailers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using EonaCat.gRPC.Core.Interfaces.Services;
|
||||
using EonaCat.gRPC.Repository;
|
||||
using EonaCat.gRPC.Repository.Base;
|
||||
using EonaCat.gRPC.Repository.DatabaseContext;
|
||||
using EonaCat.gRPC.Service;
|
||||
using System.Text;
|
||||
using EonaCat.LogStack.Extensions;
|
||||
using EonaCat.Mapper;
|
||||
|
||||
namespace EonaCat.gRPC.Api.Helpers;
|
||||
|
||||
public static class Extension
|
||||
{
|
||||
public static void AddInfrastructureServices(this WebApplicationBuilder builder)
|
||||
{
|
||||
RegisterSwagger(builder);
|
||||
RegisterLogger(builder);
|
||||
RegisterDatabaseContext(builder);
|
||||
RegisterAuthentication(builder);
|
||||
}
|
||||
public static void AddBusinessServices(this WebApplicationBuilder builder)
|
||||
{
|
||||
RegisterRepositoryDependencies(builder.Services);
|
||||
RegisterServiceDependencies(builder);
|
||||
}
|
||||
|
||||
public static void RegisterServiceDependencies(WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.Configure<AppSettings>(builder.Configuration.GetSection("Jwt"));
|
||||
builder.Services.AddTransient<IUserService, UserService>();
|
||||
}
|
||||
|
||||
private static void RegisterRepositoryDependencies(IServiceCollection services)
|
||||
{
|
||||
services.AddScoped(typeof(IBaseRepository<>), typeof(BaseRepository<>));
|
||||
services.AddScoped<IUserRepository, UserRepository>();
|
||||
}
|
||||
|
||||
private static void RegisterAuthentication(WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
}).AddJwtBearer(options =>
|
||||
{
|
||||
options.RequireHttpsMetadata = true;
|
||||
options.SaveToken = true;
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(builder.Configuration.GetSection("Jwt").GetSection("Secret").Value)),
|
||||
ValidateIssuer = false,
|
||||
ValidateAudience = false,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private static void RegisterDatabaseContext(WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.AddDbContext<AppDbContext>((provider, options) =>
|
||||
{
|
||||
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
|
||||
});
|
||||
}
|
||||
|
||||
private static void RegisterLogger(WebApplicationBuilder builder)
|
||||
{
|
||||
builder.AddEonaCatLogging((x) => x
|
||||
.AddFlow(new LogStack.Flows.ConsoleFlow())
|
||||
.AddFlow(new LogStack.Flows.FileFlow("./logs")));
|
||||
}
|
||||
|
||||
private static void RegisterSwagger(WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.AddSwaggerGen(c =>
|
||||
{
|
||||
c.SwaggerDoc("v1", new OpenApiInfo
|
||||
{
|
||||
Title = "EonaCat gRPC API",
|
||||
Version = "v1",
|
||||
Description = "EonaCat gRPC API"
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static void AppUseSwagger(this WebApplication app)
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI(options =>
|
||||
{
|
||||
options.SwaggerEndpoint("/swagger/v1/swagger.json", "EonaCat gRPC API");
|
||||
options.RoutePrefix = string.Empty;
|
||||
});
|
||||
}
|
||||
|
||||
public static void MapGrpcServices(this WebApplication app)
|
||||
{
|
||||
app.MapGrpcService<UserHandler>();
|
||||
app.MapGrpcService<AuthenticationHandler>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using EonaCat.gRPC.Proto;
|
||||
|
||||
namespace EonaCat.gRPC.Api.Helpers;
|
||||
|
||||
public static class JwtAuthenticationManager
|
||||
{
|
||||
public static AuthenticationResponse Authenticate(IOptions<AppSettings> appSettings, AuthenticationRequest authenticationRequest)
|
||||
{
|
||||
// Implement DbCheck ----
|
||||
if (authenticationRequest.UserName != "admin" || authenticationRequest.Password != "admin")
|
||||
throw new RpcException(new Status(StatusCode.Unauthenticated, "Invalid ProtoUserResponse Credentials"));
|
||||
|
||||
var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
|
||||
var tokenKey = Encoding.ASCII.GetBytes(appSettings.Value.Secret);
|
||||
var tokenExpiryDateTime = DateTime.UtcNow.AddMinutes(appSettings.Value.Validity);
|
||||
var securityTokenDescriptor = new SecurityTokenDescriptor()
|
||||
{
|
||||
Subject = new ClaimsIdentity(new List<Claim>
|
||||
{
|
||||
new(ClaimTypes.Name, authenticationRequest.UserName),
|
||||
new(ClaimTypes.Role, "Administrator")
|
||||
}),
|
||||
Expires = tokenExpiryDateTime,
|
||||
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(tokenKey), SecurityAlgorithms.HmacSha256Signature)
|
||||
};
|
||||
var securityToken = jwtSecurityTokenHandler.CreateToken(securityTokenDescriptor);
|
||||
var token = jwtSecurityTokenHandler.WriteToken(securityToken);
|
||||
|
||||
return new AuthenticationResponse
|
||||
{
|
||||
AccessToken = token,
|
||||
ExpiresIn = (int)tokenExpiryDateTime.Subtract(DateTime.UtcNow).TotalSeconds
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using EonaCat.Json.Converters;
|
||||
using EonaCat.Json.Serialization;
|
||||
using System.Reflection;
|
||||
|
||||
namespace EonaCat.gRPC.Api.Helpers;
|
||||
|
||||
public class TimeStampContractResolver : DefaultContractResolver
|
||||
{
|
||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||
{
|
||||
var property = base.CreateProperty(member, memberSerialization);
|
||||
if (property.PropertyType == typeof(Google.Protobuf.WellKnownTypes.Timestamp))
|
||||
{
|
||||
property.Converter = new TimeStampConverter();
|
||||
}
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
public class TimeStampConverter : DateTimeConverterBase
|
||||
{
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
|
||||
JsonSerializer serializer)
|
||||
{
|
||||
var date = DateTime.Parse(reader.Value?.ToString());
|
||||
date = DateTime.SpecifyKind(date, DateTimeKind.Utc);
|
||||
return Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(date);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
writer.WriteValue(((Google.Protobuf.WellKnownTypes.Timestamp)value).ToString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using EonaCat.gRPC.Api.Helpers;
|
||||
|
||||
namespace EonaCat.gRPC.Api.Middleware.Interceptors;
|
||||
|
||||
public class ExceptionInterceptor : Interceptor
|
||||
{
|
||||
private readonly ILogger<ExceptionInterceptor> _logger;
|
||||
private readonly Guid _correlationId;
|
||||
|
||||
public ExceptionInterceptor(ILogger<ExceptionInterceptor> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_correlationId = Guid.NewGuid();
|
||||
}
|
||||
|
||||
public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
|
||||
TRequest request,
|
||||
ServerCallContext context,
|
||||
UnaryServerMethod<TRequest, TResponse> continuation)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await continuation(request, context);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw e.Handle(context, _logger, _correlationId);
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task<TResponse> ClientStreamingServerHandler<TRequest, TResponse>(
|
||||
IAsyncStreamReader<TRequest> requestStream,
|
||||
ServerCallContext context,
|
||||
ClientStreamingServerMethod<TRequest, TResponse> continuation)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await continuation(requestStream, context);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw e.Handle(context, _logger, _correlationId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override async Task ServerStreamingServerHandler<TRequest, TResponse>(
|
||||
TRequest request,
|
||||
IServerStreamWriter<TResponse> responseStream,
|
||||
ServerCallContext context,
|
||||
ServerStreamingServerMethod<TRequest, TResponse> continuation)
|
||||
{
|
||||
try
|
||||
{
|
||||
await continuation(request, responseStream, context);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw e.Handle(context, _logger, _correlationId);
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task DuplexStreamingServerHandler<TRequest, TResponse>(
|
||||
IAsyncStreamReader<TRequest> requestStream,
|
||||
IServerStreamWriter<TResponse> responseStream,
|
||||
ServerCallContext context,
|
||||
DuplexStreamingServerMethod<TRequest, TResponse> continuation)
|
||||
{
|
||||
try
|
||||
{
|
||||
await continuation(requestStream, responseStream, context);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw e.Handle(context, _logger, _correlationId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
namespace EonaCat.gRPC.Api.Middleware.Interceptors;
|
||||
|
||||
public class LoggerInterceptor : Interceptor
|
||||
{
|
||||
private readonly ILogger<LoggerInterceptor> _logger;
|
||||
|
||||
public LoggerInterceptor(ILogger<LoggerInterceptor> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
|
||||
TRequest request,
|
||||
ServerCallContext context,
|
||||
UnaryServerMethod<TRequest, TResponse> continuation)
|
||||
{
|
||||
LogCall(context);
|
||||
try
|
||||
{
|
||||
return await continuation(request, context);
|
||||
}
|
||||
catch (SqlException e)
|
||||
{
|
||||
_logger.LogError(e, $"An SQL error occurred when calling {context.Method}");
|
||||
Status status = e.Number == -2
|
||||
? new Status(StatusCode.DeadlineExceeded, $"SQL timeout: {e.Message}")
|
||||
: new Status(StatusCode.Internal, $"SQL error: {e.Message}");
|
||||
|
||||
throw new RpcException(status, e.Message);
|
||||
}
|
||||
catch (RpcException e)
|
||||
{
|
||||
_logger.LogError(e, $"gRPC error when calling {context.Method}: {e.Status.Detail}");
|
||||
throw;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, $"An error occurred when calling {context.Method}");
|
||||
throw new RpcException(Status.DefaultCancelled, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void LogCall(ServerCallContext context)
|
||||
{
|
||||
var httpContext = context.GetHttpContext();
|
||||
_logger.LogDebug($"Starting call. Request: {httpContext.Request.Path}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using ProtoBuf.Grpc.Server;
|
||||
using EonaCat.gRPC.Api.Helpers;
|
||||
using EonaCat.gRPC.Api.Middleware.Interceptors;
|
||||
using CompressionLevel = System.IO.Compression.CompressionLevel;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Additional configuration is required to successfully run gRPC on macOS.
|
||||
// For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
|
||||
|
||||
// Start: gRPC Configurations
|
||||
builder.Services.AddGrpc(options =>
|
||||
{
|
||||
options.Interceptors.Add<LoggerInterceptor>();
|
||||
options.Interceptors.Add<ExceptionInterceptor>();
|
||||
});
|
||||
builder.Services.AddGrpcReflection();
|
||||
builder.Services.AddGrpc().AddJsonTranscoding();
|
||||
builder.Services.AddGrpcSwagger();
|
||||
builder.Services.AddCodeFirstGrpc(config =>
|
||||
{
|
||||
config.ResponseCompressionLevel = CompressionLevel.Optimal;
|
||||
});
|
||||
//builder.Services.TryAddSingleton(BinderConfiguration.Create(
|
||||
// binder: new ServiceBinderWithServiceResolutionFromServiceCollection(builder.Services)));
|
||||
//builder.Services.AddCodeFirstGrpcReflection();
|
||||
// End: gRPC Configurations
|
||||
|
||||
builder.AddInfrastructureServices();
|
||||
builder.AddBusinessServices();
|
||||
builder.Services.AddAuthorization();
|
||||
|
||||
|
||||
var app = builder.Build();
|
||||
// Configure the HTTP request pipeline.
|
||||
app.AppUseSwagger();
|
||||
app.MapGrpcReflectionService();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapGrpcServices();
|
||||
|
||||
app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
|
||||
app.Run();
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "http://localhost:5227",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:7166;http://localhost:5227",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using ProtoBuf.Grpc;
|
||||
using EonaCat.gRPC.Api.Helpers;
|
||||
using EonaCat.gRPC.Proto;
|
||||
|
||||
namespace EonaCat.gRPC.Api.Services;
|
||||
|
||||
public class AuthenticationHandler : IAuthenticationService
|
||||
{
|
||||
private readonly IOptions<AppSettings> _appSettings;
|
||||
|
||||
public AuthenticationHandler(IOptions<AppSettings> appSettings)
|
||||
{
|
||||
_appSettings = appSettings;
|
||||
}
|
||||
|
||||
public Task<AuthenticationResponse> Authenticate(AuthenticationRequest request, CallContext context = default)
|
||||
{
|
||||
var authenticationResponse = JwtAuthenticationManager.Authenticate(_appSettings, request);
|
||||
if (authenticationResponse == null)
|
||||
throw new RpcException(new Status(StatusCode.Unauthenticated, "Invalid ProtoUserResponse Credentials"));
|
||||
return Task.FromResult(authenticationResponse);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using EonaCat.gRPC.Core.Interfaces.Services;
|
||||
using EonaCat.gRPC.Proto;
|
||||
using EonaCat.Mapper;
|
||||
|
||||
namespace EonaCat.gRPC.Api.Services;
|
||||
|
||||
public class UserHandler : IProtoUserService
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public UserHandler(IUserService userService)
|
||||
{
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
public async ValueTask<BaseResponse<string>> Create(UserCreateRequest userCreateRequest)
|
||||
{
|
||||
var user = CustomMapper.Map<UserCreateRequest, UserEntity>(userCreateRequest);
|
||||
var response = await _userService.CreateUser(user);
|
||||
return BaseResponse<string>.Created(response.ToString());
|
||||
}
|
||||
|
||||
public async Task<BaseResponse<List<UserResponse>?>> GetAsync()
|
||||
{
|
||||
var users = await _userService.GetAsync();
|
||||
var mapped = CustomMapper.Map<List<UserEntity>, List<UserResponse>>(users.ToList());
|
||||
return BaseResponse<List<UserResponse>?>.Ok(mapped);
|
||||
}
|
||||
|
||||
public async Task<BaseResponse<UserResponse?>> GetByIdAsync(string id)
|
||||
{
|
||||
if (string.IsNullOrEmpty(id) || !long.TryParse(id, out _))
|
||||
return BaseResponse<UserResponse?>.Failed(null, message: "Invalid ID: It is either empty or not a valid long.");
|
||||
var user = await _userService.GetAsync(Convert.ToInt64(id));
|
||||
var mapped = CustomMapper.Map<UserEntity, UserResponse>(user);
|
||||
return BaseResponse<UserResponse>.Ok(mapped);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Server=EONACAT_TESTMACHINE;Database=EonaCatgRPC;User ID=sa;Password=mytestpassword;Trusted_Connection=True;TrustServerCertificate=True;MultipleActiveResultSets=true"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Server=YOUR_SERVER;User Id=YOUR_USER_ID; Password=YOUR_PASSWORD; Database=YOUR_DATABASE_NAME"
|
||||
},
|
||||
"Jwt": {
|
||||
"Secret": "ertwet3245sgf2342werwergww4352345",
|
||||
"Issuer": "https://localhost:7166/",
|
||||
"Audience": "https://localhost:7166/",
|
||||
"Validity": 30
|
||||
},
|
||||
"Kestrel": {
|
||||
"EndpointDefaults": {
|
||||
"Protocols": "Http2"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user