add configs

master
Amir Hossein Khademi 2023-10-10 18:01:21 +03:30
parent be609347ed
commit 07e19e7118
26 changed files with 1639 additions and 11 deletions

View File

@ -0,0 +1,56 @@
{
"ConnectionStrings": {
"Postgres": "User ID=postgres;Password=root;Host=localhost;Port=5432;Database=iGarsonDB;",
"PostgresServer": "Host=pg-0,pg-1;Username=igarsonAgent;Password=xHTpBf4wC+bBeNg2pL6Ga7VEWKFJx7VPEUpqxwPFfOc2YYTVwFQuHfsiqoVeT9+6;Database=BrizcoDB;Load Balance Hosts=true;Target Session Attributes=primary;Application Name=iGLS"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "None",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.SignalR": "Debug",
"Microsoft.AspNetCore.Http.Connections": "Debug"
}
},
"SiteSettings": {
"BaseUrl": "http://localhost:32769",
"UserSetting": {
"Username": "Root",
"Email": "info@brizco.io",
"Password": "root1234",
"Phone": "09211111111",
"RoleName": "RootAdmin",
"FirstName": "همه کاره",
"LastName": "سیستم"
},
"JwtSettings": {
"SecretKey": "pg8mt74/bk5yx2mr23Zvsu/81Z2czAycEo9ewcm34AndD8SFDXGqBiYv_YaHosseinYaAli_ABOOOOOOOOOLFAZL_BIMEH_JAD_NASABE_YA_GHARIBAL_GHORABA_@@@@_06/0CZWyAqy2H6Xpjp0npg8mt74/bk5yx2mr23Zvsu/81Z2czAycEo9ewcm34AndD8SFDXGqBiYvACLB0dED9vjy+h5sK1BnB30=",
"Issuer": "Brizco",
"Audience": "Brizco",
"ExpireAddDay": "15"
}
},
"IpRateLimiting": {
"EnableEndpointRateLimiting": false,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"IpWhitelist": [ "127.0.0.1", "::1/10", "192.168.0.0/24" ],
"EndpointWhitelist": [ "get:/api/license", "*:/api/status" ],
"ClientWhitelist": [ "dev-id-1", "dev-id-2" ],
"GeneralRules": [
{
"Endpoint": "*",
"Period": "1m",
"Limit": 60
},
{
"Endpoint": "*",
"Period": "15m",
"Limit": 250
}
]
},
"AllowedHosts": "*"
}

View File

@ -0,0 +1,56 @@
{
"ConnectionStrings": {
"Postgres": "User ID=postgres;Password=root;Host=localhost;Port=5432;Database=iGarsonDB;",
"PostgresServer": "Host=pg-0,pg-1;Username=igarsonAgent;Password=xHTpBf4wC+bBeNg2pL6Ga7VEWKFJx7VPEUpqxwPFfOc2YYTVwFQuHfsiqoVeT9+6;Database=BrizcoDB;Load Balance Hosts=true;Target Session Attributes=primary;Application Name=iGLS",
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "None",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.SignalR": "Debug",
"Microsoft.AspNetCore.Http.Connections": "Debug"
}
},
"SiteSettings": {
"BaseUrl": "http://localhost:32769",
"UserSetting": {
"Username": "root",
"Email": "info@documed.ir",
"Password": "i1nLGN86rU/HQgzC",
"Phone": "09211111111",
"RoleName": "RootAdmin",
"FirstName": "همه کاره",
"LastName": "سیستم"
},
"JwtSettings": {
"SecretKey": "YaHosseinYaAli_pg8mt74/bk5yx2mr23Zvsu/81Z2czAycEo9ewcm34AndD8SFDXGqBiYv_ABOOOOOOOOOLFAZL_BIMEH_JAD_NASABE_pg8mt74/bk5yx2mr23Zvsu/81Z2czAycEo9ewcm34AndD8SFDXGqBiYv_YA_GHARIBAL_GHORABA_@@@@_06/0CZWyAqy2H6Xpjp0npg8mt74/bk5yx2mr23Zvsu/81Z2czAycEo9ewcm34AndD8SFDXGqBiYvACLB0dED9vjy+h5sK1BnB30=",
"Issuer": "Brizco",
"Audience": "Brizco",
"ExpireAddDay": "15"
}
},
"IpRateLimiting": {
"EnableEndpointRateLimiting": false,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"IpWhitelist": [ "127.0.0.1", "::1/10", "192.168.0.0/24" ],
"EndpointWhitelist": [ "get:/api/license", "*:/api/status" ],
"ClientWhitelist": [ "dev-id-1", "dev-id-2" ],
"GeneralRules": [
{
"Endpoint": "*",
"Period": "1m",
"Limit": 60
},
{
"Endpoint": "*",
"Period": "15m",
"Limit": 250
}
]
},
"AllowedHosts": "*"
}

View File

@ -53,4 +53,39 @@
<ProjectReference Include="..\DocuMed.Infrastructure\DocuMed.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="Autofac" />
<Using Include="Autofac.Extensions.DependencyInjection" />
<Using Include="Carter" />
<Using Include="DocuMed.Api.WebFramework.Configurations" />
<Using Include="DocuMed.Api.WebFramework.Swagger" />
<Using Include="DocuMed.Common.Extensions" />
<Using Include="DocuMed.Common.Models" />
<Using Include="DocuMed.Common.Models.Api" />
<Using Include="DocuMed.Common.Models.Entity" />
<Using Include="DocuMed.Common.Models.Exception" />
<Using Include="DocuMed.Common.Models.Mapper" />
<Using Include="DocuMed.Core" />
<Using Include="DocuMed.Core.Models.Api" />
<Using Include="DocuMed.Domain" />
<Using Include="DocuMed.Domain.Entities.User" />
<Using Include="DocuMed.Domain.Models.Settings" />
<Using Include="DocuMed.Infrastructure" />
<Using Include="DocuMed.Infrastructure.Models" />
<Using Include="DocuMed.Repository" />
<Using Include="DocuMed.Repository.Abstracts" />
<Using Include="DocuMed.Repository.Extensions" />
<Using Include="DocuMed.Repository.Models" />
<Using Include="DocuMed.Repository.Repositories.Base.Contracts" />
<Using Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
<Using Include="Microsoft.AspNetCore.Identity" />
<Using Include="Microsoft.AspNetCore.Mvc" />
<Using Include="Microsoft.AspNetCore.Mvc.Filters" />
<Using Include="Microsoft.Extensions.Options" />
<Using Include="Serilog" />
<Using Include="Serilog.Events" />
<Using Include="Serilog.Sinks.SystemConsole.Themes" />
<Using Include="System.Security.Claims" />
</ItemGroup>
</Project>

View File

@ -1,23 +1,96 @@
using DocuMed.Api.WebFramework.MiddleWares;
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.UseSerilog();
LoggerConfig.ConfigureSerilog();
string env = builder.Environment.IsDevelopment() == true ? "Development" : "Production";
builder.Host.UseContentRoot(Directory.GetCurrentDirectory());
if (builder.Environment.IsDevelopment())
builder.Configuration
.AddJsonFile($"AppSettings/appsettings.json")
.AddJsonFile($"AppSettings/appsettings.{env}.json");
if (builder.Environment.IsProduction())
builder.Configuration
.AddJsonFile($"AppSettings/Production/appsettings.{env}.json");
var configuration = builder.Configuration;
var siteSetting = configuration.GetSection(nameof(SiteSettings)).Get<SiteSettings>();
builder.Services.Configure<SiteSettings>(configuration.GetSection(nameof(SiteSettings)));
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddCustomSwagger(siteSetting.BaseUrl);
builder.Services.AddCustomApiVersioning();
builder.Services.AddCustomController();
builder.Services.AddControllers();
builder.Services.AddCustomResponseCompression();
builder.Services.AddCustomMvc();
builder.Services.AddCustomAuthorization();
builder.Services.AddJwtCustomAuthentication(siteSetting.JwtSettings);
builder.Services.AddCustomIdentity();
builder.Services.AddCustomDbContext(configuration);
builder.Services.AddCarter();
builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
{
var assembly = typeof(CoreConfig).Assembly;
builder
.RegisterAssemblyTypes(assembly)
.AssignableTo<IScopedDependency>()
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
var assemblyB = typeof(InfrastructureConfig).Assembly;
builder.RegisterAssemblyTypes(assemblyB)
.AssignableTo<IScopedDependency>()
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
var assemblyC = typeof(RepositoryConfig).Assembly;
builder.RegisterAssemblyTypes(assemblyC)
.AssignableTo<IScopedDependency>()
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
var assemblyD = typeof(Program).Assembly;
builder.RegisterAssemblyTypes(assemblyD)
.AssignableTo<IScopedDependency>()
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
app.UseCustomSwagger(siteSetting.BaseUrl);
//app.UseSwagger();
//app.UseSwaggerUI();
}
app.UseAuthorization();
app.UseAuthentication();
app.UseCors(x => x
.SetIsOriginAllowed(origin => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
app.UseExceptionHandlerMiddleware();
app.MapCarter();
app.UseStaticFiles();
await app.InitialDb();
app.MapControllers();
app.Run();

View File

@ -0,0 +1,15 @@
namespace DocuMed.Api.Services;
public class CurrentUserService : ICurrentUserService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public CurrentUserService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public string? UserId => _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);
public string? RoleName => _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.Role);
public string? UserName => _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.Name);
}

View File

@ -0,0 +1,74 @@
namespace DocuMed.Api.WebFramework.Bases;
public class ApiResultFactory
{
}
public class ApiResultFilterAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is OkObjectResult okObjectResult)
{
var apiResult = new ApiResult<object>(true, ApiResultStatusCode.Success, okObjectResult.Value);
context.Result = new JsonResult(apiResult) { StatusCode = okObjectResult.StatusCode };
}
else if (context.Result is OkResult okResult)
{
var apiResult = new ApiResult(true, ApiResultStatusCode.Success);
context.Result = new JsonResult(apiResult) { StatusCode = okResult.StatusCode };
}
else if (context.Result is BadRequestResult badRequestResult)
{
var apiResult = new ApiResult(false, ApiResultStatusCode.BadRequest);
context.Result = new JsonResult(apiResult) { StatusCode = badRequestResult.StatusCode };
}
else if (context.Result is BadRequestObjectResult badRequestObjectResult)
{
var message = badRequestObjectResult.Value.ToString();
if (badRequestObjectResult.Value is SerializableError errors)
{
var errorMessages = errors.SelectMany(p => (string[])p.Value).Distinct();
message = string.Join(" | ", errorMessages);
}
if (badRequestObjectResult.Value is ValidationProblemDetails problemDetails)
{
var errorMessages = problemDetails.Errors.Values.SelectMany(v => v);
message = string.Join(" | ", errorMessages);
}
var apiResult = new ApiResult(false, ApiResultStatusCode.BadRequest, message);
context.Result = new JsonResult(apiResult) { StatusCode = badRequestObjectResult.StatusCode };
}
else if (context.Result is ContentResult contentResult)
{
var apiResult = new ApiResult(true, ApiResultStatusCode.Success, contentResult.Content);
context.Result = new JsonResult(apiResult) { StatusCode = contentResult.StatusCode };
}
else if (context.Result is NotFoundResult notFoundResult)
{
var apiResult = new ApiResult(false, ApiResultStatusCode.NotFound);
context.Result = new JsonResult(apiResult) { StatusCode = notFoundResult.StatusCode };
}
else if (context.Result is NotFoundObjectResult notFoundObjectResult)
{
var apiResult = new ApiResult<object>(false, ApiResultStatusCode.NotFound, notFoundObjectResult.Value);
context.Result = new JsonResult(apiResult) { StatusCode = notFoundObjectResult.StatusCode };
}
else if (context.Result is ObjectResult objectResult && objectResult.StatusCode == null
&& !(objectResult.Value is ApiResult))
{
var apiResult = new ApiResult<object>(true, ApiResultStatusCode.Success, objectResult.Value);
context.Result = new JsonResult(apiResult) { StatusCode = objectResult.StatusCode };
}
else if (context.Result is ObjectResult objectResultBad && objectResultBad.Value is ApiResult)
{
var apiResult = objectResultBad.Value as ApiResult;
context.Result = new JsonResult(apiResult) { StatusCode = objectResultBad.StatusCode };
}
base.OnResultExecuting(context);
}
}

View File

@ -0,0 +1,33 @@
using System.Net;
using Microsoft.AspNetCore.Authorization;
namespace DocuMed.Api.WebFramework.Bases;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class ClaimRequirement : AuthorizeAttribute, IAuthorizationFilter
{
private readonly string _claimsType;
private readonly string _claimsValue;
public ClaimRequirement(string type,string value)
{
type = value;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var user = context.HttpContext.User;
var permissions = user.Claims?.Where(c => c.Type == _claimsType)?.ToList();
if (permissions == null)
{
context.Result = new StatusCodeResult((int)HttpStatusCode.Forbidden);
}
else
{
bool isAccepted = permissions.FirstOrDefault(p => p.Value == _claimsValue) != null;
if (!isAccepted)
context.Result = new StatusCodeResult((int)HttpStatusCode.Forbidden);
}
}
}

View File

@ -0,0 +1,224 @@
using System.Linq.Expressions;
using Mapster;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
namespace DocuMed.Api.WebFramework.Bases;
public class CrudEndpoint<TEntity,TGetAllQuery,TGetOneQuery,TCreateCommand,TUpdateCommand,TDeleteCommand> where TEntity : ApiEntity, new()
{
private readonly string _endpointName;
public CrudEndpoint(string endpointName)
{
_endpointName = endpointName;
}
public virtual void AddRoutes(IEndpointRouteBuilder app)
{
var group = app.NewVersionedApi(_endpointName).MapGroup($"api/{_endpointName}");
group.MapGet("", GetAllAsync)
.WithDisplayName("GetAll")
.HasApiVersion(1.0);
group.MapGet("{id}", GetAsync)
.WithName("GetOne")
.HasApiVersion(1.0);
group.MapPost("", Post)
.HasApiVersion(1.0);
group.MapPut("", Put)
.HasApiVersion(1.0);
group.MapDelete("", Delete)
.HasApiVersion(1.0);
}
// GET:Get All Entity
public virtual async Task<IResult> GetAllAsync(ISender sender , CancellationToken cancellationToken)
{
var res = sender.Send(Activator.CreateInstance<TGetAllQuery>(),cancellationToken);
return TypedResults.Ok(res);
}
// GET:Get An Entity By Id
public async Task<IResult> GetAsync(Guid id, ISender sender, CancellationToken cancellationToken)
=> TypedResults.Ok(sender.Send(Activator.CreateInstance<TGetOneQuery>()));
// POST:Create Entity
public virtual async Task<IResult> Post([FromBody] TCreateCommand ent , ISender mediator , CancellationToken cancellationToken)
{
return TypedResults.Ok(await mediator.Send(ent, cancellationToken));
}
// PUT:Update Entity
public virtual async Task<IResult> Put([FromBody] TEntity ent , IRepositoryWrapper _repositoryWrapper, CancellationToken cancellationToken)
{
_repositoryWrapper.SetRepository<TEntity>().Update(ent);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
return TypedResults.Ok();
}
// DELETE:Delete Entity
public virtual async Task<IResult> Delete(Guid id,IRepositoryWrapper _repositoryWrapper, CancellationToken cancellationToken)
{
var ent = await _repositoryWrapper.SetRepository<TEntity>().GetByIdAsync(cancellationToken, id);
_repositoryWrapper.SetRepository<TEntity>().Delete(ent);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
return TypedResults.Ok();
}
}
[ApiController]
//[AllowAnonymous]
[ApiResultFilter]
[Route("api/v{version:apiVersion}/[controller]")] // api/v1/[controller]
public class BaseController : ControllerBase
{
//public UserRepository UserRepository { get; set; } => property injection
public bool UserIsAutheticated => HttpContext.User.Identity.IsAuthenticated;
}
[Authorize(AuthenticationSchemes = "Bearer")]
public class CrudController<TDto, TEntity> : BaseController
where TDto : BaseDto<TDto, TEntity>, new()
where TEntity : ApiEntity, new()
{
protected readonly IRepositoryWrapper _repositoryWrapper;
public CrudController(IRepositoryWrapper repositoryWrapper)
{
_repositoryWrapper = repositoryWrapper;
}
// GET:Get All Entity
[HttpGet]
public virtual async Task<IActionResult> GetAllAsync(CancellationToken cancellationToken)
{
var projectTo = typeof(TDto).BaseType?.GetProperty("ProjectToDto")?.GetValue(null, null);
if (projectTo != null)
{
var exprss = projectTo as Expression<Func<TEntity, TDto>>;
var entites = await _repositoryWrapper
.SetRepository<TEntity>()
.TableNoTracking
.Select(exprss)
.ToListAsync(cancellationToken);
return Ok(entites);
}
throw new BaseApiException("ProjectTo Not Found");
}
// GET:Get An Entity By Id
[HttpGet("{id}")]
public virtual async Task<IActionResult> GetAsync(string id, CancellationToken cancellationToken)
{
var ent = await _repositoryWrapper.SetRepository<TEntity>().GetByIdAsync(cancellationToken, id);
var dto = ent.Adapt<TDto>();
return Ok(dto);
}
// POST:Add New Entity
[HttpPost]
public virtual async Task<IActionResult> PostOrginal([FromBody] TEntity ent, CancellationToken cancellationToken)
{
_repositoryWrapper.SetRepository<TEntity>().Add(ent);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
return Ok(ent);
}
// POST:Add New Entity By Dto
[HttpPost("Dto")]
public async Task<IActionResult> PostDto([FromBody] TDto dto, CancellationToken cancellationToken)
{
_repositoryWrapper
.SetRepository<TEntity>()
.Add(dto.ToEntity());
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
return Ok();
}
// PUT:Update Entity
[HttpPut]
public virtual async Task<IActionResult> Put([FromBody] TEntity ent, CancellationToken cancellationToken)
{
_repositoryWrapper
.SetRepository<TEntity>()
.Update(ent);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
return Ok();
}
// DELETE:Delete Entity
[HttpDelete]
[Route("{id:int}")]
public virtual async Task<IActionResult> Delete(int id, CancellationToken cancellationToken)
{
var ent = await _repositoryWrapper
.SetRepository<TEntity>()
.GetByIdAsync(cancellationToken, id);
_repositoryWrapper.SetRepository<TEntity>().Delete(ent);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
return Ok();
}
}
[Authorize(AuthenticationSchemes = "Bearer")]
public class CrudController<TEntity> : BaseController
where TEntity : ApiEntity, new()
{
protected readonly IRepositoryWrapper _repositoryWrapper;
public CrudController(IRepositoryWrapper repositoryWrapper)
{
_repositoryWrapper = repositoryWrapper;
}
// GET:Get All Entity
[HttpGet]
public virtual async Task<IActionResult> GetAllAsync()
{
return Ok(await _repositoryWrapper.SetRepository<TEntity>().TableNoTracking.ToListAsync());
}
// GET:Get An Entity By Id
[HttpGet("{id}")]
public async Task<IActionResult> GetAsync(int id, CancellationToken cancellationToken)
{
var ent = await _repositoryWrapper.SetRepository<TEntity>().GetByIdAsync(cancellationToken, id);
return Ok(ent);
}
// POST:Add New Entity
[HttpPost]
public virtual async Task<IActionResult> Post([FromBody] TEntity ent, CancellationToken cancellationToken)
{
_repositoryWrapper.SetRepository<TEntity>().Add(ent);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
return Ok(ent);
}
// PUT:Update Entity
[HttpPut]
public virtual async Task<IActionResult> Put([FromBody] TEntity ent, CancellationToken cancellationToken)
{
_repositoryWrapper.SetRepository<TEntity>().Update(ent);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
return Ok();
}
// DELETE:Delete Entity
[HttpDelete("{id}")]
public virtual async Task<IActionResult> Delete(int id, CancellationToken cancellationToken)
{
var ent = await _repositoryWrapper.SetRepository<TEntity>().GetByIdAsync(cancellationToken, id);
_repositoryWrapper.SetRepository<TEntity>().Delete(ent);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
return Ok();
}
}

