Add project files.
parent
59850dc4a9
commit
c3d4e011ff
|
@ -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
|
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.10" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,18 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Berizco.Api.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class WeatherForecastController : ControllerBase
|
||||
{
|
||||
public WeatherForecastController()
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<WeatherForecast> Get()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"]
|
|
@ -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();
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,7 @@
|
|||
namespace Brizco.Infrastructure
|
||||
{
|
||||
public class InfrastructureConfig
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Pluralize.NET" Version="1.0.2" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.10" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.6.122" />
|
||||
<PackageReference Include="StackExchange.Redis.Extensions.Core" Version="9.1.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,7 @@
|
|||
namespace Brizco.Repository
|
||||
{
|
||||
public class RepositoryConfig
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,21 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MD.PersianDateTime.Standard" Version="2.5.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.32.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="MD.PersianDateTime.Standard" />
|
||||
<Using Include="System.ComponentModel.DataAnnotations" />
|
||||
<Using Include="System.ComponentModel.DataAnnotations.Schema" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,7 @@
|
|||
namespace Brizco.Common
|
||||
{
|
||||
public class CommonConfig
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using System.Collections;
|
||||
|
||||
namespace Brizco.Common.Extensions
|
||||
{
|
||||
public static class AssertExtensions
|
||||
{
|
||||
public static void NotNull<T>(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>(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>(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<object>().Any())
|
||||
throw new ArgumentException("Argument is empty : " + message, $"{name} : {typeof(T)}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using Brizco.Common.Models.Entity;
|
||||
|
||||
namespace Brizco.Common.Extensions
|
||||
{
|
||||
public static class ClassDisplayExtensions
|
||||
{
|
||||
public static string GetDisplayAttributeName<T>()
|
||||
{
|
||||
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<T>()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<T> GetEnumValues<T>(this T input) where T : struct
|
||||
{
|
||||
if (!typeof(T).IsEnum)
|
||||
throw new NotSupportedException();
|
||||
|
||||
return Enum.GetValues(input.GetType()).Cast<T>();
|
||||
}
|
||||
|
||||
public static IEnumerable<T> GetEnumFlags<T>(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<DisplayAttribute>(false).FirstOrDefault();
|
||||
|
||||
if (attribute == null)
|
||||
return value.ToString();
|
||||
|
||||
var propValue = attribute.GetType().GetProperty(property.ToString()).GetValue(attribute, null);
|
||||
return propValue.ToString();
|
||||
}
|
||||
|
||||
public static Dictionary<int, string> ToDictionary(this Enum value)
|
||||
{
|
||||
return Enum.GetValues(value.GetType()).Cast<Enum>().ToDictionary(p => Convert.ToInt32(p), q => ToDisplay(q));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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<DisplayAttribute>().FirstOrDefault();
|
||||
if (attr == null) return memberInfo.Name;
|
||||
|
||||
return attr.Name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
namespace Brizco.Common.Extensions
|
||||
{
|
||||
public static class RandomExtensions
|
||||
{
|
||||
public static T RandomItem<T>(List<T> originList)
|
||||
{
|
||||
var random = new Random(DateTime.Now.Millisecond);
|
||||
var rand = random.Next(0, originList.Count - 1);
|
||||
return originList[rand];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<TUser>
|
||||
{
|
||||
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<string> Permissions { get; set; }
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace Brizco.Common.Models.Api
|
||||
{
|
||||
public class AppSettings
|
||||
{
|
||||
public bool Seeded { get; set; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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 = "دسترسی به افزودن و مدیریت محصولات فروشگاه شما"
|
||||
//};
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
namespace Brizco.Common.Models.Claims;
|
||||
public static class ApplicationPermission
|
||||
{
|
||||
//public const string ManageProducts = nameof(ManageProducts);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace Brizco.Common.Models.Claims
|
||||
{
|
||||
public static class CustomClaimType
|
||||
{
|
||||
public static string Permission { get; } = "Permission";
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
namespace Brizco.Common.Models
|
||||
{
|
||||
public interface IScopedDependency
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Base Dto Class initial map config between entity and dto
|
||||
/// </summary>
|
||||
/// <typeparam name="TDto">Type of Dto Class</typeparam>
|
||||
/// <typeparam name="TEntity">Type of Entity Class</typeparam>
|
||||
public abstract class BaseDto<TDto, TEntity> : INotifyPropertyChanged, IBaseDto<TDto,TEntity> where TEntity : class where TDto : class
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public static Expression<Func<TEntity, TDto>> ProjectToDto
|
||||
{
|
||||
get => GetProjectToDto();
|
||||
}
|
||||
private static Expression<Func<TEntity, TDto>> 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<Func<TEntity, TDto>>;
|
||||
}
|
||||
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<Func<TEntity, TDto>>;
|
||||
}
|
||||
else
|
||||
throw new AppException($"{typeof(TDto).Name} Projection Not Implemented");
|
||||
}
|
||||
public virtual bool Compare(object obj)
|
||||
{
|
||||
if(obj is BaseDto<TDto,TEntity> 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<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
|
||||
field = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using System.Linq.Expressions;
|
||||
|
||||
namespace Brizco.Common.Models.Mapper
|
||||
{
|
||||
public interface IBaseDto<TDto,TEntity>
|
||||
{
|
||||
int Id { get; set; }
|
||||
bool Compare(object obj);
|
||||
static Expression<Func<TEntity, TDto>> ProjectToDto;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
namespace Brizco.Common.Models.Report
|
||||
{
|
||||
public class ChartUnit
|
||||
{
|
||||
public List<long> Values { get; set; } = new();
|
||||
|
||||
public List<string> ValuesStr
|
||||
{
|
||||
get { return Values.Select(v => v.ToString()).ToList(); }
|
||||
}
|
||||
|
||||
public List<string> Labels { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ChartUnitIQuery
|
||||
{
|
||||
public IQueryable<long> Values { get; set; }
|
||||
|
||||
public List<string> ValuesStr
|
||||
{
|
||||
get { return Values.Select(v => v.ToString()).ToList(); }
|
||||
}
|
||||
|
||||
public IQueryable<string> Labels { get; set; }
|
||||
}
|
||||
|
||||
public class ChartUnit<TValue>
|
||||
{
|
||||
public List<TValue> Values { get; set; } = new();
|
||||
|
||||
public List<string> ValuesStr
|
||||
{
|
||||
get { return Values.Select(v => v.ToString()).ToList(); }
|
||||
}
|
||||
|
||||
public List<string> Labels { get; set; } = new();
|
||||
}
|
||||
}
|
|
@ -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<ReportRequestProp> ReportRequestProps { get; set; } = new();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
namespace Brizco.Common.Models.Report
|
||||
{
|
||||
public class ReportResult
|
||||
{
|
||||
private List<ReportRow> _rows;
|
||||
|
||||
public List<ReportRow> Rows
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_rows == null)
|
||||
_rows = new List<ReportRow>();
|
||||
return _rows;
|
||||
}
|
||||
set => _rows = value;
|
||||
}
|
||||
}
|
||||
|
||||
public class ReportRow
|
||||
{
|
||||
private List<string> _cells;
|
||||
|
||||
public List<string> Cells
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_cells == null)
|
||||
_cells = new List<string>();
|
||||
return _cells;
|
||||
}
|
||||
set => _cells = value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,7 @@
|
|||
namespace Brizco.Core
|
||||
{
|
||||
public class CoreConfig
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Mapster" Version="7.3.0" />
|
||||
<PackageReference Include="Mapster.Core" Version="1.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="7.0.10" />
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="4.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Brizco.Common\Brizco.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Brizco.Common.Models.Entity" />
|
||||
<Using Include="Brizco.Domain.Enums" />
|
||||
<Using Include="Microsoft.AspNetCore.Identity" />
|
||||
<Using Include="System.ComponentModel" />
|
||||
<Using Include="System.ComponentModel.DataAnnotations" />
|
||||
<Using Include="System.Runtime.CompilerServices" />
|
||||
<Using Include="System.Security.Claims" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Enums\" />
|
||||
<Folder Include="Models\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,7 @@
|
|||
namespace Brizco.Domain
|
||||
{
|
||||
public class DomainConfig
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
namespace Brizco.Domain.Entities;
|
||||
|
||||
public class ApplicationRole : IdentityRole<Guid>
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
namespace Brizco.Domain.Entities;
|
||||
|
||||
public class ApplicationUser : IdentityUser<Guid>
|
||||
{
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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; }
|
||||
}
|
|
@ -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; }
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
namespace Brizco.Domain.Entities;
|
||||
|
||||
public class SubTask : Task
|
||||
{
|
||||
}
|
|
@ -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<TaskUser>? TaskUsers { get; set; }
|
||||
public virtual ICollection<TaskShift>? TaskShifts { get; set; }
|
||||
public virtual ICollection<TaskRole>? TaskRoles { get; set; }
|
||||
|
||||
}
|
|
@ -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; }
|
||||
|
||||
}
|
|
@ -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; }
|
||||
}
|
|
@ -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; }
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
namespace Brizco.Domain.Enums;
|
||||
|
||||
public enum PurchaseAmountType
|
||||
{
|
||||
[Display(Name = "کلیوگرم")]
|
||||
Kilogram,
|
||||
[Display(Name = "عدد")]
|
||||
Numerical,
|
||||
[Display(Name = "بسته")]
|
||||
Pack
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
namespace Brizco.Domain.Enums;
|
||||
|
||||
public enum TaskStatus
|
||||
{
|
||||
[Display(Name = "ساخته شده")]
|
||||
Created,
|
||||
[Display(Name = "در حال انجام")]
|
||||
InProgress,
|
||||
[Display(Name = "انجام شده")]
|
||||
Done,
|
||||
[Display(Name = "انجام نشده")]
|
||||
UnDone
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace Brizco.Domain.Enums;
|
||||
|
||||
public enum TaskType
|
||||
{
|
||||
SubTask,
|
||||
Purchase
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<PropertyChanged />
|
||||
</Weavers>
|
|
@ -0,0 +1,78 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Pluralize.NET" Version="1.0.2" />
|
||||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.7.1" />
|
||||
<PackageReference Include="MediatR.Extensions.Autofac.DependencyInjection" Version="11.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.1.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.8" />
|
||||
<PackageReference Include="Autofac" Version="7.1.0" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog" Version="3.0.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
|
||||
<PackageReference Include="Serilog.Sinks.ElmahIo" Version="5.0.37" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.PostgreSQL" Version="2.3.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Seq" Version="5.2.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.10" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.6.122" />
|
||||
<PackageReference Include="StackExchange.Redis.Extensions.Core" Version="9.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="7.0.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Brizco.Common\Brizco.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Autofac" />
|
||||
<Using Include="Autofac.Extensions.DependencyInjection" />
|
||||
<Using Include="Brizco.Common.Extensions" />
|
||||
<Using Include="Brizco.Common.Models" />
|
||||
<Using Include="Brizco.Common.Models.Api" />
|
||||
<Using Include="Brizco.Identity.Api" />
|
||||
<Using Include="Brizco.Identity.Api.Domain" />
|
||||
<Using Include="Brizco.Identity.Api.Domain.Entities" />
|
||||
<Using Include="Brizco.Identity.Api.Models" />
|
||||
<Using Include="Brizco.Identity.Api.Services.Contracts" />
|
||||
<Using Include="Brizco.Identity.Api.WebFramework.Configurations" />
|
||||
<Using Include="Brizco.Identity.Api.WebFramework.MiddleWares" />
|
||||
<Using Include="Microsoft.AspNetCore.Identity" />
|
||||
<Using Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
|
||||
<Using Include="Microsoft.AspNetCore.Mvc" />
|
||||
<Using Include="Microsoft.AspNetCore.Mvc.Authorization" />
|
||||
<Using Include="Microsoft.EntityFrameworkCore" />
|
||||
<Using Include="Newtonsoft.Json" />
|
||||
<Using Include="Serilog" />
|
||||
<Using Include="StackExchange.Redis.Extensions.Core.Configuration" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Models\" />
|
||||
<Folder Include="Services\Contracts\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -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<IActionResult> GetUsersAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await _userService.GetUsersAsync(cancellationToken));
|
||||
}
|
||||
}
|
|
@ -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"]
|
|
@ -0,0 +1,7 @@
|
|||
namespace Brizco.Identity.Api.Domain;
|
||||
public class ApplicationContext : IdentityDbContext<ApplicationUser,ApplicationRole,Guid>
|
||||
{
|
||||
public ApplicationContext(DbContextOptions<ApplicationContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
namespace Brizco.Identity.Api.Domain.Entities;
|
||||
|
||||
public class ApplicationRole : IdentityRole<Guid>
|
||||
{
|
||||
public Guid ComplexId { get; set; }
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace Brizco.Identity.Api.Domain.Entities;
|
||||
|
||||
public class ApplicationUser : IdentityUser<Guid>
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<PropertyChanged />
|
||||
</Weavers>
|
|
@ -0,0 +1,279 @@
|
|||
// <auto-generated />
|
||||
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
|
||||
{
|
||||
/// <inheritdoc />
|
||||
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<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("ComplexId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("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<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("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<System.Guid>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("RoleId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("RoleId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", 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<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Brizco.Identity.Api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class init : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoles",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
ComplexId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
NormalizedName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUsers",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
UserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
NormalizedUserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
Email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
NormalizedEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
EmailConfirmed = table.Column<bool>(type: "boolean", nullable: false),
|
||||
PasswordHash = table.Column<string>(type: "text", nullable: true),
|
||||
SecurityStamp = table.Column<string>(type: "text", nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true),
|
||||
PhoneNumber = table.Column<string>(type: "text", nullable: true),
|
||||
PhoneNumberConfirmed = table.Column<bool>(type: "boolean", nullable: false),
|
||||
TwoFactorEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||
LockoutEnd = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
LockoutEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||
AccessFailedCount = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoleClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
RoleId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
ClaimType = table.Column<string>(type: "text", nullable: true),
|
||||
ClaimValue = table.Column<string>(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<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
UserId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
ClaimType = table.Column<string>(type: "text", nullable: true),
|
||||
ClaimValue = table.Column<string>(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<string>(type: "text", nullable: false),
|
||||
ProviderKey = table.Column<string>(type: "text", nullable: false),
|
||||
ProviderDisplayName = table.Column<string>(type: "text", nullable: true),
|
||||
UserId = table.Column<Guid>(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<Guid>(type: "uuid", nullable: false),
|
||||
RoleId = table.Column<Guid>(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<Guid>(type: "uuid", nullable: false),
|
||||
LoginProvider = table.Column<string>(type: "text", nullable: false),
|
||||
Name = table.Column<string>(type: "text", nullable: false),
|
||||
Value = table.Column<string>(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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,276 @@
|
|||
// <auto-generated />
|
||||
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<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("ComplexId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("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<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("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<System.Guid>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("RoleId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("RoleId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", 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<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Brizco.Identity.Api.Domain.Entities.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
namespace Brizco.Identity.Api.Models;
|
||||
|
||||
public class SiteSettings
|
||||
{
|
||||
public string BaseUrl { get; set; } = string.Empty;
|
||||
}
|
|
@ -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<SiteSettings>();
|
||||
builder.Services.Configure<SiteSettings>(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<ContainerBuilder>(builder =>
|
||||
{
|
||||
|
||||
var assemblyD = typeof(Program).Assembly;
|
||||
builder.RegisterAssemblyTypes(assemblyD)
|
||||
.AssignableTo<IScopedDependency>()
|
||||
.AsImplementedInterfaces()
|
||||
.InstancePerLifetimeScope();
|
||||
|
||||
});
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseCustomSwagger(siteSetting.BaseUrl, builder.Environment);
|
||||
}
|
||||
|
||||
app.UseAuthorization();
|
||||
app.UseAuthentication();
|
||||
app.UseExceptionHandlerMiddleware();
|
||||
app.MapControllers();
|
||||
app.UseCors("CorsPolicy");
|
||||
|
||||
DbInitializerService.InitilizeDB(app);
|
||||
|
||||
|
||||
|
||||
app.Run();
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
namespace Brizco.Identity.Api.Services.Contracts;
|
||||
|
||||
public interface IDbInitializerService : IScopedDependency
|
||||
{
|
||||
void Initialize();
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
namespace Brizco.Identity.Api.Services.Contracts;
|
||||
|
||||
public interface IUserService : IScopedDependency
|
||||
{
|
||||
Task<List<ApplicationUser>> GetUsersAsync(CancellationToken cancellationToken);
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
namespace Brizco.Identity.Api.Services;
|
||||
|
||||
public class DbInitializerService : IDbInitializerService
|
||||
{
|
||||
private readonly ApplicationContext _context;
|
||||
private readonly ILogger<DbInitializerService> _logger;
|
||||
|
||||
public DbInitializerService( ApplicationContext context, ILogger<DbInitializerService> 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<IServiceScopeFactory>();
|
||||
using (var scope = scopeFactory.CreateScope())
|
||||
{
|
||||
var identityDbInitialize = scope.ServiceProvider.GetService<IDbInitializerService>();
|
||||
if (identityDbInitialize != null)
|
||||
{
|
||||
identityDbInitialize.Initialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using Brizco.Common.Models.Exception;
|
||||
|
||||
namespace Brizco.Identity.Api.Services;
|
||||
|
||||
public class UserService : IUserService
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
public UserService(UserManager<ApplicationUser> userManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public async Task<List<ApplicationUser>> 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);
|
||||
}
|
||||
}
|
|
@ -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<TData> : 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>(TData data)
|
||||
{
|
||||
return new ApiResult<TData>(true, ApiResultStatusCode.Success, data);
|
||||
}
|
||||
|
||||
public static implicit operator ApiResult<TData>(OkResult result)
|
||||
{
|
||||
return new ApiResult<TData>(true, ApiResultStatusCode.Success, null);
|
||||
}
|
||||
|
||||
public static implicit operator ApiResult<TData>(OkObjectResult result)
|
||||
{
|
||||
return new ApiResult<TData>(true, ApiResultStatusCode.Success, (TData)result.Value);
|
||||
}
|
||||
|
||||
public static implicit operator ApiResult<TData>(BadRequestResult result)
|
||||
{
|
||||
return new ApiResult<TData>(false, ApiResultStatusCode.BadRequest, null);
|
||||
}
|
||||
|
||||
public static implicit operator ApiResult<TData>(BadRequestObjectResult result)
|
||||
{
|
||||
var message = result.Value.ToString();
|
||||
if (result.Value is SerializableError errors)
|
||||
{
|
||||
var errorMessages = errors.SelectMany(p => (string[])p.Value).Distinct();
|
||||
message = string.Join(" | ", errorMessages);
|
||||
}
|
||||
return new ApiResult<TData>(false, ApiResultStatusCode.BadRequest, null, message);
|
||||
}
|
||||
|
||||
public static implicit operator ApiResult<TData>(ContentResult result)
|
||||
{
|
||||
return new ApiResult<TData>(true, ApiResultStatusCode.Success, null, result.Content);
|
||||
}
|
||||
|
||||
public static implicit operator ApiResult<TData>(NotFoundResult result)
|
||||
{
|
||||
return new ApiResult<TData>(false, ApiResultStatusCode.NotFound, null);
|
||||
}
|
||||
|
||||
public static implicit operator ApiResult<TData>(NotFoundObjectResult result)
|
||||
{
|
||||
return new ApiResult<TData>(false, ApiResultStatusCode.NotFound, (TData)result.Value);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -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<object>(true, ApiResultStatusCode.Success, okObjectResult.Value);
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = okObjectResult.StatusCode };
|
||||
}
|
||||
else if (context.Result is OkResult okResult)
|
||||
{
|
||||
var apiResult = new ApiResult(true, ApiResultStatusCode.Success);
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = okResult.StatusCode };
|
||||
}
|
||||
else if (context.Result is BadRequestResult badRequestResult)
|
||||
{
|
||||
var apiResult = new ApiResult(false, ApiResultStatusCode.BadRequest);
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = badRequestResult.StatusCode };
|
||||
}
|
||||
else if (context.Result is BadRequestObjectResult badRequestObjectResult)
|
||||
{
|
||||
var message = badRequestObjectResult.Value.ToString();
|
||||
|
||||
if (badRequestObjectResult.Value is SerializableError errors)
|
||||
{
|
||||
var errorMessages = errors.SelectMany(p => (string[])p.Value).Distinct();
|
||||
message = string.Join(" | ", errorMessages);
|
||||
}
|
||||
if (badRequestObjectResult.Value is ValidationProblemDetails problemDetails)
|
||||
{
|
||||
var errorMessages = problemDetails.Errors.Values.SelectMany(v => v);
|
||||
message = string.Join(" | ", errorMessages);
|
||||
}
|
||||
var apiResult = new ApiResult(false, ApiResultStatusCode.BadRequest, message);
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = badRequestObjectResult.StatusCode };
|
||||
}
|
||||
else if (context.Result is ContentResult contentResult)
|
||||
{
|
||||
var apiResult = new ApiResult(true, ApiResultStatusCode.Success, contentResult.Content);
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = contentResult.StatusCode };
|
||||
}
|
||||
else if (context.Result is NotFoundResult notFoundResult)
|
||||
{
|
||||
var apiResult = new ApiResult(false, ApiResultStatusCode.NotFound);
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = notFoundResult.StatusCode };
|
||||
}
|
||||
else if (context.Result is NotFoundObjectResult notFoundObjectResult)
|
||||
{
|
||||
var apiResult = new ApiResult<object>(false, ApiResultStatusCode.NotFound, notFoundObjectResult.Value);
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = notFoundObjectResult.StatusCode };
|
||||
}
|
||||
else if (context.Result is ObjectResult objectResult && objectResult.StatusCode == null
|
||||
&& !(objectResult.Value is ApiResult))
|
||||
{
|
||||
var apiResult = new ApiResult<object>(true, ApiResultStatusCode.Success, objectResult.Value);
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = objectResult.StatusCode };
|
||||
}
|
||||
else if (context.Result is ObjectResult objectResultBad && (objectResultBad.Value is ApiResult))
|
||||
{
|
||||
|
||||
var apiResult = objectResultBad.Value as ApiResult;
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = objectResultBad.StatusCode };
|
||||
}
|
||||
|
||||
base.OnResultExecuting(context);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Brizco.Identity.Api.WebFramework.Configurations
|
||||
{
|
||||
public class ConfigureJwtBearerOptions : IPostConfigureOptions<JwtBearerOptions>
|
||||
{
|
||||
public void PostConfigure(string name, JwtBearerOptions options)
|
||||
{
|
||||
var originalOnMessageReceived = options.Events.OnMessageReceived;
|
||||
options.Events.OnMessageReceived = async context =>
|
||||
{
|
||||
await originalOnMessageReceived(context);
|
||||
|
||||
if (string.IsNullOrEmpty(context.Token))
|
||||
{
|
||||
var accessToken = context.Request.Query["access_token"];
|
||||
if (!string.IsNullOrEmpty(accessToken))
|
||||
{
|
||||
context.Token = accessToken;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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') به صورت بزرگ داشته باشد" }; }
|
||||
}
|
||||
}
|
|
@ -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<ApplicationContext>(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<ApplicationUser>(options =>
|
||||
{
|
||||
options.Password.RequireLowercase = false;
|
||||
options.Password.RequireUppercase = false;
|
||||
options.Password.RequireDigit = false;
|
||||
options.Password.RequireNonAlphanumeric = false;
|
||||
options.User.RequireUniqueEmail = false;
|
||||
})
|
||||
.AddRoles<ApplicationRole>()
|
||||
.AddSignInManager<SignInManager<ApplicationUser>>()
|
||||
.AddEntityFrameworkStores<ApplicationContext>()
|
||||
.AddDefaultTokenProviders()
|
||||
.AddErrorDescriber<PersianIdentityErrorDescriber>();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<RemoveVersionParameters>();
|
||||
|
||||
//set version "api/v{version}/[controller]" from current swagger doc verion
|
||||
options.DocumentFilter<SetVersionInPaths>();
|
||||
|
||||
//Seperate and categorize end-points by doc version
|
||||
options.DocInclusionPredicate((version, desc) =>
|
||||
{
|
||||
if (!desc.TryGetMethodInfo(out var methodInfo)) return false;
|
||||
var versions = methodInfo.DeclaringType
|
||||
.GetCustomAttributes(true)
|
||||
.OfType<ApiVersionAttribute>()
|
||||
.SelectMany(attr => attr.Versions)
|
||||
.ToList();
|
||||
|
||||
return versions.Any(v => $"v{v.ToString()}" == version);
|
||||
});
|
||||
|
||||
#endregion
|
||||
|
||||
#region Security
|
||||
|
||||
var url = $"{baseUrl}/api/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<string>()
|
||||
}
|
||||
});
|
||||
|
||||
// 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<UnauthorizedResponsesOperationFilter>(true, "Bearer");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Customize
|
||||
|
||||
options.OperationFilter<ApplySummariesOperationFilter>();
|
||||
|
||||
#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
|
||||
}
|
||||
}
|
|
@ -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<ExceptionHandlerMiddleware>();
|
||||
}
|
||||
}
|
||||
public class ExceptionHandlerMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IWebHostEnvironment _env;
|
||||
private readonly ILogger<ExceptionHandlerMiddleware> _logger;
|
||||
|
||||
public ExceptionHandlerMiddleware(
|
||||
RequestDelegate next,
|
||||
IWebHostEnvironment env,
|
||||
ILogger<ExceptionHandlerMiddleware> 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<string, string>
|
||||
{
|
||||
["Exception"] = exception.Message,
|
||||
["StackTrace"] = exception.StackTrace,
|
||||
};
|
||||
if (exception.InnerException != null)
|
||||
{
|
||||
dic.Add("InnerException.Exception", exception.InnerException.Message);
|
||||
dic.Add("InnerException.StackTrace", exception.InnerException.StackTrace);
|
||||
}
|
||||
|
||||
if (exception.AdditionalData != null)
|
||||
dic.Add("AdditionalData", JsonConvert.SerializeObject(exception.AdditionalData));
|
||||
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<string, string>
|
||||
{
|
||||
["Exception"] = exception.Message,
|
||||
["InnerException"] = exception.InnerException?.Message,
|
||||
["StackTrace"] = exception.StackTrace,
|
||||
};
|
||||
message = JsonConvert.SerializeObject(dic);
|
||||
}
|
||||
|
||||
message = exception.Message;
|
||||
await WriteToResponseAsync();
|
||||
}
|
||||
|
||||
async Task WriteToResponseAsync()
|
||||
{
|
||||
if (context.Response.HasStarted)
|
||||
throw new InvalidOperationException("The response has already started, the http status code middleware will not be executed.");
|
||||
|
||||
var result = new ApiResult(false, apiStatusCode, message);
|
||||
|
||||
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<string, string>
|
||||
{
|
||||
["Exception"] = exception.Message,
|
||||
["StackTrace"] = exception.StackTrace
|
||||
};
|
||||
if (exception is SecurityTokenExpiredException tokenException)
|
||||
dic.Add("Expires", tokenException.Expires.ToString());
|
||||
|
||||
message = JsonConvert.SerializeObject(dic);
|
||||
}
|
||||
}
|
||||
|
||||
JwtSecurityToken ReadJwtToken(bool fromHeader = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (fromHeader)
|
||||
{
|
||||
var stream = context.Request.Headers.Values.First(v => v.FirstOrDefault().Contains("Bearer")).FirstOrDefault();
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var jsonToken = handler.ReadToken(stream.Split(" ").Last());
|
||||
return jsonToken as JwtSecurityToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
string stream = context.Request.Query["access_token"]; ;
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var jsonToken = handler.ReadToken(stream.Split(" ").Last());
|
||||
return jsonToken as JwtSecurityToken;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new BaseApiException(ApiResultStatusCode.UnAuthorized, e.Message + " Jwt is wrong", HttpStatusCode.Unauthorized);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<config>
|
||||
<add key='maxHttpRequestsPerSource' value='10' />
|
||||
</config>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="VnfRepos" value="https://packages.vnfco.ir/repository/nuget-group/index.json" />
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
|
||||
</packageSources>
|
||||
|
||||
<packageSourceCredentials>
|
||||
<VnfRepos>
|
||||
<add key="Username" value="runner" />
|
||||
<add key="ClearTextPassword" value="22102210aA" />
|
||||
</VnfRepos>
|
||||
</packageSourceCredentials>
|
||||
</configuration>
|
||||
|
||||
|
||||
<!-- <configuration>
|
||||
<config>
|
||||
<add key='maxHttpRequestsPerSource' value='10' />
|
||||
</config>
|
||||
<packageSources>
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
|
||||
</packageSources>
|
||||
</configuration> -->
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" Sdk="Microsoft.Docker.Sdk">
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectVersion>2.1</ProjectVersion>
|
||||
<DockerTargetOS>Linux</DockerTargetOS>
|
||||
<ProjectGuid>9feffa46-5403-42cd-87a4-9b2ac30bd7b6</ProjectGuid>
|
||||
<DockerLaunchAction>LaunchBrowser</DockerLaunchAction>
|
||||
<DockerServiceUrl>{Scheme}://localhost:{ServicePort}/swagger</DockerServiceUrl>
|
||||
<DockerServiceName>berizco.api</DockerServiceName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Include="docker-compose.override.yml">
|
||||
<DependentUpon>docker-compose.yml</DependentUpon>
|
||||
</None>
|
||||
<None Include="docker-compose.yml" />
|
||||
<None Include=".dockerignore" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"profiles": {
|
||||
"Docker Compose": {
|
||||
"commandName": "DockerCompose",
|
||||
"commandVersion": "1.0",
|
||||
"serviceActions": {
|
||||
"berizco.api": "StartDebugging",
|
||||
"brizco.identity.api": "StartDebugging"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue