From c3d4e011ffc30ba739f31d89d82daf824ed7a759 Mon Sep 17 00:00:00 2001 From: "Amir.H Khademi" Date: Fri, 8 Sep 2023 12:25:21 +0330 Subject: [PATCH] Add project files. --- .dockerignore | 25 ++ Berizco.Api/Berizco.Api.csproj | 17 + .../Controllers/WeatherForecastController.cs | 18 + Berizco.Api/Dockerfile | 21 ++ Berizco.Api/Program.cs | 23 ++ Berizco.Api/Properties/launchSettings.json | 40 +++ Berizco.Api/WeatherForecast.cs | 13 + Berizco.Api/appsettings.Development.json | 8 + Berizco.Api/appsettings.json | 9 + .../Brizco.Infrastructure.csproj | 9 + .../InfrastructureConfig.cs | 7 + Berizco.Repository/Brizco.Repository.csproj | 25 ++ Berizco.Repository/RepositoryConfig.cs | 7 + Berizco.sln | 72 ++++ Brizco.Common/Brizco.Common.csproj | 21 ++ Brizco.Common/CommonConfig.cs | 7 + Brizco.Common/Extensions/AssertExtensions.cs | 30 ++ .../Extensions/ClassDisplayExtensions.cs | 39 ++ .../Extensions/DateTimeExtensions.cs | 61 ++++ Brizco.Common/Extensions/EnumExtensions.cs | 55 +++ .../Extensions/NewtonJsonExtensions.cs | 18 + .../Extensions/PhoneNumberExtensions.cs | 41 +++ .../Extensions/PropertyExtensions.cs | 17 + Brizco.Common/Extensions/RandomExtensions.cs | 12 + Brizco.Common/Extensions/StringExtensions.cs | 165 +++++++++ .../Extensions/ValidationExtensions.cs | 21 ++ Brizco.Common/Models/Api/AccessToken.cs | 51 +++ .../Models/Api/ApiResultStatusCode.cs | 42 +++ Brizco.Common/Models/Api/AppSettings.cs | 7 + Brizco.Common/Models/Api/FileUploadRequest.cs | 15 + Brizco.Common/Models/Api/HealthCheck.cs | 9 + Brizco.Common/Models/Api/ResponseFile.cs | 9 + Brizco.Common/Models/Api/TokenRequest.cs | 18 + .../Models/Claims/ApplicationClaims.cs | 11 + .../Models/Claims/ApplicationPermission.cs | 5 + Brizco.Common/Models/Claims/ClaimDto.cs | 20 ++ .../Models/Claims/CustomClaimType.cs | 7 + Brizco.Common/Models/Entity/ApiEntity.cs | 29 ++ Brizco.Common/Models/Entity/ClassDisplay.cs | 25 ++ Brizco.Common/Models/Entity/IApiEntity.cs | 13 + .../Models/Exception/AppException.cs | 26 ++ .../Models/Exception/BaseApiException.cs | 91 +++++ Brizco.Common/Models/IScopedDependency.cs | 6 + Brizco.Common/Models/Mapper/BaseDto.cs | 97 +++++ Brizco.Common/Models/Mapper/IBaseDto.cs | 11 + Brizco.Common/Models/Report/ChartUnit.cs | 38 ++ Brizco.Common/Models/Report/ReportRequest.cs | 24 ++ Brizco.Common/Models/Report/ReportResult.cs | 34 ++ Brizco.Common/Models/Report/ReportableItem.cs | 89 +++++ Brizco.Core/Brizco.Core.csproj | 9 + Brizco.Core/CoreConfig.cs | 7 + Brizco.Domain/Brizco.Domain.csproj | 43 +++ Brizco.Domain/DomainConfig.cs | 7 + Brizco.Domain/Entities/ApplicationRole.cs | 6 + Brizco.Domain/Entities/ApplicationUser.cs | 6 + Brizco.Domain/Entities/Complex.cs | 8 + Brizco.Domain/Entities/ComplexUser.cs | 11 + Brizco.Domain/Entities/Shift.cs | 10 + Brizco.Domain/Entities/SubTask.cs | 5 + Brizco.Domain/Entities/Task.cs | 23 ++ Brizco.Domain/Entities/TaskRole.cs | 10 + Brizco.Domain/Entities/TaskShift.cs | 9 + Brizco.Domain/Entities/TaskUser.cs | 9 + Brizco.Domain/Enums/PurchaseAmountType.cs | 11 + Brizco.Domain/Enums/TaskStatus.cs | 13 + Brizco.Domain/Enums/TaskType.cs | 7 + Brizco.Domain/FodyWeavers.xml | 3 + .../Brizco.Identity.Api.csproj | 78 ++++ .../Controllers/V1/AccountController.cs | 24 ++ Brizco.Identity.Api/Dockerfile | 21 ++ .../Domain/ApplicationContext.cs | 7 + .../Domain/Entities/ApplicationRole.cs | 6 + .../Domain/Entities/ApplicationUser.cs | 7 + Brizco.Identity.Api/FodyWeavers.xml | 3 + .../20230830071509_init.Designer.cs | 279 ++++++++++++++ .../Migrations/20230830071509_init.cs | 224 ++++++++++++ .../ApplicationContextModelSnapshot.cs | 276 ++++++++++++++ Brizco.Identity.Api/Models/JwtSettings.cs | 8 + Brizco.Identity.Api/Models/SiteSettings.cs | 6 + Brizco.Identity.Api/Program.cs | 52 +++ .../Properties/launchSettings.json | 41 +++ .../Contracts/IDbInitializerService.cs | 6 + .../Services/Contracts/IUserService.cs | 6 + .../Services/DbInitializerService.cs | 38 ++ Brizco.Identity.Api/Services/UserService.cs | 28 ++ .../WebFramework/Bases/ApiResult.cs | 118 ++++++ .../Bases/ApiResultFilterAttribute.cs | 72 ++++ .../ConfigureJwtBearerOptions.cs | 26 ++ .../Configurations/LoggerConfiguration.cs | 24 ++ .../PersianIdentityErrorDescriber.cs | 27 ++ .../Configurations/ServiceExtensions.cs | 145 ++++++++ .../Configurations/SwaggerConfiguration.cs | 339 ++++++++++++++++++ .../MiddleWares/ExceptionHandlerMiddleware.cs | 182 ++++++++++ .../appsettings.Development.json | 17 + Brizco.Identity.Api/appsettings.json | 9 + NuGet.config | 28 ++ docker-compose.dcproj | 18 + docker-compose.override.yml | 15 + docker-compose.yml | 42 +++ launchSettings.json | 12 + 100 files changed, 3839 insertions(+) create mode 100644 .dockerignore create mode 100644 Berizco.Api/Berizco.Api.csproj create mode 100644 Berizco.Api/Controllers/WeatherForecastController.cs create mode 100644 Berizco.Api/Dockerfile create mode 100644 Berizco.Api/Program.cs create mode 100644 Berizco.Api/Properties/launchSettings.json create mode 100644 Berizco.Api/WeatherForecast.cs create mode 100644 Berizco.Api/appsettings.Development.json create mode 100644 Berizco.Api/appsettings.json create mode 100644 Berizco.Infrastructure/Brizco.Infrastructure.csproj create mode 100644 Berizco.Infrastructure/InfrastructureConfig.cs create mode 100644 Berizco.Repository/Brizco.Repository.csproj create mode 100644 Berizco.Repository/RepositoryConfig.cs create mode 100644 Berizco.sln create mode 100644 Brizco.Common/Brizco.Common.csproj create mode 100644 Brizco.Common/CommonConfig.cs create mode 100644 Brizco.Common/Extensions/AssertExtensions.cs create mode 100644 Brizco.Common/Extensions/ClassDisplayExtensions.cs create mode 100644 Brizco.Common/Extensions/DateTimeExtensions.cs create mode 100644 Brizco.Common/Extensions/EnumExtensions.cs create mode 100644 Brizco.Common/Extensions/NewtonJsonExtensions.cs create mode 100644 Brizco.Common/Extensions/PhoneNumberExtensions.cs create mode 100644 Brizco.Common/Extensions/PropertyExtensions.cs create mode 100644 Brizco.Common/Extensions/RandomExtensions.cs create mode 100644 Brizco.Common/Extensions/StringExtensions.cs create mode 100644 Brizco.Common/Extensions/ValidationExtensions.cs create mode 100644 Brizco.Common/Models/Api/AccessToken.cs create mode 100644 Brizco.Common/Models/Api/ApiResultStatusCode.cs create mode 100644 Brizco.Common/Models/Api/AppSettings.cs create mode 100644 Brizco.Common/Models/Api/FileUploadRequest.cs create mode 100644 Brizco.Common/Models/Api/HealthCheck.cs create mode 100644 Brizco.Common/Models/Api/ResponseFile.cs create mode 100644 Brizco.Common/Models/Api/TokenRequest.cs create mode 100644 Brizco.Common/Models/Claims/ApplicationClaims.cs create mode 100644 Brizco.Common/Models/Claims/ApplicationPermission.cs create mode 100644 Brizco.Common/Models/Claims/ClaimDto.cs create mode 100644 Brizco.Common/Models/Claims/CustomClaimType.cs create mode 100644 Brizco.Common/Models/Entity/ApiEntity.cs create mode 100644 Brizco.Common/Models/Entity/ClassDisplay.cs create mode 100644 Brizco.Common/Models/Entity/IApiEntity.cs create mode 100644 Brizco.Common/Models/Exception/AppException.cs create mode 100644 Brizco.Common/Models/Exception/BaseApiException.cs create mode 100644 Brizco.Common/Models/IScopedDependency.cs create mode 100644 Brizco.Common/Models/Mapper/BaseDto.cs create mode 100644 Brizco.Common/Models/Mapper/IBaseDto.cs create mode 100644 Brizco.Common/Models/Report/ChartUnit.cs create mode 100644 Brizco.Common/Models/Report/ReportRequest.cs create mode 100644 Brizco.Common/Models/Report/ReportResult.cs create mode 100644 Brizco.Common/Models/Report/ReportableItem.cs create mode 100644 Brizco.Core/Brizco.Core.csproj create mode 100644 Brizco.Core/CoreConfig.cs create mode 100644 Brizco.Domain/Brizco.Domain.csproj create mode 100644 Brizco.Domain/DomainConfig.cs create mode 100644 Brizco.Domain/Entities/ApplicationRole.cs create mode 100644 Brizco.Domain/Entities/ApplicationUser.cs create mode 100644 Brizco.Domain/Entities/Complex.cs create mode 100644 Brizco.Domain/Entities/ComplexUser.cs create mode 100644 Brizco.Domain/Entities/Shift.cs create mode 100644 Brizco.Domain/Entities/SubTask.cs create mode 100644 Brizco.Domain/Entities/Task.cs create mode 100644 Brizco.Domain/Entities/TaskRole.cs create mode 100644 Brizco.Domain/Entities/TaskShift.cs create mode 100644 Brizco.Domain/Entities/TaskUser.cs create mode 100644 Brizco.Domain/Enums/PurchaseAmountType.cs create mode 100644 Brizco.Domain/Enums/TaskStatus.cs create mode 100644 Brizco.Domain/Enums/TaskType.cs create mode 100644 Brizco.Domain/FodyWeavers.xml create mode 100644 Brizco.Identity.Api/Brizco.Identity.Api.csproj create mode 100644 Brizco.Identity.Api/Controllers/V1/AccountController.cs create mode 100644 Brizco.Identity.Api/Dockerfile create mode 100644 Brizco.Identity.Api/Domain/ApplicationContext.cs create mode 100644 Brizco.Identity.Api/Domain/Entities/ApplicationRole.cs create mode 100644 Brizco.Identity.Api/Domain/Entities/ApplicationUser.cs create mode 100644 Brizco.Identity.Api/FodyWeavers.xml create mode 100644 Brizco.Identity.Api/Migrations/20230830071509_init.Designer.cs create mode 100644 Brizco.Identity.Api/Migrations/20230830071509_init.cs create mode 100644 Brizco.Identity.Api/Migrations/ApplicationContextModelSnapshot.cs create mode 100644 Brizco.Identity.Api/Models/JwtSettings.cs create mode 100644 Brizco.Identity.Api/Models/SiteSettings.cs create mode 100644 Brizco.Identity.Api/Program.cs create mode 100644 Brizco.Identity.Api/Properties/launchSettings.json create mode 100644 Brizco.Identity.Api/Services/Contracts/IDbInitializerService.cs create mode 100644 Brizco.Identity.Api/Services/Contracts/IUserService.cs create mode 100644 Brizco.Identity.Api/Services/DbInitializerService.cs create mode 100644 Brizco.Identity.Api/Services/UserService.cs create mode 100644 Brizco.Identity.Api/WebFramework/Bases/ApiResult.cs create mode 100644 Brizco.Identity.Api/WebFramework/Bases/ApiResultFilterAttribute.cs create mode 100644 Brizco.Identity.Api/WebFramework/Configurations/ConfigureJwtBearerOptions.cs create mode 100644 Brizco.Identity.Api/WebFramework/Configurations/LoggerConfiguration.cs create mode 100644 Brizco.Identity.Api/WebFramework/Configurations/PersianIdentityErrorDescriber.cs create mode 100644 Brizco.Identity.Api/WebFramework/Configurations/ServiceExtensions.cs create mode 100644 Brizco.Identity.Api/WebFramework/Configurations/SwaggerConfiguration.cs create mode 100644 Brizco.Identity.Api/WebFramework/MiddleWares/ExceptionHandlerMiddleware.cs create mode 100644 Brizco.Identity.Api/appsettings.Development.json create mode 100644 Brizco.Identity.Api/appsettings.json create mode 100644 NuGet.config create mode 100644 docker-compose.dcproj create mode 100644 docker-compose.override.yml create mode 100644 docker-compose.yml create mode 100644 launchSettings.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3729ff0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*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 \ No newline at end of file diff --git a/Berizco.Api/Berizco.Api.csproj b/Berizco.Api/Berizco.Api.csproj new file mode 100644 index 0000000..c6c5641 --- /dev/null +++ b/Berizco.Api/Berizco.Api.csproj @@ -0,0 +1,17 @@ + + + + net7.0 + enable + enable + Linux + ..\docker-compose.dcproj + + + + + + + + + diff --git a/Berizco.Api/Controllers/WeatherForecastController.cs b/Berizco.Api/Controllers/WeatherForecastController.cs new file mode 100644 index 0000000..efce28f --- /dev/null +++ b/Berizco.Api/Controllers/WeatherForecastController.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Berizco.Api.Controllers +{ + [ApiController] + [Route("[controller]")] + public class WeatherForecastController : ControllerBase + { + public WeatherForecastController() + { + } + + public IEnumerable Get() + { + + } + } +} \ No newline at end of file diff --git a/Berizco.Api/Dockerfile b/Berizco.Api/Dockerfile new file mode 100644 index 0000000..bc68f77 --- /dev/null +++ b/Berizco.Api/Dockerfile @@ -0,0 +1,21 @@ +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base +WORKDIR /app +EXPOSE 80 + +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +WORKDIR /src +COPY ["Berizco.Api/Berizco.Api.csproj", "Berizco.Api/"] +RUN dotnet restore "Berizco.Api/Berizco.Api.csproj" +COPY . . +WORKDIR "/src/Berizco.Api" +RUN dotnet build "Berizco.Api.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "Berizco.Api.csproj" -c Release -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Berizco.Api.dll"] \ No newline at end of file diff --git a/Berizco.Api/Program.cs b/Berizco.Api/Program.cs new file mode 100644 index 0000000..df2434c --- /dev/null +++ b/Berizco.Api/Program.cs @@ -0,0 +1,23 @@ +var builder = WebApplication.CreateBuilder(args); + +// 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(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/Berizco.Api/Properties/launchSettings.json b/Berizco.Api/Properties/launchSettings.json new file mode 100644 index 0000000..f702a33 --- /dev/null +++ b/Berizco.Api/Properties/launchSettings.json @@ -0,0 +1,40 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5062" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Docker": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", + "environmentVariables": { + "ASPNETCORE_URLS": "http://+:80" + }, + "publishAllPorts": true + } + }, + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:28470", + "sslPort": 0 + } + } +} \ No newline at end of file diff --git a/Berizco.Api/WeatherForecast.cs b/Berizco.Api/WeatherForecast.cs new file mode 100644 index 0000000..a55992d --- /dev/null +++ b/Berizco.Api/WeatherForecast.cs @@ -0,0 +1,13 @@ +namespace Berizco.Api +{ + public class WeatherForecast + { + public DateOnly Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } + } +} \ No newline at end of file diff --git a/Berizco.Api/appsettings.Development.json b/Berizco.Api/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/Berizco.Api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Berizco.Api/appsettings.json b/Berizco.Api/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/Berizco.Api/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/Berizco.Infrastructure/Brizco.Infrastructure.csproj b/Berizco.Infrastructure/Brizco.Infrastructure.csproj new file mode 100644 index 0000000..cfadb03 --- /dev/null +++ b/Berizco.Infrastructure/Brizco.Infrastructure.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/Berizco.Infrastructure/InfrastructureConfig.cs b/Berizco.Infrastructure/InfrastructureConfig.cs new file mode 100644 index 0000000..33d8838 --- /dev/null +++ b/Berizco.Infrastructure/InfrastructureConfig.cs @@ -0,0 +1,7 @@ +namespace Brizco.Infrastructure +{ + public class InfrastructureConfig + { + + } +} \ No newline at end of file diff --git a/Berizco.Repository/Brizco.Repository.csproj b/Berizco.Repository/Brizco.Repository.csproj new file mode 100644 index 0000000..fc5b947 --- /dev/null +++ b/Berizco.Repository/Brizco.Repository.csproj @@ -0,0 +1,25 @@ + + + + net7.0 + enable + enable + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/Berizco.Repository/RepositoryConfig.cs b/Berizco.Repository/RepositoryConfig.cs new file mode 100644 index 0000000..7e4c7c6 --- /dev/null +++ b/Berizco.Repository/RepositoryConfig.cs @@ -0,0 +1,7 @@ +namespace Brizco.Repository +{ + public class RepositoryConfig + { + + } +} \ No newline at end of file diff --git a/Berizco.sln b/Berizco.sln new file mode 100644 index 0000000..1dfeb8e --- /dev/null +++ b/Berizco.sln @@ -0,0 +1,72 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34018.315 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Berizco.Api", "Berizco.Api\Berizco.Api.csproj", "{9D6CE82C-1BB9-42E7-96B5-6D43AFCF8365}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Brizco.Common", "Brizco.Common\Brizco.Common.csproj", "{351E45E6-873B-40F5-97A2-CC02FB168A3D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Brizco.Domain", "Brizco.Domain\Brizco.Domain.csproj", "{D76A820B-FD40-46AA-93BF-D640F69C4C7E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Brizco.Core", "Brizco.Core\Brizco.Core.csproj", "{305576D6-7440-4153-B6CA-3D65169E4438}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Brizco.Infrastructure", "Berizco.Infrastructure\Brizco.Infrastructure.csproj", "{DE7B8F3D-392B-4C0D-8B70-40CA1F4BB15A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Brizco.Repository", "Berizco.Repository\Brizco.Repository.csproj", "{E170997A-90FF-4179-991F-B5C36746C969}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Brizco.Identity", "Brizco.Identity", "{5B01AC0C-8DFA-400C-8137-984F67227682}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Brizco.Identity.Api", "Brizco.Identity.Api\Brizco.Identity.Api.csproj", "{6E54FD68-0FC8-4252-8F4B-714823C58FAB}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{9FEFFA46-5403-42CD-87A4-9B2AC30BD7B6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9D6CE82C-1BB9-42E7-96B5-6D43AFCF8365}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D6CE82C-1BB9-42E7-96B5-6D43AFCF8365}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D6CE82C-1BB9-42E7-96B5-6D43AFCF8365}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D6CE82C-1BB9-42E7-96B5-6D43AFCF8365}.Release|Any CPU.Build.0 = Release|Any CPU + {351E45E6-873B-40F5-97A2-CC02FB168A3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {351E45E6-873B-40F5-97A2-CC02FB168A3D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {351E45E6-873B-40F5-97A2-CC02FB168A3D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {351E45E6-873B-40F5-97A2-CC02FB168A3D}.Release|Any CPU.Build.0 = Release|Any CPU + {D76A820B-FD40-46AA-93BF-D640F69C4C7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D76A820B-FD40-46AA-93BF-D640F69C4C7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D76A820B-FD40-46AA-93BF-D640F69C4C7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D76A820B-FD40-46AA-93BF-D640F69C4C7E}.Release|Any CPU.Build.0 = Release|Any CPU + {305576D6-7440-4153-B6CA-3D65169E4438}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {305576D6-7440-4153-B6CA-3D65169E4438}.Debug|Any CPU.Build.0 = Debug|Any CPU + {305576D6-7440-4153-B6CA-3D65169E4438}.Release|Any CPU.ActiveCfg = Release|Any CPU + {305576D6-7440-4153-B6CA-3D65169E4438}.Release|Any CPU.Build.0 = Release|Any CPU + {DE7B8F3D-392B-4C0D-8B70-40CA1F4BB15A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DE7B8F3D-392B-4C0D-8B70-40CA1F4BB15A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DE7B8F3D-392B-4C0D-8B70-40CA1F4BB15A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DE7B8F3D-392B-4C0D-8B70-40CA1F4BB15A}.Release|Any CPU.Build.0 = Release|Any CPU + {E170997A-90FF-4179-991F-B5C36746C969}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E170997A-90FF-4179-991F-B5C36746C969}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E170997A-90FF-4179-991F-B5C36746C969}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E170997A-90FF-4179-991F-B5C36746C969}.Release|Any CPU.Build.0 = Release|Any CPU + {6E54FD68-0FC8-4252-8F4B-714823C58FAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6E54FD68-0FC8-4252-8F4B-714823C58FAB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E54FD68-0FC8-4252-8F4B-714823C58FAB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6E54FD68-0FC8-4252-8F4B-714823C58FAB}.Release|Any CPU.Build.0 = Release|Any CPU + {9FEFFA46-5403-42CD-87A4-9B2AC30BD7B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9FEFFA46-5403-42CD-87A4-9B2AC30BD7B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9FEFFA46-5403-42CD-87A4-9B2AC30BD7B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9FEFFA46-5403-42CD-87A4-9B2AC30BD7B6}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {6E54FD68-0FC8-4252-8F4B-714823C58FAB} = {5B01AC0C-8DFA-400C-8137-984F67227682} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E17C1DB7-407D-4734-9AE4-541062EAF3FA} + EndGlobalSection +EndGlobal diff --git a/Brizco.Common/Brizco.Common.csproj b/Brizco.Common/Brizco.Common.csproj new file mode 100644 index 0000000..88b9ccf --- /dev/null +++ b/Brizco.Common/Brizco.Common.csproj @@ -0,0 +1,21 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + + + + diff --git a/Brizco.Common/CommonConfig.cs b/Brizco.Common/CommonConfig.cs new file mode 100644 index 0000000..8f82c91 --- /dev/null +++ b/Brizco.Common/CommonConfig.cs @@ -0,0 +1,7 @@ +namespace Brizco.Common +{ + public class CommonConfig + { + + } +} \ No newline at end of file diff --git a/Brizco.Common/Extensions/AssertExtensions.cs b/Brizco.Common/Extensions/AssertExtensions.cs new file mode 100644 index 0000000..1da2851 --- /dev/null +++ b/Brizco.Common/Extensions/AssertExtensions.cs @@ -0,0 +1,30 @@ +using System.Collections; + +namespace Brizco.Common.Extensions +{ + public static class AssertExtensions + { + public static void NotNull(T obj, string name, string message = null) + where T : class + { + if (obj is null) + throw new ArgumentNullException($"{name} : {typeof(T)}", message); + } + + public static void NotNull(T? obj, string name, string message = null) + where T : struct + { + if (!obj.HasValue) + throw new ArgumentNullException($"{name} : {typeof(T)}", message); + } + + public static void NotEmpty(T obj, string name, string message = null, T defaultValue = null) + where T : class + { + if (obj == defaultValue + || obj is string str && string.IsNullOrWhiteSpace(str) + || obj is IEnumerable list && !list.Cast().Any()) + throw new ArgumentException("Argument is empty : " + message, $"{name} : {typeof(T)}"); + } + } +} \ No newline at end of file diff --git a/Brizco.Common/Extensions/ClassDisplayExtensions.cs b/Brizco.Common/Extensions/ClassDisplayExtensions.cs new file mode 100644 index 0000000..b60357b --- /dev/null +++ b/Brizco.Common/Extensions/ClassDisplayExtensions.cs @@ -0,0 +1,39 @@ +using Brizco.Common.Models.Entity; + +namespace Brizco.Common.Extensions +{ + public static class ClassDisplayExtensions + { + public static string GetDisplayAttributeName() + { + var attrs = + Attribute.GetCustomAttributes(typeof(T)); + + foreach (var attr in attrs) + { + var displayAttribute = attr as ClassDisplay; + if (displayAttribute == null) + continue; + return displayAttribute.GetName(); + } + + return null; + } + + public static string GetDisplayAttributeDescription() + { + var attrs = + Attribute.GetCustomAttributes(typeof(T)); + + foreach (var attr in attrs) + { + var displayAttribute = attr as ClassDisplay; + if (displayAttribute == null) + continue; + return displayAttribute.GetDescription(); + } + + return null; + } + } +} \ No newline at end of file diff --git a/Brizco.Common/Extensions/DateTimeExtensions.cs b/Brizco.Common/Extensions/DateTimeExtensions.cs new file mode 100644 index 0000000..48c39e1 --- /dev/null +++ b/Brizco.Common/Extensions/DateTimeExtensions.cs @@ -0,0 +1,61 @@ +using MD.PersianDateTime.Standard; + +namespace Brizco.Common.Extensions +{ + public static class DateTimeExtensions + { + public static string GetPersianDayOfWeek(this DayOfWeek dayOfWeek) + { + switch (dayOfWeek) + { + case DayOfWeek.Friday: + return "جمعه"; + case DayOfWeek.Monday: + return "دوشنبه"; + case DayOfWeek.Saturday: + return "شنبه"; + case DayOfWeek.Sunday: + return "یکشنبه"; + case DayOfWeek.Thursday: + return "پنج شنبه"; + case DayOfWeek.Tuesday: + return "سه شنبه"; + case DayOfWeek.Wednesday: + return "چهارشنبه"; + } + + return ""; + } + + public static DateTime UnixTimeStampToDateTime(double unixTimeStamp) + { + // Unix timestamp is seconds past epoch + var dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + dtDateTime = dtDateTime.AddMilliseconds(unixTimeStamp).ToLocalTime(); + return dtDateTime; + } + + public static long DateTimeToUnixTimeStamp(DateTime dateTime) + { + return ((DateTimeOffset)dateTime).ToUnixTimeMilliseconds(); + } + + public static int DifferenceByDay(DateTime originDateTime, DateTime destDateTime) + { + return (int)(destDateTime - originDateTime).TotalDays; + } + + public static int DifferenceByHoure(DateTime originDateTime, DateTime destDateTime) + { + return (int)(destDateTime - originDateTime).TotalHours; + } + + public static TimeSpan Difference(DateTime originDateTime, DateTime destDateTime) + { + var durateion = (destDateTime - originDateTime).Duration(); + return durateion; + } + public static PersianDateTime ToPersianDateTime(this DateTime dateTime) + => new PersianDateTime(dateTime); + } +} \ No newline at end of file diff --git a/Brizco.Common/Extensions/EnumExtensions.cs b/Brizco.Common/Extensions/EnumExtensions.cs new file mode 100644 index 0000000..4c702ba --- /dev/null +++ b/Brizco.Common/Extensions/EnumExtensions.cs @@ -0,0 +1,55 @@ +using System.ComponentModel.DataAnnotations; +using System.Reflection; + +namespace Brizco.Common.Extensions +{ + public enum DisplayProperty + { + Description, + GroupName, + Name, + Prompt, + ShortName, + Order + } + + public static class EnumExtensions + { + public static IEnumerable GetEnumValues(this T input) where T : struct + { + if (!typeof(T).IsEnum) + throw new NotSupportedException(); + + return Enum.GetValues(input.GetType()).Cast(); + } + + public static IEnumerable GetEnumFlags(this T input) where T : struct + { + if (!typeof(T).IsEnum) + throw new NotSupportedException(); + + foreach (var value in Enum.GetValues(input.GetType())) + if ((input as Enum).HasFlag(value as Enum)) + yield return (T)value; + } + + public static string ToDisplay(this Enum value, DisplayProperty property = DisplayProperty.Name) + { + AssertExtensions.NotNull(value, nameof(value)); + + var attribute = value.GetType().GetField(value.ToString()) + .GetCustomAttributes(false).FirstOrDefault(); + + if (attribute == null) + return value.ToString(); + + var propValue = attribute.GetType().GetProperty(property.ToString()).GetValue(attribute, null); + return propValue.ToString(); + } + + public static Dictionary ToDictionary(this Enum value) + { + return Enum.GetValues(value.GetType()).Cast().ToDictionary(p => Convert.ToInt32(p), q => ToDisplay(q)); + } + } +} \ No newline at end of file diff --git a/Brizco.Common/Extensions/NewtonJsonExtensions.cs b/Brizco.Common/Extensions/NewtonJsonExtensions.cs new file mode 100644 index 0000000..baab2ec --- /dev/null +++ b/Brizco.Common/Extensions/NewtonJsonExtensions.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Brizco.Common.Extensions +{ + public static class NewtonJsonExtensions + { + public static JsonSerializerSettings CamelCaseSerialize => + new() + { + ContractResolver = new DefaultContractResolver + { + NamingStrategy = new CamelCaseNamingStrategy() + }, + Formatting = Formatting.Indented + }; + } +} \ No newline at end of file diff --git a/Brizco.Common/Extensions/PhoneNumberExtensions.cs b/Brizco.Common/Extensions/PhoneNumberExtensions.cs new file mode 100644 index 0000000..094ea2b --- /dev/null +++ b/Brizco.Common/Extensions/PhoneNumberExtensions.cs @@ -0,0 +1,41 @@ +using System.Text.RegularExpressions; + +namespace Brizco.Common.Extensions; +public static class PhoneNumberExtensions +{ + public static bool CheckPhoneNumber(string phoneNumber) + { + var regex = new Regex(@"(^(989|0989|\+989|09|9)[0-9]{9}$)"); + return regex.IsMatch(phoneNumber); + } + public static string GetVerifyFromPhoneNumber(string phoneNumber) + { + var dateTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute / 5, 0); + var timeStamp = ((DateTimeOffset)dateTime).ToUnixTimeMilliseconds() / 10000; + + int FTD = int.Parse(string.Format("{0}{1}{2}", + GetSumDigit(int.Parse(string.Format("{0}{1}{2}", phoneNumber[5], phoneNumber[7], phoneNumber[9]))) % 10, + GetSumDigit(int.Parse(string.Format("{0}{1}{2}", phoneNumber[4], phoneNumber[6], phoneNumber[8]))) % 10, + GetSumDigit(int.Parse(string.Format("{0}{1}{2}", phoneNumber[10], phoneNumber[9], phoneNumber[8]))) % 10)); + int ATD = GetSumDigit(((int)timeStamp % 1000) + FTD); + timeStamp = (int)timeStamp / 1000; + int BTD = GetSumDigit(((int)timeStamp % 1000) + ATD); + timeStamp = (int)timeStamp / 1000; + int CTD = GetSumDigit(((int)timeStamp % 1000) + ATD); + FTD = GetSumDigit(FTD); + if (ATD % 2 == 0) + return string.Format("{0}{1}{2}{3}", GetSumDigit(ATD) % 10, GetSumDigit(BTD) % 10, GetSumDigit(CTD) % 10, GetSumDigit(FTD) % 10); + else + return string.Format("{0}{1}{2}{3}", ATD % 10, BTD % 10, CTD % 10, FTD % 10); + } + private static int GetSumDigit(int number) + { + string sNumber = number.ToString(); + int total = 0; + foreach (var s in sNumber) + total += int.Parse(s.ToString()); + return total; + } + + +} \ No newline at end of file diff --git a/Brizco.Common/Extensions/PropertyExtensions.cs b/Brizco.Common/Extensions/PropertyExtensions.cs new file mode 100644 index 0000000..d524ef2 --- /dev/null +++ b/Brizco.Common/Extensions/PropertyExtensions.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; +using System.Reflection; + +namespace Brizco.Common.Extensions +{ + public static class PropertyExtensions + { + public static string GetPropertyDisplayName(this MemberInfo propertyExpression) + { + var memberInfo = propertyExpression; + var attr = memberInfo.GetCustomAttributes().FirstOrDefault(); + if (attr == null) return memberInfo.Name; + + return attr.Name; + } + } +} \ No newline at end of file diff --git a/Brizco.Common/Extensions/RandomExtensions.cs b/Brizco.Common/Extensions/RandomExtensions.cs new file mode 100644 index 0000000..e8632cb --- /dev/null +++ b/Brizco.Common/Extensions/RandomExtensions.cs @@ -0,0 +1,12 @@ +namespace Brizco.Common.Extensions +{ + public static class RandomExtensions + { + public static T RandomItem(List originList) + { + var random = new Random(DateTime.Now.Millisecond); + var rand = random.Next(0, originList.Count - 1); + return originList[rand]; + } + } +} \ No newline at end of file diff --git a/Brizco.Common/Extensions/StringExtensions.cs b/Brizco.Common/Extensions/StringExtensions.cs new file mode 100644 index 0000000..3c16382 --- /dev/null +++ b/Brizco.Common/Extensions/StringExtensions.cs @@ -0,0 +1,165 @@ +namespace Brizco.Common.Extensions +{ + public static class StringExtensions + { + public static string ToPriceWhitPriceType(this long price, string priceType) + { + return price.ToString("N0") + " " + priceType; + } + + public static string ToPriceWhitPriceType(this decimal price, string priceType) + { + return price.ToString("N0") + " " + priceType; + } + + public static string ToPriceWhitPriceType(this double price, string priceType) + { + return price.ToString("N0") + " " + priceType; + } + + public static bool HasValue(this string value, bool ignoreWhiteSpace = true) + { + return ignoreWhiteSpace ? !string.IsNullOrWhiteSpace(value) : !string.IsNullOrEmpty(value); + } + + public static int ToInt(this string value) + { + return Convert.ToInt32(value); + } + + public static decimal ToDecimal(this string value) + { + return Convert.ToDecimal(value); + } + + public static string ToNumeric(this int value) + { + return value.ToString("N0"); //"123,456" + } + + public static string ToNumeric(this decimal value) + { + return value.ToString("N0"); + } + + public static string ToCurrency(this int value) + { + //fa-IR => current culture currency symbol => ریال + //123456 => "123,123ریال" + return value.ToString("C0"); + } + + public static string ToCurrency(this decimal value) + { + return value.ToString("C0"); + } + + public static string En2Fa(this string str) + { + return str.Replace("0", "۰") + .Replace("1", "۱") + .Replace("2", "۲") + .Replace("3", "۳") + .Replace("4", "۴") + .Replace("5", "۵") + .Replace("6", "۶") + .Replace("7", "۷") + .Replace("8", "۸") + .Replace("9", "۹"); + } + + public static string Fa2En(this string str) + { + return str.Replace("۰", "0") + .Replace("۱", "1") + .Replace("۲", "2") + .Replace("۳", "3") + .Replace("۴", "4") + .Replace("۵", "5") + .Replace("۶", "6") + .Replace("۷", "7") + .Replace("۸", "8") + .Replace("۹", "9") + //iphone numeric + .Replace("٠", "0") + .Replace("١", "1") + .Replace("٢", "2") + .Replace("٣", "3") + .Replace("٤", "4") + .Replace("٥", "5") + .Replace("٦", "6") + .Replace("٧", "7") + .Replace("٨", "8") + .Replace("٩", "9"); + } + + public static string FixPersianChars(this string str) + { + return str.Replace("ﮎ", "ک") + .Replace("ﮏ", "ک") + .Replace("ﮐ", "ک") + .Replace("ﮑ", "ک") + .Replace("ك", "ک") + .Replace("ي", "ی") + .Replace(" ", " ") + .Replace("‌", " ") + .Replace("ھ", "ه"); //.Replace("ئ", "ی"); + } + + public static string CleanString(this string str) + { + return str.Trim().FixPersianChars().Fa2En().NullIfEmpty(); + } + + public static string NullIfEmpty(this string str) + { + return str?.Length == 0 ? null : str; + } + + public static string GetId(int length = 8) + { + return Guid.NewGuid().ToString("N").Substring(0, length); + } + + public static string ConvertTo3Digit(this string str) + { + str = string.Concat(str.Split(',')); + var array = str.ToCharArray(); + Array.Reverse(array); + str = new string(array); + + var newStr = ""; + for (var i = 0; i < str.Length; i++) + { + newStr += str[i]; + if ((i + 1) % 3 == 0) + newStr += ","; + } + + var newarray = newStr.ToCharArray(); + Array.Reverse(newarray); + newStr = new string(newarray); + if (newStr.Length > 0 && newStr[0] == ',') + newStr = newStr.Substring(1); + + return newStr; + } + + public static string ConvertToOrginal(this string str) + { + return string.Concat(str.Split(',')); + } + + public static string CheckPhoneNumber(string phoneNumber) + { + if (phoneNumber.Substring(0, 3).Contains("+98")) + phoneNumber = phoneNumber.Replace("+98", "0"); + else if (phoneNumber.Substring(0, 2).Contains("98")) + phoneNumber = string.Concat("0", phoneNumber.Substring(2)); + else if (phoneNumber[0] != '0') + phoneNumber = string.Concat("0", phoneNumber); + + return phoneNumber; + } + } +} \ No newline at end of file diff --git a/Brizco.Common/Extensions/ValidationExtensions.cs b/Brizco.Common/Extensions/ValidationExtensions.cs new file mode 100644 index 0000000..0ab4fbf --- /dev/null +++ b/Brizco.Common/Extensions/ValidationExtensions.cs @@ -0,0 +1,21 @@ +namespace Brizco.Common.Extensions +{ + public static class ValidationExtensions + { + public static bool CheckDateIs(this DateTime dateTime, DateTime From, DateTime To) + { + if (dateTime.Date > To.Date && dateTime.Date < From.Date) + return true; + return false; + } + + public static bool CheckDateFromToNow(this DateTime dateTime, int fromDays = 5) + { + var From = DateTime.Now.AddDays(-fromDays); + var To = DateTime.Now; + if (dateTime.Date > To.Date && dateTime.Date < From.Date) + return true; + return false; + } + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Api/AccessToken.cs b/Brizco.Common/Models/Api/AccessToken.cs new file mode 100644 index 0000000..d7dd878 --- /dev/null +++ b/Brizco.Common/Models/Api/AccessToken.cs @@ -0,0 +1,51 @@ +using System.IdentityModel.Tokens.Jwt; + +namespace Brizco.Common.Models.Api +{ + public class AccessToken + { + public AccessToken() + { + } + + public AccessToken(JwtSecurityToken securityToken) + { + access_token = new JwtSecurityTokenHandler().WriteToken(securityToken); + token_type = "Bearer"; + expire_in_datetime = securityToken.ValidTo; + expires_in = (int)(securityToken.ValidTo - DateTime.UtcNow).TotalSeconds; + } + + public string access_token { get; set; } = string.Empty; + public string refresh_token { get; set; } = string.Empty; + public string token_type { get; set; } = string.Empty; + public int expires_in { get; set; } + public string token_id { get; set; } = string.Empty; + public DateTime expire_in_datetime { get; set; } + public string BearerToken => $"Bearer {access_token}"; + } + + public class AccessToken + { + public AccessToken() + { + } + + public AccessToken(JwtSecurityToken securityToken) + { + access_token = new JwtSecurityTokenHandler().WriteToken(securityToken); + token_type = "Bearer"; + expires_in = (int)(securityToken.ValidTo - DateTime.UtcNow).TotalSeconds; + } + + public string access_token { get; set; } = string.Empty; + public string ig_access_token { get; set; } = string.Empty; + public string refresh_token { get; set; } = string.Empty; + public string token_type { get; set; } = string.Empty; + public int expires_in { get; set; } + public TUser User { get; set; } + + public string BearerToken => $"Bearer {access_token}"; + public List Permissions { get; set; } + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Api/ApiResultStatusCode.cs b/Brizco.Common/Models/Api/ApiResultStatusCode.cs new file mode 100644 index 0000000..aa8cb77 --- /dev/null +++ b/Brizco.Common/Models/Api/ApiResultStatusCode.cs @@ -0,0 +1,42 @@ +using System.ComponentModel.DataAnnotations; +using System.Net; + +namespace Brizco.Common.Models.Api +{ + public enum ApiResultStatusCode + { + [Display(Name = "عملیات با موفقیت انجام شد")] + Success = HttpStatusCode.OK, + + [Display(Name = "خطایی در سرور رخ داده است")] + ServerError = HttpStatusCode.InternalServerError, + + [Display(Name = "پارامتر های ارسالی معتبر نیستند")] + BadRequest = HttpStatusCode.BadRequest, + + [Display(Name = "یافت نشد")] + NotFound = HttpStatusCode.NotFound, + + [Display(Name = "لیست خالی است")] + ListEmpty = 4, + + [Display(Name = "خطایی در پردازش رخ داد")] + LogicError = 5, + + [Display(Name = "موجودی کیف پول کافی نمی باشد")] + WalletBalanceNoEnough = 6, + + [Display(Name = "خطای احراز هویت")] + UnAuthorized = HttpStatusCode.Unauthorized, + + [Display(Name = "سرور خاموش شده است لطفا منتظر بمانید")] + ServerDown = HttpStatusCode.ServiceUnavailable, + + [Display(Name = "در ارسال پیامک مورد نظر مشکلی رخ داده است")] + SendSmsError = 7, + + + [Display(Name = "در ارسال درخواست به سرورهای دیگر مشکلی رخ داده است")] + RefitError = 8 + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Api/AppSettings.cs b/Brizco.Common/Models/Api/AppSettings.cs new file mode 100644 index 0000000..8a30569 --- /dev/null +++ b/Brizco.Common/Models/Api/AppSettings.cs @@ -0,0 +1,7 @@ +namespace Brizco.Common.Models.Api +{ + public class AppSettings + { + public bool Seeded { get; set; } + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Api/FileUploadRequest.cs b/Brizco.Common/Models/Api/FileUploadRequest.cs new file mode 100644 index 0000000..628564d --- /dev/null +++ b/Brizco.Common/Models/Api/FileUploadRequest.cs @@ -0,0 +1,15 @@ +namespace Brizco.Common.Models.Api +{ + public enum FileUploadType + { + Handout, + Video, + Image + } + public class FileUploadRequest + { + public string StringBaseFile { get; set; } + public string FileName { get; set; } + public FileUploadType FileUploadType { get; set; } + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Api/HealthCheck.cs b/Brizco.Common/Models/Api/HealthCheck.cs new file mode 100644 index 0000000..a552b30 --- /dev/null +++ b/Brizco.Common/Models/Api/HealthCheck.cs @@ -0,0 +1,9 @@ +namespace Brizco.Common.Models.Api +{ + public class HealthCheck + { + public bool Health { get; set; } + public string Version { get; set; } + public string TotalMemory { get; set; } + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Api/ResponseFile.cs b/Brizco.Common/Models/Api/ResponseFile.cs new file mode 100644 index 0000000..8b178dc --- /dev/null +++ b/Brizco.Common/Models/Api/ResponseFile.cs @@ -0,0 +1,9 @@ +namespace Brizco.Common.Models.Api +{ + public class ResponseFile + { + public string Url { get; set; } + public string Location { get; set; } + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Api/TokenRequest.cs b/Brizco.Common/Models/Api/TokenRequest.cs new file mode 100644 index 0000000..93d54c3 --- /dev/null +++ b/Brizco.Common/Models/Api/TokenRequest.cs @@ -0,0 +1,18 @@ +namespace Brizco.Common.Models.Api +{ + public class TokenRequest + { + public string grant_type { get; set; } + public string username { get; set; } + public string password { get; set; } + public string refresh_token { get; set; } + public string scope { get; set; } + + public string client_id { get; set; } + public string client_secret { get; set; } + + public string login_hash { get; set; } + public string device_id { get; set; } + public string license_id { get; set; } + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Claims/ApplicationClaims.cs b/Brizco.Common/Models/Claims/ApplicationClaims.cs new file mode 100644 index 0000000..bff2ad9 --- /dev/null +++ b/Brizco.Common/Models/Claims/ApplicationClaims.cs @@ -0,0 +1,11 @@ +namespace Brizco.Common.Models.Claims; +public static class ApplicationClaims +{ + //public static ClaimDto ManageProducts { get; } = new ClaimDto + //{ + // Type = CustomClaimType.Permission, + // Value = ApplicationPermission.ManageProducts, + // Title = "دسترسی کامل به محصولات", + // Detail = "دسترسی به افزودن و مدیریت محصولات فروشگاه شما" + //}; +} diff --git a/Brizco.Common/Models/Claims/ApplicationPermission.cs b/Brizco.Common/Models/Claims/ApplicationPermission.cs new file mode 100644 index 0000000..d9a1055 --- /dev/null +++ b/Brizco.Common/Models/Claims/ApplicationPermission.cs @@ -0,0 +1,5 @@ +namespace Brizco.Common.Models.Claims; +public static class ApplicationPermission +{ + //public const string ManageProducts = nameof(ManageProducts); +} diff --git a/Brizco.Common/Models/Claims/ClaimDto.cs b/Brizco.Common/Models/Claims/ClaimDto.cs new file mode 100644 index 0000000..8c84499 --- /dev/null +++ b/Brizco.Common/Models/Claims/ClaimDto.cs @@ -0,0 +1,20 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Security.Claims; + +namespace Brizco.Common.Models.Claims; +public class ClaimDto : INotifyPropertyChanged +{ + public string Value { get; set; } = string.Empty; + public string Type { get; set; } = string.Empty; + public string Title { get; set; } = string.Empty; + public string Detail { get; set; } = string.Empty; + public bool IsSelected { get; set; } = false; + + public event PropertyChangedEventHandler PropertyChanged; + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + public Claim GetClaim => new Claim(Type, Value); +} diff --git a/Brizco.Common/Models/Claims/CustomClaimType.cs b/Brizco.Common/Models/Claims/CustomClaimType.cs new file mode 100644 index 0000000..0189cb5 --- /dev/null +++ b/Brizco.Common/Models/Claims/CustomClaimType.cs @@ -0,0 +1,7 @@ +namespace Brizco.Common.Models.Claims +{ + public static class CustomClaimType + { + public static string Permission { get; } = "Permission"; + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Entity/ApiEntity.cs b/Brizco.Common/Models/Entity/ApiEntity.cs new file mode 100644 index 0000000..98108cc --- /dev/null +++ b/Brizco.Common/Models/Entity/ApiEntity.cs @@ -0,0 +1,29 @@ +namespace Brizco.Common.Models.Entity; +public abstract class ApiEntity : IApiEntity +{ + [Key] + public Guid Id { get; set; } + + [Display(Name = "تاریخ حذف")] + public DateTime RemovedAt { get; set; } + + [Display(Name = "تاریخ ساخت")] + public DateTime CreatedAt { get; set; } + + [Display(Name = "ساخته شده توسط")] + public string CreatedBy { get; set; } = string.Empty; + + [Display(Name = "حذف شده")] + public bool IsRemoved { get; set; } + [Display(Name = "حذف شده توسط")] + public string RemovedBy { get; set; } = string.Empty; + [Display(Name = "اخرین تغییر در")] + public DateTime ModifiedAt { get; set; } + + [Display(Name = "اخرین تغییر توسط")] + public string ModifiedBy { get; set; } = string.Empty; + + + [NotMapped] + public PersianDateTime CreationPersianDate => new PersianDateTime(CreatedAt); +} diff --git a/Brizco.Common/Models/Entity/ClassDisplay.cs b/Brizco.Common/Models/Entity/ClassDisplay.cs new file mode 100644 index 0000000..1c354b8 --- /dev/null +++ b/Brizco.Common/Models/Entity/ClassDisplay.cs @@ -0,0 +1,25 @@ +namespace Brizco.Common.Models.Entity +{ + [AttributeUsage(AttributeTargets.Class)] + public class ClassDisplay : Attribute + { + private readonly string _description; + private readonly string _name; + + public ClassDisplay(string name, string description) + { + _name = name; + _description = description; + } + + public string GetName() + { + return _name; + } + + public string GetDescription() + { + return _description; + } + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Entity/IApiEntity.cs b/Brizco.Common/Models/Entity/IApiEntity.cs new file mode 100644 index 0000000..2f6ccf6 --- /dev/null +++ b/Brizco.Common/Models/Entity/IApiEntity.cs @@ -0,0 +1,13 @@ +namespace Brizco.Common.Models.Entity +{ + public interface IApiEntity + { + string CreatedBy { get; set; } + string ModifiedBy { get; set; } + string RemovedBy { get; set; } + bool IsRemoved { get; set; } + DateTime CreatedAt { get; set; } + DateTime RemovedAt { get; set; } + DateTime ModifiedAt { get; set; } + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Exception/AppException.cs b/Brizco.Common/Models/Exception/AppException.cs new file mode 100644 index 0000000..f46df91 --- /dev/null +++ b/Brizco.Common/Models/Exception/AppException.cs @@ -0,0 +1,26 @@ +using System.Runtime.Serialization; +using Brizco.Common.Models.Api; + +namespace Brizco.Common.Models.Exception +{ + [Serializable()] + public class AppException : System.Exception + { + protected AppException(SerializationInfo info, StreamingContext context) : base(info, context) { } + + public ApiResultStatusCode StatusCode { get; set; } + public AppException() + { + StatusCode = ApiResultStatusCode.ServerError; + } + public AppException(string message) : base(message) + { + StatusCode = ApiResultStatusCode.ServerError; + } + + public AppException(string message, ApiResultStatusCode statusCode) : base(message) + { + StatusCode = statusCode; + } + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Exception/BaseApiException.cs b/Brizco.Common/Models/Exception/BaseApiException.cs new file mode 100644 index 0000000..39bdaa7 --- /dev/null +++ b/Brizco.Common/Models/Exception/BaseApiException.cs @@ -0,0 +1,91 @@ +using System.Net; +using System.Runtime.Serialization; +using Brizco.Common.Models.Api; + +namespace Brizco.Common.Models.Exception +{ + [Serializable()] + public class BaseApiException : System.Exception + { + protected BaseApiException(SerializationInfo info, StreamingContext context) : base(info, context) { } + public BaseApiException() + : this(ApiResultStatusCode.ServerError) + { + } + + public BaseApiException(ApiResultStatusCode statusCode) + : this(statusCode, null) + { + } + + public BaseApiException(string message) + : this(ApiResultStatusCode.ServerError, message) + { + } + + public BaseApiException(ApiResultStatusCode statusCode, string message) + : this(statusCode, message, HttpStatusCode.InternalServerError) + { + } + + public BaseApiException(string message, object additionalData) : this(ApiResultStatusCode.ServerError, message, additionalData) + { + } + + public BaseApiException(ApiResultStatusCode statusCode, object additionalData) : this(statusCode, null, additionalData) + { + } + + public BaseApiException(ApiResultStatusCode statusCode, string message, object additionalData) + : this(statusCode, message, HttpStatusCode.InternalServerError, additionalData) + { + } + + public BaseApiException(ApiResultStatusCode statusCode, string message, HttpStatusCode httpStatusCode) + : this(statusCode, message, httpStatusCode, null) + { + } + + public BaseApiException(ApiResultStatusCode statusCode, string message, HttpStatusCode httpStatusCode, object additionalData) + : this(statusCode, message, httpStatusCode, null, additionalData) + { + } + + public BaseApiException(string message, System.Exception exception) + : this(ApiResultStatusCode.ServerError, message, exception) + { + } + + public BaseApiException(string message, System.Exception exception, object additionalData) + : this(ApiResultStatusCode.ServerError, message, exception, additionalData) + { + } + + public BaseApiException(ApiResultStatusCode statusCode, string message, System.Exception exception) + : this(statusCode, message, HttpStatusCode.InternalServerError, exception) + { + } + + public BaseApiException(ApiResultStatusCode statusCode, string message, System.Exception exception, object additionalData) + : this(statusCode, message, HttpStatusCode.InternalServerError, exception, additionalData) + { + } + + public BaseApiException(ApiResultStatusCode statusCode, string message, HttpStatusCode httpStatusCode, System.Exception exception) + : this(statusCode, message, httpStatusCode, exception, null) + { + } + + public BaseApiException(ApiResultStatusCode statusCode, string message, HttpStatusCode httpStatusCode, System.Exception exception, object additionalData) + : base(message, exception) + { + ApiStatusCode = statusCode; + HttpStatusCode = httpStatusCode; + AdditionalData = additionalData; + } + + public HttpStatusCode HttpStatusCode { get; set; } + public ApiResultStatusCode ApiStatusCode { get; set; } + public object AdditionalData { get; set; } + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/IScopedDependency.cs b/Brizco.Common/Models/IScopedDependency.cs new file mode 100644 index 0000000..da76769 --- /dev/null +++ b/Brizco.Common/Models/IScopedDependency.cs @@ -0,0 +1,6 @@ +namespace Brizco.Common.Models +{ + public interface IScopedDependency + { + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Mapper/BaseDto.cs b/Brizco.Common/Models/Mapper/BaseDto.cs new file mode 100644 index 0000000..f155583 --- /dev/null +++ b/Brizco.Common/Models/Mapper/BaseDto.cs @@ -0,0 +1,97 @@ +using System.ComponentModel; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using Brizco.Common.Models.Exception; + +namespace Brizco.Common.Models.Mapper +{ + /// + /// Base Dto Class initial map config between entity and dto + /// + /// Type of Dto Class + /// Type of Entity Class + public abstract class BaseDto : INotifyPropertyChanged, IBaseDto where TEntity : class where TDto : class + { + public int Id { get; set; } + public static Expression> ProjectToDto + { + get => GetProjectToDto(); + } + private static Expression> GetProjectToDto() + { + var assembly = typeof(TEntity).Assembly; + var mapperName = $"{typeof(TEntity).Name}Mapper"; + var mapperType = assembly.GetTypes()?.FirstOrDefault(t => t.Name.Contains(mapperName)); + if (mapperType == null) + throw new AppException($"{typeof(TEntity).Name}Mapper Not Found!"); + if (typeof(TDto).Name.Contains("SDto")) + { + var projectProperty = mapperType.GetProperty("ProjectToSDto"); + if (projectProperty == null) + throw new AppException($"{typeof(TEntity).Name}Mapper Dont Have ProjectTo"); + return projectProperty.GetValue(null, null) as Expression>; + } + else if (typeof(TDto).Name.Contains("LDto")) + { + var projectProperty = mapperType.GetProperty("ProjectToLDto"); + if (projectProperty == null) + throw new AppException($"{typeof(TEntity).Name}Mapper Dont Have ProjectTo"); + return projectProperty.GetValue(null, null) as Expression>; + } + else + throw new AppException($"{typeof(TDto).Name} Projection Not Implemented"); + } + public virtual bool Compare(object obj) + { + if(obj is BaseDto objDto) + return objDto.Id == this.Id; + return Equals(obj); + } + + public TDto Clone() + { + return (TDto)MemberwiseClone(); + } + + public TEntity ToEntity() + { + var assembly = typeof(TEntity).Assembly; + var mapperName = $"{typeof(TEntity).Name}Mapper"; + var mapperType = assembly.GetTypes()?.FirstOrDefault(t => t.Name.Contains(mapperName)); + var toEntityMethodInfo = mapperType.GetMethod($"AdaptTo{typeof(TEntity).Name}"); + var parms = new[] { this }; + var entity = toEntityMethodInfo.Invoke(null, parms); + if (entity is TEntity o) + return o; + return null; + } + + public static TDto FromEntity(TEntity model) + { + var assembly = typeof(TEntity).Assembly; + var mapperName = $"{typeof(TEntity).Name}Mapper"; + var mapperType = assembly.GetTypes()?.FirstOrDefault(t => t.Name.Contains(mapperName)); + var toDtoMethodInfo = mapperType.GetMethod("AdaptToDto"); + var parms = new[] { model }; + var dto = toDtoMethodInfo.Invoke(null, parms); + if (dto is TDto o) + return o; + return null; + } + + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + protected bool SetField(ref T field, T value, [CallerMemberName] string propertyName = null) + { + if (EqualityComparer.Default.Equals(field, value)) return false; + field = value; + OnPropertyChanged(propertyName); + return true; + } + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Mapper/IBaseDto.cs b/Brizco.Common/Models/Mapper/IBaseDto.cs new file mode 100644 index 0000000..083106b --- /dev/null +++ b/Brizco.Common/Models/Mapper/IBaseDto.cs @@ -0,0 +1,11 @@ +using System.Linq.Expressions; + +namespace Brizco.Common.Models.Mapper +{ + public interface IBaseDto + { + int Id { get; set; } + bool Compare(object obj); + static Expression> ProjectToDto; + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Report/ChartUnit.cs b/Brizco.Common/Models/Report/ChartUnit.cs new file mode 100644 index 0000000..9ec5808 --- /dev/null +++ b/Brizco.Common/Models/Report/ChartUnit.cs @@ -0,0 +1,38 @@ +namespace Brizco.Common.Models.Report +{ + public class ChartUnit + { + public List Values { get; set; } = new(); + + public List ValuesStr + { + get { return Values.Select(v => v.ToString()).ToList(); } + } + + public List Labels { get; set; } = new(); + } + + public class ChartUnitIQuery + { + public IQueryable Values { get; set; } + + public List ValuesStr + { + get { return Values.Select(v => v.ToString()).ToList(); } + } + + public IQueryable Labels { get; set; } + } + + public class ChartUnit + { + public List Values { get; set; } = new(); + + public List ValuesStr + { + get { return Values.Select(v => v.ToString()).ToList(); } + } + + public List Labels { get; set; } = new(); + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Report/ReportRequest.cs b/Brizco.Common/Models/Report/ReportRequest.cs new file mode 100644 index 0000000..9bd60b0 --- /dev/null +++ b/Brizco.Common/Models/Report/ReportRequest.cs @@ -0,0 +1,24 @@ +namespace Brizco.Common.Models.Report +{ + public enum ReportType + { + ByDate, + ByValue, + BySelected + } + + public class ReportRequestProp + { + public string PropertyName { get; set; } + public string PropertyValue { get; set; } + } + + public class ReportRequest + { + public string TableType { get; set; } + public ReportType ReportType { get; set; } + public DateTime FromDateTime { get; set; } + public DateTime ToDateTime { get; set; } + public List ReportRequestProps { get; set; } = new(); + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Report/ReportResult.cs b/Brizco.Common/Models/Report/ReportResult.cs new file mode 100644 index 0000000..7af92d2 --- /dev/null +++ b/Brizco.Common/Models/Report/ReportResult.cs @@ -0,0 +1,34 @@ +namespace Brizco.Common.Models.Report +{ + public class ReportResult + { + private List _rows; + + public List Rows + { + get + { + if (_rows == null) + _rows = new List(); + return _rows; + } + set => _rows = value; + } + } + + public class ReportRow + { + private List _cells; + + public List Cells + { + get + { + if (_cells == null) + _cells = new List(); + return _cells; + } + set => _cells = value; + } + } +} \ No newline at end of file diff --git a/Brizco.Common/Models/Report/ReportableItem.cs b/Brizco.Common/Models/Report/ReportableItem.cs new file mode 100644 index 0000000..f4a36c8 --- /dev/null +++ b/Brizco.Common/Models/Report/ReportableItem.cs @@ -0,0 +1,89 @@ +using System.Collections; +using System.Reflection; + +namespace Brizco.Common.Models.Report +{ + public interface IReportableItem + { + string Name { get; set; } + string PropertyName { get; set; } + object DefaultValue { get; set; } + object SelectedValue { get; set; } + string ItemType { get; } + object Element { get; set; } + } + + public class BoolReportable : IReportableItem + { + public BoolReportable() + { + ItemType = GetType().Name; + } + + public string Name { get; set; } + public string PropertyName { get; set; } + public object DefaultValue { get; set; } + public object SelectedValue { get; set; } + public string ItemType { get; } + public object Element { get; set; } + } + + public class ListReportable : IReportableItem + { + public ListReportable() + { + ItemType = GetType().Name; + } + + public IList List { get; set; } + public IList ConvertedList { get; set; } + public string DisplayMemberPath { get; set; } + public string SelectedMemberPath { get; set; } + public string ListItemType { get; set; } + public string Name { get; set; } + public string PropertyName { get; set; } + public object DefaultValue { get; set; } + public object SelectedValue { get; set; } + public string ItemType { get; } + public object Element { get; set; } + } + + public class EnumReportable : IReportableItem + { + public EnumReportable() + { + ItemType = GetType().Name; + } + + public string EnumTypeName { get; set; } + + public string Name { get; set; } + public string PropertyName { get; set; } + public object DefaultValue { get; set; } + public object SelectedValue { get; set; } + public string ItemType { get; } + public object Element { get; set; } + + public Type EnumType(Assembly domainAssembly) + { + var types = domainAssembly.GetTypes(); + var type = types.FirstOrDefault(t => t.Name == EnumTypeName); + return type; + } + } + + public class NumericReportable : IReportableItem + { + public NumericReportable() + { + ItemType = GetType().Name; + } + + public string Name { get; set; } + public string PropertyName { get; set; } + public object DefaultValue { get; set; } + public object SelectedValue { get; set; } + public string ItemType { get; } + public object Element { get; set; } + } +} \ No newline at end of file diff --git a/Brizco.Core/Brizco.Core.csproj b/Brizco.Core/Brizco.Core.csproj new file mode 100644 index 0000000..cfadb03 --- /dev/null +++ b/Brizco.Core/Brizco.Core.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/Brizco.Core/CoreConfig.cs b/Brizco.Core/CoreConfig.cs new file mode 100644 index 0000000..5760565 --- /dev/null +++ b/Brizco.Core/CoreConfig.cs @@ -0,0 +1,7 @@ +namespace Brizco.Core +{ + public class CoreConfig + { + + } +} \ No newline at end of file diff --git a/Brizco.Domain/Brizco.Domain.csproj b/Brizco.Domain/Brizco.Domain.csproj new file mode 100644 index 0000000..ea5bd08 --- /dev/null +++ b/Brizco.Domain/Brizco.Domain.csproj @@ -0,0 +1,43 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Brizco.Domain/DomainConfig.cs b/Brizco.Domain/DomainConfig.cs new file mode 100644 index 0000000..765b12e --- /dev/null +++ b/Brizco.Domain/DomainConfig.cs @@ -0,0 +1,7 @@ +namespace Brizco.Domain +{ + public class DomainConfig + { + + } +} \ No newline at end of file diff --git a/Brizco.Domain/Entities/ApplicationRole.cs b/Brizco.Domain/Entities/ApplicationRole.cs new file mode 100644 index 0000000..034d9dd --- /dev/null +++ b/Brizco.Domain/Entities/ApplicationRole.cs @@ -0,0 +1,6 @@ +namespace Brizco.Domain.Entities; + +public class ApplicationRole : IdentityRole +{ + +} \ No newline at end of file diff --git a/Brizco.Domain/Entities/ApplicationUser.cs b/Brizco.Domain/Entities/ApplicationUser.cs new file mode 100644 index 0000000..b738a50 --- /dev/null +++ b/Brizco.Domain/Entities/ApplicationUser.cs @@ -0,0 +1,6 @@ +namespace Brizco.Domain.Entities; + +public class ApplicationUser : IdentityUser +{ +} + diff --git a/Brizco.Domain/Entities/Complex.cs b/Brizco.Domain/Entities/Complex.cs new file mode 100644 index 0000000..a195880 --- /dev/null +++ b/Brizco.Domain/Entities/Complex.cs @@ -0,0 +1,8 @@ +namespace Brizco.Domain.Entities; + +public class Complex : ApiEntity +{ + public string Name { get; set; } = string.Empty; + public string Address { get; set; } = string.Empty; + public string SupportPhone { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/Brizco.Domain/Entities/ComplexUser.cs b/Brizco.Domain/Entities/ComplexUser.cs new file mode 100644 index 0000000..fa16201 --- /dev/null +++ b/Brizco.Domain/Entities/ComplexUser.cs @@ -0,0 +1,11 @@ +namespace Brizco.Domain.Entities; +public class ComplexUser : ApiEntity +{ + public Guid UserId { get; set; } + public Guid ComplexId { get; set; } + + public Guid RoleId { get; set; } + + public ApplicationUser? User { get; set; } + public Complex? Complex { get; set; } +} \ No newline at end of file diff --git a/Brizco.Domain/Entities/Shift.cs b/Brizco.Domain/Entities/Shift.cs new file mode 100644 index 0000000..53700a8 --- /dev/null +++ b/Brizco.Domain/Entities/Shift.cs @@ -0,0 +1,10 @@ +namespace Brizco.Domain.Entities; + +public class Shift : ApiEntity +{ + public string Name { get; set; } = string.Empty; + public DayOfWeek DayOfWeek { get; set; } + public string Detail { get; set; } = string.Empty; + public TimeSpan StartAt { get; set; } + public TimeSpan EndAt { get; set; } +} \ No newline at end of file diff --git a/Brizco.Domain/Entities/SubTask.cs b/Brizco.Domain/Entities/SubTask.cs new file mode 100644 index 0000000..90e8758 --- /dev/null +++ b/Brizco.Domain/Entities/SubTask.cs @@ -0,0 +1,5 @@ +namespace Brizco.Domain.Entities; + +public class SubTask : Task +{ +} \ No newline at end of file diff --git a/Brizco.Domain/Entities/Task.cs b/Brizco.Domain/Entities/Task.cs new file mode 100644 index 0000000..3f0d166 --- /dev/null +++ b/Brizco.Domain/Entities/Task.cs @@ -0,0 +1,23 @@ +using TaskStatus = Brizco.Domain.Enums.TaskStatus; + +namespace Brizco.Domain.Entities; +public class Task : ApiEntity +{ + public TaskType Type { get; set; } + public string Title { get; set; } = string.Empty; + public string Detail { get; set; } = string.Empty; + public bool IsRelatedToShift { get; set; } + public bool IsRelatedToRole { get; set; } + public bool IsRelatedToPerson { get; set; } + public TaskStatus Status { get; set; } + public bool IsDisposable { get; set; } + public DateTime SetFor { get; set; } + public bool HasDisposed { get; set; } + public int Amount { get; set; } + public PurchaseAmountType AmountType { get; set; } + + public virtual ICollection? TaskUsers { get; set; } + public virtual ICollection? TaskShifts { get; set; } + public virtual ICollection? TaskRoles { get; set; } + +} diff --git a/Brizco.Domain/Entities/TaskRole.cs b/Brizco.Domain/Entities/TaskRole.cs new file mode 100644 index 0000000..6175c00 --- /dev/null +++ b/Brizco.Domain/Entities/TaskRole.cs @@ -0,0 +1,10 @@ +namespace Brizco.Domain.Entities; + +public class TaskRole : ApiEntity +{ + public Guid RoleId { get; set; } + public virtual ApplicationRole? Role { get; set; } + public Guid TaskId { get; set; } + public virtual Task? Task { get; set; } + +} \ No newline at end of file diff --git a/Brizco.Domain/Entities/TaskShift.cs b/Brizco.Domain/Entities/TaskShift.cs new file mode 100644 index 0000000..29b52e7 --- /dev/null +++ b/Brizco.Domain/Entities/TaskShift.cs @@ -0,0 +1,9 @@ +namespace Brizco.Domain.Entities; + +public class TaskShift : ApiEntity +{ + public Guid TaskId { get; set; } + public virtual Task? Task { get; set; } + public Guid ShiftId { get; set; } + public virtual Shift? Shift { get; set; } +} \ No newline at end of file diff --git a/Brizco.Domain/Entities/TaskUser.cs b/Brizco.Domain/Entities/TaskUser.cs new file mode 100644 index 0000000..0a778f0 --- /dev/null +++ b/Brizco.Domain/Entities/TaskUser.cs @@ -0,0 +1,9 @@ +namespace Brizco.Domain.Entities; + +public class TaskUser : ApiEntity +{ + public Guid UserId { get; set; } + public virtual ApplicationUser? User { get; set; } + public Guid TaskId { get; set; } + public virtual Task? Task { get; set; } +} \ No newline at end of file diff --git a/Brizco.Domain/Enums/PurchaseAmountType.cs b/Brizco.Domain/Enums/PurchaseAmountType.cs new file mode 100644 index 0000000..0071531 --- /dev/null +++ b/Brizco.Domain/Enums/PurchaseAmountType.cs @@ -0,0 +1,11 @@ +namespace Brizco.Domain.Enums; + +public enum PurchaseAmountType +{ + [Display(Name = "کلیوگرم")] + Kilogram, + [Display(Name = "عدد")] + Numerical, + [Display(Name = "بسته")] + Pack +} \ No newline at end of file diff --git a/Brizco.Domain/Enums/TaskStatus.cs b/Brizco.Domain/Enums/TaskStatus.cs new file mode 100644 index 0000000..b1fbc99 --- /dev/null +++ b/Brizco.Domain/Enums/TaskStatus.cs @@ -0,0 +1,13 @@ +namespace Brizco.Domain.Enums; + +public enum TaskStatus +{ + [Display(Name = "ساخته شده")] + Created, + [Display(Name = "در حال انجام")] + InProgress, + [Display(Name = "انجام شده")] + Done, + [Display(Name = "انجام نشده")] + UnDone +} \ No newline at end of file diff --git a/Brizco.Domain/Enums/TaskType.cs b/Brizco.Domain/Enums/TaskType.cs new file mode 100644 index 0000000..dd80521 --- /dev/null +++ b/Brizco.Domain/Enums/TaskType.cs @@ -0,0 +1,7 @@ +namespace Brizco.Domain.Enums; + +public enum TaskType +{ + SubTask, + Purchase +} \ No newline at end of file diff --git a/Brizco.Domain/FodyWeavers.xml b/Brizco.Domain/FodyWeavers.xml new file mode 100644 index 0000000..d5abfed --- /dev/null +++ b/Brizco.Domain/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Brizco.Identity.Api/Brizco.Identity.Api.csproj b/Brizco.Identity.Api/Brizco.Identity.Api.csproj new file mode 100644 index 0000000..2a1c5a1 --- /dev/null +++ b/Brizco.Identity.Api/Brizco.Identity.Api.csproj @@ -0,0 +1,78 @@ + + + + net7.0 + enable + enable + Linux + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Brizco.Identity.Api/Controllers/V1/AccountController.cs b/Brizco.Identity.Api/Controllers/V1/AccountController.cs new file mode 100644 index 0000000..a0f3094 --- /dev/null +++ b/Brizco.Identity.Api/Controllers/V1/AccountController.cs @@ -0,0 +1,24 @@ +using Brizco.Identity.Api.WebFramework.Bases; +using Microsoft.AspNetCore.Authorization; + +namespace Brizco.Identity.Api.Controllers.V1; + +[ApiController] +[ApiResultFilter] +[Route("api/v{version:apiVersion}/[controller]")] +[ApiVersion("1")] +[AllowAnonymous] +public class AccountController : ControllerBase +{ + private readonly IUserService _userService; + + public AccountController(IUserService userService) + { + _userService = userService; + } + [HttpGet] + public async Task GetUsersAsync(CancellationToken cancellationToken) + { + return Ok(await _userService.GetUsersAsync(cancellationToken)); + } +} \ No newline at end of file diff --git a/Brizco.Identity.Api/Dockerfile b/Brizco.Identity.Api/Dockerfile new file mode 100644 index 0000000..d12db68 --- /dev/null +++ b/Brizco.Identity.Api/Dockerfile @@ -0,0 +1,21 @@ +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base +WORKDIR /app +EXPOSE 80 + +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +WORKDIR /src +COPY ["Brizco.Identity.Api/Brizco.Identity.Api.csproj", "Brizco.Identity.Api/"] +RUN dotnet restore "Brizco.Identity.Api/Brizco.Identity.Api.csproj" +COPY . . +WORKDIR "/src/Brizco.Identity.Api" +RUN dotnet build "Brizco.Identity.Api.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "Brizco.Identity.Api.csproj" -c Release -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Brizco.Identity.Api.dll"] \ No newline at end of file diff --git a/Brizco.Identity.Api/Domain/ApplicationContext.cs b/Brizco.Identity.Api/Domain/ApplicationContext.cs new file mode 100644 index 0000000..422364b --- /dev/null +++ b/Brizco.Identity.Api/Domain/ApplicationContext.cs @@ -0,0 +1,7 @@ +namespace Brizco.Identity.Api.Domain; +public class ApplicationContext : IdentityDbContext +{ + public ApplicationContext(DbContextOptions options) : base(options) + { + } +} diff --git a/Brizco.Identity.Api/Domain/Entities/ApplicationRole.cs b/Brizco.Identity.Api/Domain/Entities/ApplicationRole.cs new file mode 100644 index 0000000..491a3eb --- /dev/null +++ b/Brizco.Identity.Api/Domain/Entities/ApplicationRole.cs @@ -0,0 +1,6 @@ +namespace Brizco.Identity.Api.Domain.Entities; + +public class ApplicationRole : IdentityRole +{ + public Guid ComplexId { get; set; } +} \ No newline at end of file diff --git a/Brizco.Identity.Api/Domain/Entities/ApplicationUser.cs b/Brizco.Identity.Api/Domain/Entities/ApplicationUser.cs new file mode 100644 index 0000000..0d3e047 --- /dev/null +++ b/Brizco.Identity.Api/Domain/Entities/ApplicationUser.cs @@ -0,0 +1,7 @@ +namespace Brizco.Identity.Api.Domain.Entities; + +public class ApplicationUser : IdentityUser +{ + +} + diff --git a/Brizco.Identity.Api/FodyWeavers.xml b/Brizco.Identity.Api/FodyWeavers.xml new file mode 100644 index 0000000..d5abfed --- /dev/null +++ b/Brizco.Identity.Api/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Brizco.Identity.Api/Migrations/20230830071509_init.Designer.cs b/Brizco.Identity.Api/Migrations/20230830071509_init.Designer.cs new file mode 100644 index 0000000..6729fcf --- /dev/null +++ b/Brizco.Identity.Api/Migrations/20230830071509_init.Designer.cs @@ -0,0 +1,279 @@ +// +using System; +using Brizco.Identity.Api.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Brizco.Identity.Api.Migrations +{ + [DbContext(typeof(ApplicationContext))] + [Migration("20230830071509_init")] + partial class init + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Brizco.Identity.Api.Domain.Entities.ApplicationRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ComplexId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Brizco.Identity.Api.Domain.Entities.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Brizco.Identity.Api/Migrations/20230830071509_init.cs b/Brizco.Identity.Api/Migrations/20230830071509_init.cs new file mode 100644 index 0000000..e9edd92 --- /dev/null +++ b/Brizco.Identity.Api/Migrations/20230830071509_init.cs @@ -0,0 +1,224 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Brizco.Identity.Api.Migrations +{ + /// + public partial class init : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ComplexId = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "boolean", nullable: false), + PasswordHash = table.Column(type: "text", nullable: true), + SecurityStamp = table.Column(type: "text", nullable: true), + ConcurrencyStamp = table.Column(type: "text", nullable: true), + PhoneNumber = table.Column(type: "text", nullable: true), + PhoneNumberConfirmed = table.Column(type: "boolean", nullable: false), + TwoFactorEnabled = table.Column(type: "boolean", nullable: false), + LockoutEnd = table.Column(type: "timestamp with time zone", nullable: true), + LockoutEnabled = table.Column(type: "boolean", nullable: false), + AccessFailedCount = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + RoleId = table.Column(type: "uuid", nullable: false), + ClaimType = table.Column(type: "text", nullable: true), + ClaimValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + UserId = table.Column(type: "uuid", nullable: false), + ClaimType = table.Column(type: "text", nullable: true), + ClaimValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(type: "text", nullable: false), + ProviderKey = table.Column(type: "text", nullable: false), + ProviderDisplayName = table.Column(type: "text", nullable: true), + UserId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(type: "uuid", nullable: false), + RoleId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(type: "uuid", nullable: false), + LoginProvider = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: false), + Value = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + } + } +} diff --git a/Brizco.Identity.Api/Migrations/ApplicationContextModelSnapshot.cs b/Brizco.Identity.Api/Migrations/ApplicationContextModelSnapshot.cs new file mode 100644 index 0000000..72882f5 --- /dev/null +++ b/Brizco.Identity.Api/Migrations/ApplicationContextModelSnapshot.cs @@ -0,0 +1,276 @@ +// +using System; +using Brizco.Identity.Api.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Brizco.Identity.Api.Migrations +{ + [DbContext(typeof(ApplicationContext))] + partial class ApplicationContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Brizco.Identity.Api.Domain.Entities.ApplicationRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ComplexId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Brizco.Identity.Api.Domain.Entities.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Brizco.Identity.Api/Models/JwtSettings.cs b/Brizco.Identity.Api/Models/JwtSettings.cs new file mode 100644 index 0000000..7567a31 --- /dev/null +++ b/Brizco.Identity.Api/Models/JwtSettings.cs @@ -0,0 +1,8 @@ +namespace Brizco.Identity.Api.Models; + +public class JwtSettings +{ + public string SecretKey { get; set; } = string.Empty; + public string Audience { get; set; } = string.Empty; + public string Issuer { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/Brizco.Identity.Api/Models/SiteSettings.cs b/Brizco.Identity.Api/Models/SiteSettings.cs new file mode 100644 index 0000000..5eadc72 --- /dev/null +++ b/Brizco.Identity.Api/Models/SiteSettings.cs @@ -0,0 +1,6 @@ +namespace Brizco.Identity.Api.Models; + +public class SiteSettings +{ + public string BaseUrl { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/Brizco.Identity.Api/Program.cs b/Brizco.Identity.Api/Program.cs new file mode 100644 index 0000000..455d90b --- /dev/null +++ b/Brizco.Identity.Api/Program.cs @@ -0,0 +1,52 @@ +using Brizco.Identity.Api.Services; + +var builder = WebApplication.CreateBuilder(args); +builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); +builder.Host.UseSerilog(); +LoggerConfig.ConfigureSerilog(); + + +var configuration = builder.Configuration; +var siteSetting = configuration.GetSection(nameof(SiteSettings)).Get(); +builder.Services.Configure(configuration.GetSection(nameof(SiteSettings))); + +builder.Services.AddControllers(); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddCustomDbContext(configuration); +builder.Services.AddCustomController(); +builder.Services.AddCustomSwagger(siteSetting.BaseUrl); +builder.Services.AddCustomCores(); +builder.Services.AddCustomIdentity(); +builder.Services.AddCustomApiVersioning(); +builder.Services.AddDataProtection(); + + +builder.Host.ConfigureContainer(builder => +{ + + var assemblyD = typeof(Program).Assembly; + builder.RegisterAssemblyTypes(assemblyD) + .AssignableTo() + .AsImplementedInterfaces() + .InstancePerLifetimeScope(); + +}); +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseCustomSwagger(siteSetting.BaseUrl, builder.Environment); +} + +app.UseAuthorization(); +app.UseAuthentication(); +app.UseExceptionHandlerMiddleware(); +app.MapControllers(); +app.UseCors("CorsPolicy"); + +DbInitializerService.InitilizeDB(app); + + + +app.Run(); diff --git a/Brizco.Identity.Api/Properties/launchSettings.json b/Brizco.Identity.Api/Properties/launchSettings.json new file mode 100644 index 0000000..811825f --- /dev/null +++ b/Brizco.Identity.Api/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5245" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Docker": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", + "environmentVariables": { + "ASPNETCORE_URLS": "http://+:80" + }, + "publishAllPorts": true, + "DockerfileRunArguments": "--network=mother -p 5245:80" + } + }, + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:25754", + "sslPort": 0 + } + } +} \ No newline at end of file diff --git a/Brizco.Identity.Api/Services/Contracts/IDbInitializerService.cs b/Brizco.Identity.Api/Services/Contracts/IDbInitializerService.cs new file mode 100644 index 0000000..521c197 --- /dev/null +++ b/Brizco.Identity.Api/Services/Contracts/IDbInitializerService.cs @@ -0,0 +1,6 @@ +namespace Brizco.Identity.Api.Services.Contracts; + +public interface IDbInitializerService : IScopedDependency +{ + void Initialize(); +} \ No newline at end of file diff --git a/Brizco.Identity.Api/Services/Contracts/IUserService.cs b/Brizco.Identity.Api/Services/Contracts/IUserService.cs new file mode 100644 index 0000000..2a472af --- /dev/null +++ b/Brizco.Identity.Api/Services/Contracts/IUserService.cs @@ -0,0 +1,6 @@ +namespace Brizco.Identity.Api.Services.Contracts; + +public interface IUserService : IScopedDependency +{ + Task> GetUsersAsync(CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/Brizco.Identity.Api/Services/DbInitializerService.cs b/Brizco.Identity.Api/Services/DbInitializerService.cs new file mode 100644 index 0000000..9a1e176 --- /dev/null +++ b/Brizco.Identity.Api/Services/DbInitializerService.cs @@ -0,0 +1,38 @@ +namespace Brizco.Identity.Api.Services; + +public class DbInitializerService : IDbInitializerService +{ + private readonly ApplicationContext _context; + private readonly ILogger _logger; + + public DbInitializerService( ApplicationContext context, ILogger logger) + { + _context = context; + _logger = logger; + } + public void Initialize() + { + try + { + _context.Database.Migrate(); + _logger.LogInformation("Migration SUCCESS !!!!"); + } + catch (Exception e) + { + _logger.LogError(e, e.Message); + } + } + + public static void InitilizeDB(IApplicationBuilder app) + { + var scopeFactory = app.ApplicationServices.GetRequiredService(); + using (var scope = scopeFactory.CreateScope()) + { + var identityDbInitialize = scope.ServiceProvider.GetService(); + if (identityDbInitialize != null) + { + identityDbInitialize.Initialize(); + } + } + } +} \ No newline at end of file diff --git a/Brizco.Identity.Api/Services/UserService.cs b/Brizco.Identity.Api/Services/UserService.cs new file mode 100644 index 0000000..6c96662 --- /dev/null +++ b/Brizco.Identity.Api/Services/UserService.cs @@ -0,0 +1,28 @@ +using Brizco.Common.Models.Exception; + +namespace Brizco.Identity.Api.Services; + +public class UserService : IUserService +{ + private readonly UserManager _userManager; + + public UserService(UserManager userManager) + { + _userManager = userManager; + } + + public async Task> GetUsersAsync(CancellationToken cancellationToken) + { + var res = await _userManager.CreateAsync(new ApplicationUser + { + Email = StringExtensions.GetId(8), + UserName = StringExtensions.GetId(9), + + }); + if (!res.Succeeded) + { + throw new AppException(res.Errors.ToString()); + } + return await _userManager.Users.ToListAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/Brizco.Identity.Api/WebFramework/Bases/ApiResult.cs b/Brizco.Identity.Api/WebFramework/Bases/ApiResult.cs new file mode 100644 index 0000000..a2c68f4 --- /dev/null +++ b/Brizco.Identity.Api/WebFramework/Bases/ApiResult.cs @@ -0,0 +1,118 @@ +namespace Brizco.Identity.Api.WebFramework.Bases +{ + public class ApiResult + { + public bool IsSuccess { get; set; } + public ApiResultStatusCode StatusCode { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string Message { get; set; } + public ApiResult(bool isSuccess, ApiResultStatusCode statusCode, string message = null) + { + IsSuccess = isSuccess; + StatusCode = statusCode; + Message = message ?? statusCode.ToDisplay(); + } + + #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 : ApiResult + where TData : class + { + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public TData Data { get; set; } + + public ApiResult(bool isSuccess, ApiResultStatusCode statusCode, TData data, string message = null) + : base(isSuccess, statusCode, message) + { + Data = data; + } + + #region Implicit Operators + public static implicit operator ApiResult(TData data) + { + return new ApiResult(true, ApiResultStatusCode.Success, data); + } + + public static implicit operator ApiResult(OkResult result) + { + return new ApiResult(true, ApiResultStatusCode.Success, null); + } + + public static implicit operator ApiResult(OkObjectResult result) + { + return new ApiResult(true, ApiResultStatusCode.Success, (TData)result.Value); + } + + public static implicit operator ApiResult(BadRequestResult result) + { + return new ApiResult(false, ApiResultStatusCode.BadRequest, null); + } + + 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, null, message); + } + + public static implicit operator ApiResult(ContentResult result) + { + return new ApiResult(true, ApiResultStatusCode.Success, null, result.Content); + } + + public static implicit operator ApiResult(NotFoundResult result) + { + return new ApiResult(false, ApiResultStatusCode.NotFound, null); + } + + public static implicit operator ApiResult(NotFoundObjectResult result) + { + return new ApiResult(false, ApiResultStatusCode.NotFound, (TData)result.Value); + } + #endregion + } +} diff --git a/Brizco.Identity.Api/WebFramework/Bases/ApiResultFilterAttribute.cs b/Brizco.Identity.Api/WebFramework/Bases/ApiResultFilterAttribute.cs new file mode 100644 index 0000000..029fc62 --- /dev/null +++ b/Brizco.Identity.Api/WebFramework/Bases/ApiResultFilterAttribute.cs @@ -0,0 +1,72 @@ +using Microsoft.AspNetCore.Mvc.Filters; + +namespace Brizco.Identity.Api.WebFramework.Bases +{ + public class ApiResultFilterAttribute : ActionFilterAttribute + { + public override void OnResultExecuting(ResultExecutingContext context) + { + if (context.Result is OkObjectResult okObjectResult) + { + var apiResult = new ApiResult(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(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(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); + } + } +} diff --git a/Brizco.Identity.Api/WebFramework/Configurations/ConfigureJwtBearerOptions.cs b/Brizco.Identity.Api/WebFramework/Configurations/ConfigureJwtBearerOptions.cs new file mode 100644 index 0000000..c67b547 --- /dev/null +++ b/Brizco.Identity.Api/WebFramework/Configurations/ConfigureJwtBearerOptions.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.Extensions.Options; + +namespace Brizco.Identity.Api.WebFramework.Configurations +{ + public class ConfigureJwtBearerOptions : IPostConfigureOptions + { + 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"]; + if (!string.IsNullOrEmpty(accessToken)) + { + context.Token = accessToken; + } + } + }; + } + } +} \ No newline at end of file diff --git a/Brizco.Identity.Api/WebFramework/Configurations/LoggerConfiguration.cs b/Brizco.Identity.Api/WebFramework/Configurations/LoggerConfiguration.cs new file mode 100644 index 0000000..412818b --- /dev/null +++ b/Brizco.Identity.Api/WebFramework/Configurations/LoggerConfiguration.cs @@ -0,0 +1,24 @@ +using Serilog; +using Serilog.Events; +using Serilog.Sinks.ElmahIo; +using Serilog.Sinks.SystemConsole.Themes; + +namespace Brizco.Identity.Api.WebFramework.Configurations +{ + public static class LoggerConfig + { + public static void ConfigureSerilog() + { + Log.Logger = new LoggerConfiguration() + .Enrich.FromLogContext() + .WriteTo.ElmahIo(new ElmahIoSinkOptions("279e90cbf4da4814a971a33df23ebd1e", new Guid("7fb24511-87b1-414e-933a-006f67e97ffc")) + { + MinimumLogEventLevel = LogEventLevel.Warning, + + }) + .WriteTo.Console(theme: AnsiConsoleTheme.Literate) + .CreateLogger(); + } + + } +} diff --git a/Brizco.Identity.Api/WebFramework/Configurations/PersianIdentityErrorDescriber.cs b/Brizco.Identity.Api/WebFramework/Configurations/PersianIdentityErrorDescriber.cs new file mode 100644 index 0000000..71ddb25 --- /dev/null +++ b/Brizco.Identity.Api/WebFramework/Configurations/PersianIdentityErrorDescriber.cs @@ -0,0 +1,27 @@ +namespace Brizco.Identity.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') به صورت بزرگ داشته باشد" }; } + } +} \ No newline at end of file diff --git a/Brizco.Identity.Api/WebFramework/Configurations/ServiceExtensions.cs b/Brizco.Identity.Api/WebFramework/Configurations/ServiceExtensions.cs new file mode 100644 index 0000000..79bdafb --- /dev/null +++ b/Brizco.Identity.Api/WebFramework/Configurations/ServiceExtensions.cs @@ -0,0 +1,145 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; +using System.Text; + +namespace Brizco.Identity.Api.WebFramework.Configurations +{ + public static class ServiceExtensions + { + public static void AddCustomDbContext(this IServiceCollection serviceCollection, IConfigurationRoot Configuration) + { + serviceCollection.AddDbContext(options => + { + options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); + options.UseNpgsql(Configuration.GetConnectionString("Postgres"), b => b.MigrationsAssembly("Brizco.Identity.Api")); + options.EnableServiceProviderCaching(false); + }, ServiceLifetime.Scoped); + AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", 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 AddCustomApiVersioning(this IServiceCollection serviceCollection) + { + serviceCollection.AddApiVersioning(options => + { + options.AssumeDefaultVersionWhenUnspecified = true; + options.DefaultApiVersion = new ApiVersion(1, 0); + options.ReportApiVersions = true; + }); + } + public static void AddCustomController(this IServiceCollection serviceCollection) + { + serviceCollection.AddControllers(options => { options.Filters.Add(new AuthorizeFilter()); }) + .AddControllersAsServices() + .AddNewtonsoftJson(options => + { + options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; + } + ); + } + + + public static void AddJwtCustomAuthentication(this IServiceCollection serviceCollection, JwtSettings jwtSettings) + { + serviceCollection.AddAuthentication(options => + { + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddCookie(IdentityConstants.ApplicationScheme, options => + { + + }) + .AddCookie(IdentityConstants.TwoFactorUserIdScheme, options => + { + + }) + .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.IncludeErrorDetails = true; + + options.Events = new JwtBearerEvents + { + OnMessageReceived = context => + { + var accessToken = context.Request.Query["access_token"]; + if (!string.IsNullOrEmpty(accessToken)) + context.Token = accessToken.ToString(); + var videoStorageOrigin = context.Request.Headers["X-Original-URI"].ToString(); + var videoToken = videoStorageOrigin.Split("?access_token=").Last(); + if (!string.IsNullOrEmpty(videoToken)) + context.Token = videoToken; + return Task.CompletedTask; + }, + OnForbidden = context => + { + context.Response.StatusCode = StatusCodes.Status403Forbidden; + return Task.CompletedTask; + }, + OnAuthenticationFailed = context => + { + context.Response.StatusCode = StatusCodes.Status401Unauthorized; + return Task.CompletedTask; + }, + OnChallenge = context => + { + + context.Response.StatusCode = StatusCodes.Status401Unauthorized; + return Task.CompletedTask; + } + }; + }); + + } + + public static void AddCustomIdentity(this IServiceCollection serviceCollection) + { + serviceCollection.AddIdentityCore(options => + { + options.Password.RequireLowercase = false; + options.Password.RequireUppercase = false; + options.Password.RequireDigit = false; + options.Password.RequireNonAlphanumeric = false; + options.User.RequireUniqueEmail = false; + }) + .AddRoles() + .AddSignInManager>() + .AddEntityFrameworkStores() + .AddDefaultTokenProviders() + .AddErrorDescriber(); + + } + } +} diff --git a/Brizco.Identity.Api/WebFramework/Configurations/SwaggerConfiguration.cs b/Brizco.Identity.Api/WebFramework/Configurations/SwaggerConfiguration.cs new file mode 100644 index 0000000..ee2f409 --- /dev/null +++ b/Brizco.Identity.Api/WebFramework/Configurations/SwaggerConfiguration.cs @@ -0,0 +1,339 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.OpenApi.Models; +using Pluralize.NET; +using Swashbuckle.AspNetCore.SwaggerGen; +using Swashbuckle.AspNetCore.SwaggerUI; + +namespace Brizco.Identity.Api.WebFramework.Configurations; +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 = "MCAS-Online Api Dacument", + Description = "MCAS-Online 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(); + + //set version "api/v{version}/[controller]" from current swagger doc verion + options.DocumentFilter(); + + //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() + .SelectMany(attr => attr.Versions) + .ToList(); + + return versions.Any(v => $"v{v.ToString()}" == version); + }); + + #endregion + + #region Security + + var url = $"{baseUrl}/api/v1/Auth/LoginSwagger"; + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n + Enter 'Bearer' [space] and then your token in the text input below. + \r\n\r\nExample: 'Bearer 12345abcdef'", + Name = "Authorization", + In = ParameterLocation.Header, + Type = SecuritySchemeType.ApiKey, + Scheme = "Bearer" + }); + + options.AddSecurityRequirement(new OpenApiSecurityRequirement() + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + }, + Name = "Bearer", + In = ParameterLocation.Header, + + }, + new List() + } + }); + + // options.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, new OpenApiSecurityScheme + // { + // In = ParameterLocation.Header, + // Description = "Please enter token", + // Name = "Authorization", + // Type = SecuritySchemeType.Http, + // BearerFormat = "JWT", + // Scheme = "bearer" + // }); + + // options.AddSecurityRequirement(new OpenApiSecurityRequirement + //{ + // { + // new OpenApiSecurityScheme + // { + // Reference = new OpenApiReference + // { + // Type=ReferenceType.SecurityScheme, + // Id="Bearer" + // } + // }, + // new string[]{} + // } + //}); + + //options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + //{ + // Type = SecuritySchemeType.OAuth2, + // Scheme = "Bearer", + // Name = "Authorization", + // In = ParameterLocation.Header, + // Flows = new OpenApiOAuthFlows + // { + // Password = new OpenApiOAuthFlow + // { + // TokenUrl = new Uri(url) + // } + // } + //}); + options.OperationFilter(true, "Bearer"); + + #endregion + + #region Customize + + options.OperationFilter(); + + #endregion + }); + } + + public static void UseCustomSwagger(this IApplicationBuilder app, string baseUrl, IWebHostEnvironment env) + { + app.UseSwagger(options => + { + options.SerializeAsV2 = true; + }); + + app.UseSwaggerUI(options => + { + //var sidebar = Path.Combine(env.ContentRootPath, "wwwroot/assets/swagger-ui/customSidebar.html"); + //options.HeadContent = File.ReadAllText(sidebar); + 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.OAuthClientId("clientname"); + options.OAuthRealm("your-realm"); + + 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 + } +} diff --git a/Brizco.Identity.Api/WebFramework/MiddleWares/ExceptionHandlerMiddleware.cs b/Brizco.Identity.Api/WebFramework/MiddleWares/ExceptionHandlerMiddleware.cs new file mode 100644 index 0000000..04a76c1 --- /dev/null +++ b/Brizco.Identity.Api/WebFramework/MiddleWares/ExceptionHandlerMiddleware.cs @@ -0,0 +1,182 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Net; +using Brizco.Common.Models.Exception; +using Brizco.Identity.Api.WebFramework.Bases; +using Microsoft.IdentityModel.Tokens; +using Newtonsoft.Json.Serialization; + +namespace Brizco.Identity.Api.WebFramework.MiddleWares +{ + public static class ExceptionHandlerMiddlewareExtensions + { + public static IApplicationBuilder UseExceptionHandlerMiddleware(this IApplicationBuilder applicationBuilder) + { + return applicationBuilder.UseMiddleware(); + } + } + public class ExceptionHandlerMiddleware + { + private readonly RequestDelegate _next; + private readonly IWebHostEnvironment _env; + private readonly ILogger _logger; + + public ExceptionHandlerMiddleware( + RequestDelegate next, + IWebHostEnvironment env, + ILogger logger) + { + _next = next; + _env = env; + _logger = logger; + } + + public async Task Invoke(HttpContext context) + { + string message = null; + HttpStatusCode httpStatusCode = HttpStatusCode.InternalServerError; + ApiResultStatusCode 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 + { + ["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)); + DefaultContractResolver contractResolver = new DefaultContractResolver + { + NamingStrategy = new CamelCaseNamingStrategy() + }; + + message = JsonConvert.SerializeObject(dic, new JsonSerializerSettings + { + ContractResolver = contractResolver, + Formatting = Formatting.Indented + }); + } + else + { + message = exception.Message; + } + + await WriteToResponseAsync(); + } + 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 + { + ["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); + + DefaultContractResolver contractResolver = new DefaultContractResolver + { + NamingStrategy = new CamelCaseNamingStrategy() + }; + + string 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 + { + ["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); + } + } + } + + } +} \ No newline at end of file diff --git a/Brizco.Identity.Api/appsettings.Development.json b/Brizco.Identity.Api/appsettings.Development.json new file mode 100644 index 0000000..25709d3 --- /dev/null +++ b/Brizco.Identity.Api/appsettings.Development.json @@ -0,0 +1,17 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "ConnectionStrings": { + "DockerCompose": "Host=pg_0;Username=berzco;Password=berzco;Database=BrizcoIdentityDB;", + "Postgres": "Host=pg-0,pg-1;Username=postgres;Password=xHTpBf4wC+bBeNg2pL6Ga7VEWKFJx7VPEUpqxwPFfOc2YYTVwFQuHfsiqoVeT9+6;Database=BrizcoIdentityDB;Load Balance Hosts=true;Target Session Attributes=primary" + + }, + + "SiteSettings": { + "BaseUrl": "http://localhost:5245" + } +} diff --git a/Brizco.Identity.Api/appsettings.json b/Brizco.Identity.Api/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/Brizco.Identity.Api/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/NuGet.config b/NuGet.config new file mode 100644 index 0000000..b9ed29d --- /dev/null +++ b/NuGet.config @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docker-compose.dcproj b/docker-compose.dcproj new file mode 100644 index 0000000..cfc35ca --- /dev/null +++ b/docker-compose.dcproj @@ -0,0 +1,18 @@ + + + + 2.1 + Linux + 9feffa46-5403-42cd-87a4-9b2ac30bd7b6 + LaunchBrowser + {Scheme}://localhost:{ServicePort}/swagger + berizco.api + + + + docker-compose.yml + + + + + \ No newline at end of file diff --git a/docker-compose.override.yml b/docker-compose.override.yml new file mode 100644 index 0000000..3a676e1 --- /dev/null +++ b/docker-compose.override.yml @@ -0,0 +1,15 @@ +version: '3.4' + +services: + brizco.identity.api: + environment: + - ASPNETCORE_ENVIRONMENT=Development + ports: + - "80" + + berizco.api: + environment: + - ASPNETCORE_ENVIRONMENT=Development + ports: + - "80" + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..8ba3754 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,42 @@ +version: '3.4' +networks: + brizco: + driver: bridge + +services: + brizco.identity.api: + image: ${DOCKER_REGISTRY-}brizcoidentityapi + depends_on: + - "postgres_image" + build: + context: . + dockerfile: Brizco.Identity.Api/Dockerfile + ports: + - "5245:80" + networks: + - brizco + + berizco.api: + image: ${DOCKER_REGISTRY-}berizcoapi + build: + context: . + dockerfile: Berizco.Api/Dockerfile + + + postgres_image: + image: postgres:latest + ports: + - "5432" + restart: always + volumes: + - db:/var/lib/postgresql/data + environment: + POSTGRES_USER: "berzco" + POSTGRES_PASSWORD: "berzco" + POSTGRES_DB: "berzcoDB" + networks: + - brizco +volumes: + db: + driver: local + diff --git a/launchSettings.json b/launchSettings.json new file mode 100644 index 0000000..d85b4ec --- /dev/null +++ b/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Docker Compose": { + "commandName": "DockerCompose", + "commandVersion": "1.0", + "serviceActions": { + "berizco.api": "StartDebugging", + "brizco.identity.api": "StartDebugging" + } + } + } +} \ No newline at end of file