View File

@ -0,0 +1,21 @@
namespace DocuMed.Api.WebFramework.Configurations;
public class ConfigureJwtBearerOptions : IPostConfigureOptions<JwtBearerOptions>
{
public void PostConfigure(string name, JwtBearerOptions options)
{
var originalOnMessageReceived = options.Events.OnMessageReceived;
options.Events.OnMessageReceived = async context =>
{
await originalOnMessageReceived(context);
if (string.IsNullOrEmpty(context.Token))
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken)) context.Token = accessToken;
}
};
}
}

View File

@ -0,0 +1,19 @@
namespace DocuMed.Api.WebFramework.Configurations;
public static class LoggerConfig
{
public static void ConfigureSerilog()
{
var logName = $"{DirectoryAddress.Logs}/Log_Server_.log";
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console(theme: AnsiConsoleTheme.Literate)
.WriteTo.Sentry(o =>
{
o.MinimumEventLevel = LogEventLevel.Error;
o.Dsn = "https://592b7fbb29464442a8e996247abe857f@watcher.igarson.app/7";
})
.MinimumLevel.Override("Microsoft.EntityFrameworkCore.Database.Command", LogEventLevel.Warning)
.CreateLogger();
}
}

View File

@ -0,0 +1,134 @@
namespace DocuMed.Api.WebFramework.Configurations;
public class PersianIdentityErrorDescriber : IdentityErrorDescriber
{
public override IdentityError DefaultError()
{
return new IdentityError { Code = nameof(DefaultError), Description = "ارور ناشناخته ای رخ داده است" };
}
public override IdentityError ConcurrencyFailure()
{
return new IdentityError
{ Code = nameof(ConcurrencyFailure), Description = "در درخواست شما تداخلی ایجاد شده است" };
}
public override IdentityError PasswordMismatch()
{
return new IdentityError { Code = nameof(PasswordMismatch), Description = "رمز عبور اشتباه است" };
}
public override IdentityError InvalidToken()
{
return new IdentityError { Code = nameof(InvalidToken), Description = "توکن ارسالی اشتباه است" };
}
public override IdentityError LoginAlreadyAssociated()
{
return new IdentityError
{ Code = nameof(LoginAlreadyAssociated), Description = "یوزری با این مشخصات در حال حاضر لاگین کرده است" };
}
public override IdentityError InvalidUserName(string userName)
{
return new IdentityError
{
Code = nameof(InvalidUserName),
Description = $"یوزر نیم '{userName}' صحیح نمی باشد فقط می توانید از حروف و اعداد استفاده کنید"
};
}
public override IdentityError InvalidEmail(string email)
{
return new IdentityError { Code = nameof(InvalidEmail), Description = $"ایمیل '{email}' صحیح نمی باشد" };
}
public override IdentityError DuplicateUserName(string userName)
{
return new IdentityError
{
Code = nameof(DuplicateUserName),
Description = $"یوزرنیم '{userName}' قبلا توسط اکانت دیگری استفاده شده است"
};
}
public override IdentityError DuplicateEmail(string email)
{
return new IdentityError
{ Code = nameof(DuplicateEmail), Description = $"ایمیل '{email}' قبل استفاده شده است" };
}
public override IdentityError InvalidRoleName(string role)
{
return new IdentityError { Code = nameof(InvalidRoleName), Description = $"نقش '{role}' موجود نمی باشد" };
}
public override IdentityError DuplicateRoleName(string role)
{
return new IdentityError
{ Code = nameof(DuplicateRoleName), Description = $"نقش '{role}' قبلا برای این کاربر استفاده شده است" };
}
public override IdentityError UserAlreadyHasPassword()
{
return new IdentityError
{ Code = nameof(UserAlreadyHasPassword), Description = "کاربر قبلا رمز عبوری را استفاده کرده است" };
}
public override IdentityError UserLockoutNotEnabled()
{
return new IdentityError { Code = nameof(UserLockoutNotEnabled), Description = "کاربر مورد نظر قفل شده است" };
}
public override IdentityError UserAlreadyInRole(string role)
{
return new IdentityError
{ Code = nameof(UserAlreadyInRole), Description = "نشق مورد نظر برای این کاربر استفاده شده است" };
}
public override IdentityError UserNotInRole(string role)
{
return new IdentityError { Code = nameof(UserNotInRole), Description = $"کاربر مورد نظر در نقش '{role}' نیست" };
}
public override IdentityError PasswordTooShort(int length)
{
return new IdentityError
{ Code = nameof(PasswordTooShort), Description = $"پسورد حداقل باید {length} کاراکتر باشد" };
}
public override IdentityError PasswordRequiresNonAlphanumeric()
{
return new IdentityError
{
Code = nameof(PasswordRequiresNonAlphanumeric),
Description = "رمز عبور باید حداقل یک کاراکتر غیر عددی داشته باشد"
};
}
public override IdentityError PasswordRequiresDigit()
{
return new IdentityError
{
Code = nameof(PasswordRequiresDigit), Description = "پسور مورد نظر باید حداقل یک عدد داشته باشد ('0'-'9')"
};
}
public override IdentityError PasswordRequiresLower()
{
return new IdentityError
{
Code = nameof(PasswordRequiresLower),
Description = "پسورد مورد نظر باید حداقل یکی از حروف ('a'-'z') به صورت کوچک داشته باشد"
};
}
public override IdentityError PasswordRequiresUpper()
{
return new IdentityError
{
Code = nameof(PasswordRequiresUpper),
Description = "پسورد مورد نظر باید حداقل یکی از حروف ('A'-'Z') به صورت بزرگ داشته باشد"
};
}
}

View File

@ -0,0 +1,195 @@
using System.IO.Compression;
using System.Text;
using Asp.Versioning;
using AspNetCoreRateLimit;
using AspNetCoreRateLimit.Redis;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using StackExchange.Redis.Extensions.Core.Configuration;
using StackExchange.Redis.Extensions.Newtonsoft;
namespace DocuMed.Api.WebFramework.Configurations;
public static class ServiceExtensions
{
public static void AddIpRateLimit(this IServiceCollection services, IConfigurationRoot configuration)
{
//load general configuration from appsettings.json
services.Configure<IpRateLimitOptions>(configuration.GetSection("IpRateLimiting"));
//load ip rules from appsettings.json
services.Configure<IpRateLimitPolicies>(configuration.GetSection("IpRateLimitPolicies"));
// inject counter and rules stores
//services.AddInMemoryRateLimiting();
services.AddSingleton<IIpPolicyStore, DistributedCacheIpPolicyStore>();
services.AddSingleton<IRateLimitCounterStore, DistributedCacheRateLimitCounterStore>();
services.AddDistributedRateLimiting<AsyncKeyLockProcessingStrategy>();
services.AddDistributedRateLimiting<RedisProcessingStrategy>();
services.AddRedisRateLimiting();
// configuration (resolvers, counter key builders)
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
}
public static void AddCustomStackExchangeRedis(this IServiceCollection serviceCollection, SiteSettings siteSettings)
{
serviceCollection.AddStackExchangeRedisExtensions<NewtonsoftSerializer>(options =>
{
return new List<RedisConfiguration>
{
new()
{
Hosts = new[]
{
new RedisHost
{
Port = siteSettings.MasterRedisConfiguration.Port,
Host = siteSettings.MasterRedisConfiguration.Host
}
},
Password = siteSettings.MasterRedisConfiguration.Password,
Ssl = false
}
};
});
}
public static void AddCustomDbContext(this IServiceCollection serviceCollection, IConfigurationRoot Configuration)
{
serviceCollection.AddDbContextFactory<ApplicationContext>(options =>
{
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
options.UseNpgsql(Configuration.GetConnectionString("PostgresServer"), b => b.MigrationsAssembly("Brizco.Repository"))
.UseProjectAssembly(typeof(ApplicationUser).Assembly);
//options.EnableServiceProviderCaching(true);
}).BuildServiceProvider();
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
}
public static void AddCustomResponseCompression(this IServiceCollection serviceCollection)
{
serviceCollection.Configure<GzipCompressionProviderOptions>(options =>
options.Level = CompressionLevel.Fastest);
serviceCollection.AddResponseCompression(options =>
{
options.Providers.Add<GzipCompressionProvider>();
options.EnableForHttps = true;
});
}
public static void AddCustomCores(this IServiceCollection serviceCollection)
{
serviceCollection.AddCors(options => options.AddPolicy("CorsPolicy",
builder =>
{
builder.AllowAnyMethod()
.SetPreflightMaxAge(TimeSpan.FromHours(24))
.WithExposedHeaders("Access-control-allow-origins")
.AllowAnyHeader()
.SetIsOriginAllowed(_ => true)
.AllowCredentials();
}));
}
public static void AddCustomController(this IServiceCollection serviceCollection)
{
serviceCollection.AddControllers(options => { options.Filters.Add(new AuthorizeFilter()); })
.AddControllersAsServices()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
//options.SerializerSettings.ContractResolver = new DefaultContractResolver();
}
);
}
public static void AddCustomMvc(this IServiceCollection serviceCollection)
{
serviceCollection
.AddMvc()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
}
public static void AddCustomAuthorization(this IServiceCollection serviceCollection)
{
serviceCollection.AddAuthorization();
}
public static void AddJwtCustomAuthentication(this IServiceCollection serviceCollection, JwtSettings jwtSettings)
{
serviceCollection.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
var secretKey = Encoding.UTF8.GetBytes(jwtSettings.SecretKey);
var validateParammetrs = new TokenValidationParameters
{
ClockSkew = TimeSpan.Zero,
RequireSignedTokens = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(secretKey),
RequireExpirationTime = true,
ValidateLifetime = true,
ValidateAudience = true,
ValidAudience = jwtSettings.Audience,
ValidateIssuer = true,
ValidIssuer = jwtSettings.Issuer
};
options.RequireHttpsMetadata = true;
options.SaveToken = true;
options.TokenValidationParameters = validateParammetrs;
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// If the request is for our hub...
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken))
// Read the token out of the query string
context.Token = accessToken.ToString();
return Task.CompletedTask;
}
};
});
}
public static void AddCustomIdentity(this IServiceCollection serviceCollection)
{
serviceCollection.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequireDigit = false;
options.Password.RequireNonAlphanumeric = false;
options.User.RequireUniqueEmail = false;
}).AddEntityFrameworkStores<ApplicationContext>()
.AddDefaultTokenProviders()
.AddErrorDescriber<PersianIdentityErrorDescriber>();
;
}
public static void AddCustomApiVersioning(this IServiceCollection serviceCollection)
{
serviceCollection.AddApiVersioning(options =>
{
options.AssumeDefaultVersionWhenUnspecified = true;
options.DefaultApiVersion = new ApiVersion(1, 0);
options.ApiVersionReader = new HeaderApiVersionReader("api-version");
options.ReportApiVersions = true;
});
}
}

View File

@ -0,0 +1,214 @@
using System.IdentityModel.Tokens.Jwt;
using System.Net;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Task = System.Threading.Tasks.Task;
namespace DocuMed.Api.WebFramework.MiddleWares;
public static class ExceptionHandlerMiddlewareExtensions
{
public static IApplicationBuilder UseExceptionHandlerMiddleware(this IApplicationBuilder applicationBuilder)
{
return applicationBuilder.UseMiddleware<ExceptionHandlerMiddleware>();
}
}
public class ExceptionHandlerMiddleware
{
private readonly IWebHostEnvironment _env;
private readonly ILogger<ExceptionHandlerMiddleware> _logger;
private readonly RequestDelegate _next;
public ExceptionHandlerMiddleware(
RequestDelegate next,
IWebHostEnvironment env,
ILogger<ExceptionHandlerMiddleware> logger)
{
_next = next;
_env = env;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
string message = null;
var httpStatusCode = HttpStatusCode.InternalServerError;
var apiStatusCode = ApiResultStatusCode.ServerError;
try
{
await _next(context);
}
catch (BaseApiException exception)
{
_logger.LogError(exception, exception.Message);
httpStatusCode = exception.HttpStatusCode;
apiStatusCode = exception.ApiStatusCode;
if (_env.IsDevelopment())
{
var dic = new Dictionary<string, string>
{
["Exception"] = exception.Message,
["StackTrace"] = exception.StackTrace
};
if (exception.InnerException != null)
{
dic.Add("InnerException.Exception", exception.InnerException.Message);
dic.Add("InnerException.StackTrace", exception.InnerException.StackTrace);
}
if (exception.AdditionalData != null)
dic.Add("AdditionalData", JsonConvert.SerializeObject(exception.AdditionalData));
var contractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
};
message = JsonConvert.SerializeObject(dic, new JsonSerializerSettings
{
ContractResolver = contractResolver,
Formatting = Formatting.Indented
});
}
else
{
message = exception.Message;
}
if(exception.AdditionalData==null)
await WriteToResponseAsync();
else
await WriteToResponseWithObjectAsync(exception.AdditionalData);
}
catch (SecurityTokenExpiredException exception)
{
_logger.LogError(exception, exception.Message);
SetUnAuthorizeResponse(exception);
await WriteToResponseAsync();
}
catch (UnauthorizedAccessException exception)
{
_logger.LogError(exception, exception.Message);
SetUnAuthorizeResponse(exception);
await WriteToResponseAsync();
}
catch (Exception exception)
{
_logger.LogError(exception, exception.Message);
if (_env.IsDevelopment())
{
var dic = new Dictionary<string, string>
{
["Exception"] = exception.Message,
["InnerException"] = exception.InnerException?.Message,
["StackTrace"] = exception.StackTrace
};
message = JsonConvert.SerializeObject(dic);
}
message = exception.Message;
await WriteToResponseAsync();
}
async Task WriteToResponseAsync()
{
if (context.Response.HasStarted)
throw new InvalidOperationException("The response has already started, the http status code middleware will not be executed.");
var result = new ApiResult(false, apiStatusCode, message);
var contractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
};
var json = JsonConvert.SerializeObject(result, new JsonSerializerSettings
{
ContractResolver = contractResolver,
Formatting = Formatting.Indented
});
context.Response.StatusCode = (int)httpStatusCode;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(json);
}
async Task WriteToResponseWithObjectAsync(object additionalData)
{
if (context.Response.HasStarted)
throw new InvalidOperationException(
"The response has already started, the http status code middleware will not be executed.");
var result = new ApiResult<object>(false, apiStatusCode, additionalData, message);
var contractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
};
var json = JsonConvert.SerializeObject(result, new JsonSerializerSettings
{
ContractResolver = contractResolver,
Formatting = Formatting.Indented
});
context.Response.StatusCode = (int)httpStatusCode;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(json);
}
void SetUnAuthorizeResponse(Exception exception)
{
httpStatusCode = HttpStatusCode.Unauthorized;
apiStatusCode = ApiResultStatusCode.UnAuthorized;
if (_env.IsDevelopment())
{
var dic = new Dictionary<string, string>
{
["Exception"] = exception.Message,
["StackTrace"] = exception.StackTrace
};
if (exception is SecurityTokenExpiredException tokenException)
dic.Add("Expires", tokenException.Expires.ToString());
message = JsonConvert.SerializeObject(dic);
}
}
JwtSecurityToken ReadJwtToken(bool fromHeader = true)
{
try
{
if (fromHeader)
{
var stream = context.Request.Headers.Values.First(v => v.FirstOrDefault().Contains("Bearer"))
.FirstOrDefault();
var handler = new JwtSecurityTokenHandler();
var jsonToken = handler.ReadToken(stream.Split(" ").Last());
return jsonToken as JwtSecurityToken;
}
else
{
string stream = context.Request.Query["access_token"];
;
var handler = new JwtSecurityTokenHandler();
var jsonToken = handler.ReadToken(stream.Split(" ").Last());
return jsonToken as JwtSecurityToken;
}
}
catch (Exception e)
{
throw new BaseApiException(ApiResultStatusCode.UnAuthorized, e.Message + " Jwt is wrong",
HttpStatusCode.Unauthorized);
}
}
}
}

View File

@ -0,0 +1,37 @@
using System.Diagnostics;
namespace DocuMed.Api.WebFramework.MiddleWares;
public static class PerformanceMiddlewareExtensions
{
public static IApplicationBuilder UsePerformanceMiddlewar(this IApplicationBuilder applicationBuilder)
{
return applicationBuilder.UseMiddleware<PerformanceMiddleware>();
}
}
public class PerformanceMiddleware
{
private readonly ILogger<ExceptionHandlerMiddleware> _logger;
private readonly RequestDelegate _next;
private readonly Stopwatch _timer;
public PerformanceMiddleware(
RequestDelegate next,
ILogger<ExceptionHandlerMiddleware> logger)
{
_next = next;
_logger = logger;
_timer = new Stopwatch();
}
public async System.Threading.Tasks.Task Invoke(HttpContext context)
{
_timer.Start();
await _next(context);
_timer.Stop();
var elapsedMilliseconds = _timer.ElapsedMilliseconds;
_logger.LogWarning($"REQUEST TIMER : {elapsedMilliseconds}");
}
}

View File

@ -0,0 +1,280 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.OpenApi.Models;
using Pluralize.NET;
using Swashbuckle.AspNetCore.SwaggerGen;
using Swashbuckle.AspNetCore.SwaggerUI;
namespace DocuMed.Api.WebFramework.Swagger;
public static class SwaggerConfiguration
{
public static void AddCustomSwagger(this IServiceCollection services,string baseUrl)
{
services.AddSwaggerGen(options =>
{
//var xmlDuc = Path.Combine(AppContext.BaseDirectory, "swaggerApi.xml");
//options.IncludeXmlComments(xmlDuc,true);
options.SwaggerDoc("v1",
new OpenApiInfo
{
Version = "v1",
Title = "iGarson Api Dacument",
Description = "iGarson api for clients that wana use",
License = new OpenApiLicense { Name = "Vira Safir Fanavar " },
Contact = new OpenApiContact
{
Name = "Amir Hossein Khademi",
Email = "avvampier@gmail.com",
Url = new Uri("http://amir-khademi.ir/")
}
});
options.EnableAnnotations();
options.DescribeAllParametersInCamelCase();
options.IgnoreObsoleteActions();
//#region Versioning
//// Remove version parameter from all Operations
//options.OperationFilter<RemoveVersionParameters>();
////set version "api/v{version}/[controller]" from current swagger doc verion
//options.DocumentFilter<SetVersionInPaths>();
////Seperate and categorize end-points by doc version
//options.DocInclusionPredicate((version, desc) =>
//{
// if (!desc.TryGetMethodInfo(out var methodInfo)) return false;
// var versions = methodInfo.DeclaringType
// .GetCustomAttributes(true)
// .OfType<ApiVersionAttribute>()
// .SelectMany(attr => attr.Versions)
// .ToList();
// return versions.Any(v => $"v{v.ToString()}" == version);
//});
//#endregion
#region Security
var url = $"{baseUrl}/api/auth/login/swagger";
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Scheme = "Bearer",
Name = "Bearer",
Flows = new OpenApiOAuthFlows
{
Password = new OpenApiOAuthFlow
{
TokenUrl = new Uri(url)
}
}
});
options.OperationFilter<UnauthorizedResponsesOperationFilter>(true, "Bearer");
#endregion
#region Customize
options.OperationFilter<ApplySummariesOperationFilter>();
#endregion
});
}
public static void UseCustomSwagger(this IApplicationBuilder app,string baseUrl)
{
app.UseSwagger(options =>
{
options.SerializeAsV2 = true;
});
app.UseSwaggerUI(options =>
{
options.InjectStylesheet("/assets/swagger-ui/x3/theme-flattop.css");
options.DocExpansion(DocExpansion.None);
// Display
options.DefaultModelExpandDepth(2);
options.DefaultModelRendering(ModelRendering.Model);
options.DefaultModelsExpandDepth(-1);
options.DisplayOperationId();
options.DisplayRequestDuration();
options.EnableDeepLinking();
options.EnableFilter();
options.ShowExtensions();
options.OAuthUseBasicAuthenticationWithAccessCodeGrant();
options.SwaggerEndpoint($"{baseUrl}/swagger/v1/swagger.json", "V1 Docs");
});
}
}
public class RemoveVersionParameters : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
// Remove version parameter from all Operations
var versionParameter = operation.Parameters.SingleOrDefault(p => p.Name == "version");
if (versionParameter != null)
operation.Parameters.Remove(versionParameter);
}
}
public class SetVersionInPaths : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
if (swaggerDoc == null)
throw new ArgumentNullException(nameof(swaggerDoc));
var replacements = new OpenApiPaths();
foreach (var (key, value) in swaggerDoc.Paths)
replacements.Add(key.Replace("v{version}", swaggerDoc.Info.Version, StringComparison.InvariantCulture),
value);
swaggerDoc.Paths = replacements;
}
}
public class UnauthorizedResponsesOperationFilter : IOperationFilter
{
private readonly bool includeUnauthorizedAndForbiddenResponses;
private readonly string schemeName;
public UnauthorizedResponsesOperationFilter(bool includeUnauthorizedAndForbiddenResponses,
string schemeName = "Bearer")
{
this.includeUnauthorizedAndForbiddenResponses = includeUnauthorizedAndForbiddenResponses;
this.schemeName = schemeName;
}
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var filters = context.ApiDescription.ActionDescriptor.FilterDescriptors;
var hasAnynomousEndPoint = context.ApiDescription.ActionDescriptor.EndpointMetadata.Any(e =>
e.GetType() == typeof(AllowAnonymousAttribute));
//var hasAnonymous = filters.Any(p => p.Filter is AllowAnonymousFilter);
if (hasAnynomousEndPoint)
return;
/*var hasAuthorize = filters.Any(p => p.Filter is AuthorizeFilter);
if (!hasAuthorize)
return;*/
if (includeUnauthorizedAndForbiddenResponses)
{
operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" });
operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" });
}
operation.Security.Add(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] { }
}
});
}
}
public class ApplySummariesOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var controllerActionDescriptor = context.ApiDescription.ActionDescriptor as ControllerActionDescriptor;
if (controllerActionDescriptor == null) return;
var pluralizer = new Pluralizer();
var actionName = controllerActionDescriptor.ActionName;
var singularizeName = pluralizer.Singularize(controllerActionDescriptor.ControllerName);
var pluralizeName = pluralizer.Pluralize(singularizeName);
var parameterCount = operation.Parameters.Where(p => p.Name != "version" && p.Name != "api-version").Count();
if (IsGetAllAction())
{
if (!operation.Summary.HasValue())
operation.Summary = $"Returns all {pluralizeName}";
}
else if (IsActionName("Post", "Create"))
{
if (!operation.Summary.HasValue())
operation.Summary = $"Creates a {singularizeName}";
if (operation.Parameters.Count > 0 && !operation.Parameters[0].Description.HasValue())
operation.Parameters[0].Description = $"A {singularizeName} representation";
}
else if (IsActionName("Read", "Get"))
{
if (!operation.Summary.HasValue())
operation.Summary = $"Retrieves a {singularizeName} by unique id";
if (operation.Parameters.Count > 0 && !operation.Parameters[0].Description.HasValue())
operation.Parameters[0].Description = $"a unique id for the {singularizeName}";
}
else if (IsActionName("Put", "Edit", "Update"))
{
if (!operation.Summary.HasValue())
operation.Summary = $"Updates a {singularizeName} by unique id";
//if (!operation.Parameters[0].OrderDescription.HasValue())
// operation.Parameters[0].OrderDescription = $"A unique id for the {singularizeName}";
if (operation.Parameters.Count > 0 && !operation.Parameters[0].Description.HasValue())
operation.Parameters[0].Description = $"A {singularizeName} representation";
}
else if (IsActionName("Delete", "Remove"))
{
if (!operation.Summary.HasValue())
operation.Summary = $"Deletes a {singularizeName} by unique id";
if (operation.Parameters.Count > 0 && !operation.Parameters[0].Description.HasValue())
operation.Parameters[0].Description = $"A unique id for the {singularizeName}";
}
else
{
if (!operation.Summary.HasValue())
operation.Summary = $"{actionName} {pluralizeName}";
}
#region Local Functions
bool IsGetAllAction()
{
foreach (var name in new[] { "Get", "Read", "Select" })
if (actionName.Equals(name, StringComparison.OrdinalIgnoreCase) && parameterCount == 0 ||
actionName.Equals($"{name}All", StringComparison.OrdinalIgnoreCase) ||
actionName.Equals($"{name}{pluralizeName}", StringComparison.OrdinalIgnoreCase) ||
actionName.Equals($"{name}All{singularizeName}", StringComparison.OrdinalIgnoreCase) ||
actionName.Equals($"{name}All{pluralizeName}", StringComparison.OrdinalIgnoreCase))
return true;
return false;
}
bool IsActionName(params string[] names)
{
foreach (var name in names)
if (actionName.Contains(name, StringComparison.OrdinalIgnoreCase) ||
actionName.Contains($"{name}ById", StringComparison.OrdinalIgnoreCase) ||
actionName.Contains($"{name}{singularizeName}", StringComparison.OrdinalIgnoreCase) ||
actionName.Contains($"{name}{singularizeName}ById", StringComparison.OrdinalIgnoreCase))
return true;
return false;
}
#endregion
}
}

View File

@ -1,6 +1,6 @@
namespace DocuMed.Core
{
public class Class1
public class CoreConfig
{
}

View File

@ -19,4 +19,15 @@
<ProjectReference Include="..\DocuMed.Repository\DocuMed.Repository.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Models\Api\" />
</ItemGroup>
<ItemGroup>
<Using Include="DocuMed.Common.Extensions" />
<Using Include="DocuMed.Common.Models.Api" />
<Using Include="Microsoft.AspNetCore.Mvc" />
<Using Include="Newtonsoft.Json" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,127 @@
namespace DocuMed.Core.Models.Api;
public class ApiResult
{
public ApiResult(bool isSuccess, ApiResultStatusCode statusCode, string message = null)
{
IsSuccess = isSuccess;
StatusCode = statusCode;
Message = message ?? statusCode.ToDisplay();
}
public bool IsSuccess { get; set; }
public ApiResultStatusCode StatusCode { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string Message { get; set; }
#region Implicit Operators
public static implicit operator ApiResult(OkResult result)
{
return new ApiResult(true, ApiResultStatusCode.Success);
}
public static implicit operator ApiResult(BadRequestResult result)
{
return new ApiResult(false, ApiResultStatusCode.BadRequest);
}
public static implicit operator ApiResult(BadRequestObjectResult result)
{
var message = result.Value.ToString();
if (result.Value is SerializableError errors)
{
var errorMessages = errors.SelectMany(p => (string[])p.Value).Distinct();
message = string.Join(" | ", errorMessages);
}
return new ApiResult(false, ApiResultStatusCode.BadRequest, message);
}
public static implicit operator ApiResult(ContentResult result)
{
return new ApiResult(true, ApiResultStatusCode.Success, result.Content);
}
public static implicit operator ApiResult(NotFoundResult result)
{
return new ApiResult(false, ApiResultStatusCode.NotFound);
}
public static implicit operator ApiResult(ForbidResult result)
{
return new ApiResult(false, ApiResultStatusCode.NotFound);
}
public static implicit operator ApiResult(StatusCodeResult result)
{
return new ApiResult(false, ApiResultStatusCode.NotFound);
}
#endregion
}
public class ApiResult<TData> : ApiResult
where TData : class
{
public ApiResult(bool isSuccess, ApiResultStatusCode statusCode, TData data, string message = null)
: base(isSuccess, statusCode, message)
{
Data = data;
}
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public TData Data { get; set; }
#region Implicit Operators
public static implicit operator ApiResult<TData>(TData data)
{
return new ApiResult<TData>(true, ApiResultStatusCode.Success, data);
}
public static implicit operator ApiResult<TData>(OkResult result)
{
return new ApiResult<TData>(true, ApiResultStatusCode.Success, null);
}
public static implicit operator ApiResult<TData>(OkObjectResult result)
{
return new ApiResult<TData>(true, ApiResultStatusCode.Success, (TData)result.Value);
}
public static implicit operator ApiResult<TData>(BadRequestResult result)
{
return new ApiResult<TData>(false, ApiResultStatusCode.BadRequest, null);
}
public static implicit operator ApiResult<TData>(BadRequestObjectResult result)
{
var message = result.Value.ToString();
if (result.Value is SerializableError errors)
{
var errorMessages = errors.SelectMany(p => (string[])p.Value).Distinct();
message = string.Join(" | ", errorMessages);
}
return new ApiResult<TData>(false, ApiResultStatusCode.BadRequest, null, message);
}
public static implicit operator ApiResult<TData>(ContentResult result)
{
return new ApiResult<TData>(true, ApiResultStatusCode.Success, null, result.Content);
}
public static implicit operator ApiResult<TData>(NotFoundResult result)
{
return new ApiResult<TData>(false, ApiResultStatusCode.NotFound, null);
}
public static implicit operator ApiResult<TData>(NotFoundObjectResult result)
{
return new ApiResult<TData>(false, ApiResultStatusCode.NotFound, (TData)result.Value);
}
#endregion
}

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
@ -14,4 +14,8 @@
<ProjectReference Include="..\DocuMed.Core\DocuMed.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Models\" />
</ItemGroup>
</Project>

View File

@ -1,6 +1,6 @@
namespace DocuMed.Infrastructure
{
public class Class1
public class InfrastructureConfig
{
}

View File

@ -0,0 +1,6 @@
namespace DocuMed.Infrastructure.Models;
public static class DirectoryAddress
{
private static readonly string BaseDire = $"{Directory.GetCurrentDirectory()}/wwwroot";
public static string Logs = $"{BaseDire}/logs";
}

View File

@ -4,6 +4,5 @@ public interface ICurrentUserService : IScopedDependency
{
string? UserId { get; }
string? RoleName { get; }
string? ComplexId { get; }
string? UserName { get; }
}

View File

@ -34,6 +34,7 @@
<Using Include="DocuMed.Common.Models.Claims" />
<Using Include="DocuMed.Common.Models.Entity" />
<Using Include="DocuMed.Domain.Entities.User" />
<Using Include="DocuMed.Domain.Enums" />
<Using Include="DocuMed.Domain.Models.Settings" />
<Using Include="DocuMed.Repository.Extensions" />
<Using Include="DocuMed.Repository.Models" />

View File

@ -1,5 +1,19 @@
namespace DocuMed.Repository;
public class RepositoryConfig
{
using Microsoft.AspNetCore.Builder;
}
namespace DocuMed.Repository;
public static class RepositoryConfig
{
public static async Task InitialDb(this IApplicationBuilder app)
{
var scopeFactory = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
{
var identityDbInitialize = scope.ServiceProvider.GetService<IDbInitializerService>();
if (identityDbInitialize != null)
{
identityDbInitialize.Initialize();
await identityDbInitialize.SeedDate();
}
}
}
}