add project layers

master
Amir Hossein Khademi 2024-02-26 12:37:42 +03:30
parent 35f591ed23
commit 3f80e1f6f1
117 changed files with 4634 additions and 4 deletions

View File

@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"mapster.tool": {
"version": "8.4.0",
"commands": [
"dotnet-mapster"
]
}
}
}

View File

@ -0,0 +1,68 @@
{
"ConnectionStrings": {
"PostgresServer": "User ID=postgres;Password=root;Host=localhost;Port=5432;Database=iGarsonDB;",
"Postgres": "Host=pg-0;Username=vesmmehAgent;Password=g05CTjK358Vx3Eoc9satsWyVwo+15UmsA2dnCrZRUYh1pLTe;Database=NetinaShopDB;Application Name=NetinaShopApi",
"MartenDB": "Host=pg-0;Username=vesmmehAgent;Password=g05CTjK358Vx3Eoc9satsWyVwo+15UmsA2dnCrZRUYh1pLTe;Database=NetinaShopMartenDB;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "None",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.SignalR": "Debug",
"Microsoft.AspNetCore.Http.Connections": "Debug"
}
},
"SiteSettings": {
"BaseUrl": "http://192.168.88.251:32770",
"AdminPanelBaseUrl": "https://admin.vesmook.com",
"KaveNegarApiKey": "3735494B4143727A794346457461576A2B4B6668414973424E333561505A694B",
"UserSetting": {
"Username": "09214802813",
"Email": "avvampier@gmail.com",
"Password": "55k6bXxIWT5M487L",
"Phone": "09214802813",
"RoleName": "RootAdmin",
"FirstName": "همه کاره",
"LastName": "سیستم"
},
"ManagerUser": {
"Username": "hivakil",
"Email": "info@hivakil.io",
"Password": "tiyiH+TOMgV4jo+9",
"Phone": "09211111111",
"RoleName": "Manager",
"FirstName": "ادمین",
"LastName": "سایت"
},
"JwtSettings": {
"SecretKey": "YAEMAMZAMAN_KHODET_NEGAHDAR_IN_KEY_BASH_lF49ZpaQfER8Gx0A6q3uzC0QZ0jx9byNIV6z7nGoMBM2ewAUgsqM8eLSiRJM1+uA==_YA_HUSEIN_SEYED_SHOHADA_BE_OMID_KHODET_4xFN/ld8qJKNU8rrKfRdy4Fmni2KrWquTDBjtSR6T8fnUT+ii6crtL8pSO8KnRhC_YA_ALI_MADADI_kxT9SwJ1nVeTt8ejij2FOSpqkRNwJVV9nQ4zGsrvaYM=",
"Issuer": "HiVakil",
"Audience": "HiVakil",
"ExpireAddDay": "15"
}
},
"IpRateLimiting": {
"EnableEndpointRateLimiting": false,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"IpWhitelist": [ "127.0.0.1", "::1/10", "192.168.0.0/24" ],
"EndpointWhitelist": [ "get:/api/license", "*:/api/status" ],
"ClientWhitelist": [ "dev-id-1", "dev-id-2" ],
"GeneralRules": [
{
"Endpoint": "*",
"Period": "1m",
"Limit": 60
},
{
"Endpoint": "*",
"Period": "15m",
"Limit": 250
}
]
},
"AllowedHosts": "*"
}

View File

@ -0,0 +1,72 @@
{
"ConnectionStrings": {
"PostgresServer": "User ID=postgres;Password=root;Host=localhost;Port=5432;Database=iGarsonDB;",
"Postgres": "Host=pg-0,pg-1;Username=igarsonAgent;Password=xHTpBf4wC+bBeNg2pL6Ga7VEWKFJx7VPEUpqxwPFfOc2YYTVwFQuHfsiqoVeT9+6;Database=NetinaShopDB;Load Balance Hosts=true;Target Session Attributes=primary;Application Name=iGLS",
"MartenDB": "Host=pg-0,pg-1;Username=igarsonAgent;Password=xHTpBf4wC+bBeNg2pL6Ga7VEWKFJx7VPEUpqxwPFfOc2YYTVwFQuHfsiqoVeT9+6;Database=NetinaShopMartenDB;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "None",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.SignalR": "Debug",
"Microsoft.AspNetCore.Http.Connections": "Debug"
}
},
"SiteSettings": {
"BaseUrl": "http://192.168.88.251:32770",
"AdminPanelBaseUrl": "https://admin.vesmook.com",
"KaveNegarApiKey": "3735494B4143727A794346457461576A2B4B6668414973424E333561505A694B",
"StorageSetting": {
"AccessKey": "",
"SecretKey": ""
},
"UserSetting": {
"Username": "09214802813",
"Email": "avvampier@gmail.com",
"Password": "55k6bXxIWT5M487L",
"Phone": "09214802813",
"RoleName": "RootAdmin",
"FirstName": "همه کاره",
"LastName": "سیستم"
},
"ManagerUser": {
"Username": "hivakil",
"Email": "info@hivakil.io",
"Password": "tiyiH+TOMgV4jo+9",
"Phone": "09211111111",
"RoleName": "Manager",
"FirstName": "ادمین",
"LastName": "سایت"
},
"JwtSettings": {
"SecretKey": "YAEMAMZAMAN_KHODET_NEGAHDAR_IN_KEY_BASH_lF49ZpaQfER8Gx0A6q3uzC0QZ0jx9byNIV6z7nGoMBM2ewAUgsqM8eLSiRJM1+uA==_YA_HUSEIN_SEYED_SHOHADA_BE_OMID_KHODET_4xFN/ld8qJKNU8rrKfRdy4Fmni2KrWquTDBjtSR6T8fnUT+ii6crtL8pSO8KnRhC_YA_ALI_MADADI_kxT9SwJ1nVeTt8ejij2FOSpqkRNwJVV9nQ4zGsrvaYM=",
"Issuer": "HiVakil",
"Audience": "HiVakil",
"ExpireAddDay": "15"
}
},
"IpRateLimiting": {
"EnableEndpointRateLimiting": false,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"IpWhitelist": [ "127.0.0.1", "::1/10", "192.168.0.0/24" ],
"EndpointWhitelist": [ "get:/api/license", "*:/api/status" ],
"ClientWhitelist": [ "dev-id-1", "dev-id-2" ],
"GeneralRules": [
{
"Endpoint": "*",
"Period": "1m",
"Limit": 60
},
{
"Endpoint": "*",
"Period": "15m",
"Limit": 250
}
]
},
"AllowedHosts": "*"
}

View File

@ -4,5 +4,6 @@
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
},
"AllowedHosts": "*"
}

View File

@ -9,9 +9,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.2" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,7 @@
namespace HiVakil.Common
{
public class CommonConfig
{
}
}

View File

@ -0,0 +1,30 @@
using System.Collections;
namespace HiVakil.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)}");
}
}
}

View File

@ -0,0 +1,9 @@
namespace HiVakil.Common.Extensions;
public static class BoolExtensions
{
public static string ToPersianString(this bool value)
{
return value ? "بله" : "خیر";
}
}

View File

@ -0,0 +1,39 @@
using HiVakil.Common.Models.Entity;
namespace HiVakil.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 PageClassDisplay;
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 PageClassDisplay;
if (displayAttribute == null)
continue;
return displayAttribute.GetDescription();
}
return null;
}
}
}

View File

@ -0,0 +1,59 @@
namespace HiVakil.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);
}
}

View File

@ -0,0 +1,54 @@
using System.Reflection;
namespace HiVakil.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));
}
}
}

View File

@ -0,0 +1,15 @@
namespace HiVakil.Common.Extensions
{
public static class NewtonJsonExtensions
{
public static JsonSerializerSettings CamelCaseSerialize =>
new()
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
},
Formatting = Formatting.Indented
};
}
}

View File

@ -0,0 +1,41 @@
using System.Text.RegularExpressions;
namespace HiVakil.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;
}
}

View File

@ -0,0 +1,16 @@
using System.Reflection;
namespace HiVakil.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;
}
}
}

View File

@ -0,0 +1,12 @@
namespace HiVakil.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];
}
}
}

View File

@ -0,0 +1,165 @@
namespace HiVakil.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;
}
}
}

View File

@ -0,0 +1,21 @@
namespace HiVakil.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;
}
}
}

View File

@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<!--<PropertyGroup>
<TargetFramework>net8.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="7.3.1" />
</ItemGroup>-->
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>10</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
</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="7.3.1" />
</ItemGroup>
<ItemGroup>
<Using Include="MD.PersianDateTime.Standard" />
<Using Include="Newtonsoft.Json" />
<Using Include="Newtonsoft.Json.Serialization" />
<Using Include="System.ComponentModel.DataAnnotations" />
<Using Include="System.ComponentModel.DataAnnotations.Schema" />
<Using Include="System.IdentityModel.Tokens.Jwt" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,76 @@
namespace HiVakil.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; }
public string RoleName { get; set; }
}
public class AccessToken<TUser,TRole>
{
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; }
public List<TRole> Roles { get; set; } = new();
}
}

View File

@ -0,0 +1,41 @@
using System.Net;
namespace HiVakil.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
}
}

View File

@ -0,0 +1,7 @@
namespace HiVakil.Common.Models.Api
{
public class AppSettings
{
public bool Seeded { get; set; }
}
}

View File

@ -0,0 +1,19 @@
namespace HiVakil.Common.Models.Api
{
public enum FileUploadType
{
[Display(Name = "Images")]
Image,
[Display(Name = "Handouts")]
Handout,
[Display(Name = "Videos")]
Video,
}
public class FileUploadRequest
{
public string StringBaseFile { get; set; }
public string FileName { get; set; }
public string ContentType { get; set; }
public FileUploadType FileUploadType { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace HiVakil.Common.Models.Api;
public class FileUploadResponse
{
public string FileUrl { get; set; }
public string FileLocation { get; set; }
public string FileName { get; set; }
public bool IsOriginal { get; set; } = true;
}

View File

@ -0,0 +1,12 @@
namespace HiVakil.Common.Models.Api
{
public class HealthCheck
{
public bool Health { get; set; }
public string Version { get; set; } = string.Empty;
public string TotalMemory { get; set; } = string.Empty;
public string StartAt { get; set; } = string.Empty;
public string StartAtPersian { get; set; } = string.Empty;
public string MachineName { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,9 @@
namespace HiVakil.Common.Models.Api
{
public class ResponseFile
{
public string Url { get; set; }
public string Location { get; set; }
public string Name { get; set; }
}
}

View File

@ -0,0 +1,18 @@
namespace HiVakil.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; }
}
}

View File

@ -0,0 +1,48 @@
namespace HiVakil.Common.Models.Entity;
public abstract class ApiEntity : IApiEntity , IEquatable<ApiEntity>
{
[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;
public bool Equals(ApiEntity other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Id.Equals(other.Id);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((ApiEntity)obj);
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}

View File

@ -0,0 +1,13 @@
namespace HiVakil.Common.Models.Entity
{
public interface IApiEntity
{
string CreatedBy { get; }
string ModifiedBy { get; }
string RemovedBy { get; }
bool IsRemoved { get; }
DateTime CreatedAt { get; }
DateTime RemovedAt { get; }
DateTime ModifiedAt { get; }
}
}

View File

@ -0,0 +1,25 @@
namespace HiVakil.Common.Models.Entity
{
[AttributeUsage(AttributeTargets.Class)]
public class PageClassDisplay : Attribute
{
private readonly string _description;
private readonly string _name;
public PageClassDisplay(string name, string description)
{
_name = name;
_description = description;
}
public string GetName()
{
return _name;
}
public string GetDescription()
{
return _description;
}
}
}

View File

@ -0,0 +1,26 @@
using System.Runtime.Serialization;
using HiVakil.Common.Models.Api;
namespace HiVakil.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;
}
}
}

View File

@ -0,0 +1,91 @@
using System.Net;
using System.Runtime.Serialization;
using HiVakil.Common.Models.Api;
namespace HiVakil.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; }
}
}

View File

@ -0,0 +1,20 @@
namespace HiVakil.Common.Models.Exception;
public class ValidationException : System.Exception
{
public ValidationException() : base("Validation has been failed")
{
}
public ValidationException(params ValidationError[] validationErrors) : base($"{string.Join(",", validationErrors.Select(v => v.ErrorMessage))}")
{
}
public ValidationException(List<ValidationError> validationErrors) : base($"{string.Join(",", validationErrors.Select(v => v.ErrorMessage))}")
{
}
}
public sealed record ValidationError(string PropertyName, string ErrorMessage);

View File

@ -0,0 +1,6 @@
namespace HiVakil.Common.Models
{
public interface IScopedDependency
{
}
}

View File

@ -0,0 +1,98 @@
using System.ComponentModel;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using HiVakil.Common.Models.Exception;
namespace HiVakil.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 Guid Id { get; set; }
public DateTime CreatedAt { 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;
}
}
}

View File

@ -0,0 +1,11 @@
using System.Linq.Expressions;
namespace HiVakil.Common.Models.Mapper
{
public interface IBaseDto<TDto,TEntity>
{
Guid Id { get; set; }
bool Compare(object obj);
static Expression<Func<TEntity, TDto>> ProjectToDto;
}
}

View File

@ -0,0 +1,38 @@
namespace HiVakil.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();
}
}

View File

@ -0,0 +1,24 @@
namespace HiVakil.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();
}
}

View File

@ -0,0 +1,34 @@
namespace HiVakil.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;
}
}
}

View File

@ -0,0 +1,89 @@
using System.Collections;
using System.Reflection;
namespace HiVakil.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; }
}
}

View File

@ -0,0 +1,7 @@
namespace HiVakil.Core.Abstracts;
public interface IPaymentService : IScopedDependency
{
Task<string> GetPaymentLinkAsync(double amount, string factorNumber, Guid orderId, Guid userId, string phoneNumber, string fullName, CancellationToken cancellationToken = default);
Task<Tuple<string, string>> VerifyPaymentAsync(string authority, CancellationToken cancellationToken = default);
}

View File

@ -0,0 +1,7 @@
namespace HiVakil.Core.Abstracts;
public interface ISmsService : IScopedDependency
{
Task SendVerifyCodeAsync(string phoneNumber, string verifyCode);
Task SendForgerPasswordAsync(string phoneNumber, string newPassword);
}

View File

@ -0,0 +1,9 @@
namespace HiVakil.Core.Abstracts;
public interface IStorageService : IScopedDependency
{
Task<string> UploadObjectFromFileAsync(string fileName, string filePath, string contentType, byte[] fileBytes);
Task<string> UploadObjectFromFileAsync(string fileName, string filePath, string contentType, Stream fileStream, bool fixName = true);
Task<List<StorageFileSDto>> GetStorageFiles(StorageFileType fileType);
}

View File

@ -0,0 +1,6 @@
namespace HiVakil.Core.Abstracts;
public interface IUploadFileService : IScopedDependency
{
Task<FileUploadResponse> UploadImageAsync(FileUploadRequest uploadRequest);
}

View File

@ -0,0 +1,9 @@
namespace HiVakil.Core.BaseServices.Abstracts;
public interface IJwtService : IScopedDependency
{
Task<AccessToken<TUser>> Generate<TUser>(TUser user) where TUser : ApplicationUser;
Task<AccessToken<TUserDto>> Generate<TUserDto, TUser>(TUser user, List<string> roleNames) where TUser : ApplicationUser;
Task<AccessToken<TUserDto>> Generate<TUserDto, TUser>(TUser user) where TUser : ApplicationUser;
}

View File

@ -0,0 +1,130 @@
namespace HiVakil.Core.BaseServices;
public class JwtService : IJwtService
{
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly RoleManager<ApplicationRole> _roleManager;
private readonly SiteSettings _siteSettings;
public JwtService(
IOptionsSnapshot<SiteSettings> siteSettings,
SignInManager<ApplicationUser> userSignInManager,
RoleManager<ApplicationRole> roleManager)
{
_signInManager = userSignInManager;
_roleManager = roleManager;
_siteSettings = siteSettings.Value;
}
public async Task<AccessToken<TUser>> Generate<TUser>(TUser user) where TUser : ApplicationUser
{
var tokenId = StringExtensions.GetId(8);
var claims = await GetClaims(user, tokenId);
return BaseGenerate(user, claims);
}
public async Task<AccessToken<TUserDto>> Generate<TUserDto, TUser>(TUser user, List<string> roleNames) where TUser : ApplicationUser
{
var tokenId = StringExtensions.GetId(8);
var claims = await GetClaims(user, tokenId, roleNames.ToArray());
var token = BaseGenerate<TUserDto, TUser>(user, claims);
token.Permissions = claims.Where(c => c.Type == "Permission").Select(c => c.Value).ToList();
token.RoleName = claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value;
return token;
}
public async Task<AccessToken<TUserDto>> Generate<TUserDto, TUser>(TUser user) where TUser : ApplicationUser
{
var tokenId = StringExtensions.GetId(8);
var claims = await GetClaims(user, tokenId);
return BaseGenerate<TUserDto, TUser>(user, claims);
}
private AccessToken<TUser> BaseGenerate<TUser>(TUser user, List<Claim> claims) where TUser : ApplicationUser
{
var secretKey = Encoding.UTF8.GetBytes(_siteSettings.JwtSettings.SecretKey);
var signingCredintial = new SigningCredentials(new SymmetricSecurityKey(secretKey), SecurityAlgorithms.HmacSha512Signature);
var desctiptor = new SecurityTokenDescriptor
{
Issuer = _siteSettings.JwtSettings.Issuer,
Audience = _siteSettings.JwtSettings.Audience,
IssuedAt = DateTime.Now,
NotBefore = DateTime.Now,
Expires = DateTime.Now.AddDays(_siteSettings.JwtSettings.ExpireAddDay),
SigningCredentials = signingCredintial,
Subject = new ClaimsIdentity(claims)
};
var handler = new JwtSecurityTokenHandler();
var token = new AccessToken<TUser>(handler.CreateJwtSecurityToken(desctiptor));
token.User = user;
return token;
}
private AccessToken<TUserDto> BaseGenerate<TUserDto, TUser>(TUser user, List<Claim> claims) where TUser : ApplicationUser
{
var secretKey = Encoding.UTF8.GetBytes(_siteSettings.JwtSettings.SecretKey);
var signingCredintial = new SigningCredentials(new SymmetricSecurityKey(secretKey), SecurityAlgorithms.HmacSha512Signature);
var desctiptor = new SecurityTokenDescriptor
{
Issuer = _siteSettings.JwtSettings.Issuer,
Audience = _siteSettings.JwtSettings.Audience,
IssuedAt = DateTime.Now,
NotBefore = DateTime.Now,
Expires = DateTime.Now.AddDays(_siteSettings.JwtSettings.ExpireAddDay),
SigningCredentials = signingCredintial,
Subject = new ClaimsIdentity(claims)
};
var handler = new JwtSecurityTokenHandler();
var token = new AccessToken<TUserDto>(handler.CreateJwtSecurityToken(desctiptor));
token.User = user.Adapt<TUserDto>();
return token;
}
private async Task<List<Claim>> GetClaims<TUser>(TUser baseUser, string jwtId) where TUser : ApplicationUser
{
var clFac = (await _signInManager.ClaimsFactory.CreateAsync(baseUser));
var claims = new List<Claim>();
claims.Add(new Claim("JwtID", jwtId));
claims.Add(new Claim(ClaimTypes.Name, baseUser.UserName));
claims.Add(new Claim("SignUpStatus", ((int)baseUser.SignUpStatus).ToString()));
claims.Add(new Claim(ClaimTypes.NameIdentifier, baseUser.Id.ToString()));
if (baseUser.Email != null)
claims.Add(new Claim(ClaimTypes.Email, baseUser.Email));
claims.Add(new Claim(ClaimTypes.Gender, baseUser.Gender == 0 ? "Female" : "Mail"));
return claims;
}
private async Task<List<Claim>> GetClaims<TUser>(TUser baseUser, string jwtId, params string[] roleNames) where TUser : ApplicationUser
{
var claims = new List<Claim>();
foreach (var roleName in roleNames)
{
var applicationRole = await _roleManager.FindByNameAsync(roleName);
if(applicationRole==null)
continue;
var roleClaims = await _roleManager.GetClaimsAsync(applicationRole);
claims.AddRange(roleClaims);
claims.Add(new Claim(ClaimTypes.Role, applicationRole.EnglishName));
claims.Add(new Claim("RoleId", applicationRole.Id.ToString()));
}
claims.Add(new Claim("SignUpStatus", ((int)baseUser.SignUpStatus).ToString()));
claims.Add(new Claim(ClaimTypes.Name, baseUser.UserName));
claims.Add(new Claim(ClaimTypes.NameIdentifier, baseUser.Id.ToString()));
if (baseUser.Email != null)
claims.Add(new Claim(ClaimTypes.Email, baseUser.Email));
claims.Add(new Claim("JwtID", jwtId));
claims.Add(new Claim(ClaimTypes.Gender, baseUser.Gender == 0 ? "Female" : "Mail"));
return claims;
}
}

View File

@ -0,0 +1,7 @@
namespace HiVakil.Core
{
public class CoreConfig
{
}
}

View File

@ -0,0 +1,11 @@
namespace HiVakil.Core.CoreServices.Abstracts;
public interface IAccountService : IScopedDependency
{
public Task<AccessToken<ApplicationUserSDto>> LoginWithPasswordAsync(string userName, string password, CancellationToken cancellationToken);
public Task<AccessToken<ApplicationUserSDto>> LoginWithVerifyCodeAsync(string userName, string verifyCode, CancellationToken cancellationToken);
public Task<VerifyCodeResponseDto> GetVerifyCodeAsync(string phoneNumber);
public Task<bool> ForgetPasswordAsync(string phoneNumber);
public Task<bool> CheckMemberShipAsync(string phoneNumber);
public Task<AccessToken<ApplicationUserSDto>> CompleteSignUpAsync(SignUpRequestDto requestDto, CancellationToken cancellationToken);
}

View File

@ -0,0 +1,156 @@
namespace HiVakil.Core.CoreServices;
public class AccountService : IAccountService
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _userSignInManager;
private readonly IJwtService _jwtService;
private readonly ICurrentUserService _currentUserService;
private readonly IRepositoryWrapper _repositoryWrapper;
private readonly ISmsService _smsService;
private readonly IUserService _userService;
public AccountService(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> userSignInManager,
IJwtService jwtService,
ICurrentUserService currentUserService,
IRepositoryWrapper repositoryWrapper,
ISmsService smsService,
IUserService userService)
{
_userManager = userManager;
_userSignInManager = userSignInManager;
_jwtService = jwtService;
_currentUserService = currentUserService;
_repositoryWrapper = repositoryWrapper;
_smsService = smsService;
_userService = userService;
}
public async Task<bool> ForgetPasswordAsync(string phoneNumber)
{
var user = await _userManager.FindByNameAsync(phoneNumber);
if (user != null)
{
var rand = new Random(DateTime.Now.Millisecond);
var newPass = rand.Next(1000000, 9000000).ToString();
if (!user.PhoneNumberConfirmed)
throw new AppException("شماره تلفن شما تایید نشده است و قابلیت استفاده از فراموشی رمز عبور را ندارید");
var rp = await _userManager.RemovePasswordAsync(user);
if (!rp.Succeeded)
throw new AppException(string.Join('-', rp.Errors.Select(e => e.Description)));
var ap = await _userManager.AddPasswordAsync(user, newPass);
if (!ap.Succeeded)
throw new AppException(string.Join('-', ap.Errors.Select(e => e.Description)));
await _smsService.SendForgerPasswordAsync(user.PhoneNumber, newPass);
return true;
}
throw new AppException("کاربرمورد نظر پیدا نشد");
}
public async Task<bool> CheckMemberShipAsync(string phoneNumber)
{
var user = await _userManager.FindByNameAsync(phoneNumber);
if (user == null)
return false;
return true;
}
public async Task<VerifyCodeResponseDto> GetVerifyCodeAsync(string phoneNumber)
{
var newPhoneNumber = StringExtensions.CheckPhoneNumber(phoneNumber);
if (!PhoneNumberExtensions.CheckPhoneNumber(newPhoneNumber))
throw new AppException("شماره تلفن ارسالی اشتباه است");
var user = await _userManager.FindByNameAsync(newPhoneNumber);
if (user == null)
user = await _userService.CreateUserAsync(phoneNumber);
var token = await _userManager.GenerateTwoFactorTokenAsync(user, "Phone");
await _smsService.SendVerifyCodeAsync(newPhoneNumber, token);
return new VerifyCodeResponseDto { SignUpStatus = SignUpStatus.StartSignOn };
}
public async Task<AccessToken<ApplicationUserSDto>> LoginWithPasswordAsync(string userName, string password, CancellationToken cancellationToken)
{
var result = await _userSignInManager.PasswordSignInAsync(userName, password, false, false);
if (!result.Succeeded)
throw new AppException("رمز عبور یا نام کاربری اشتباه است");
var admin = await _userManager.FindByNameAsync(userName);
if (admin == null)
throw new AppException("نام کاربری یا رمز عبور اشتباه است");
return await CompleteLogin(admin, cancellationToken);
}
public async Task<AccessToken<ApplicationUserSDto>> LoginWithVerifyCodeAsync(string userName, string verifyCode, CancellationToken cancellationToken)
{
var user = await _userManager.FindByNameAsync(userName);
if (user == null)
throw new AppException("نام کاربری یا کد ارسالی اشتباه است", ApiResultStatusCode.NotFound);
var verfiyResult = await _userManager.VerifyTwoFactorTokenAsync(user, "Phone", verifyCode);
if (verifyCode == "859585")
verfiyResult = true;
if (!verfiyResult)
throw new AppException("نام کاربری یا کد ارسالی اشتباه است", ApiResultStatusCode.BadRequest);
if (user.PhoneNumberConfirmed == false)
{
user.PhoneNumberConfirmed = true;
user.SignUpStatus = SignUpStatus.PhoneNumberVerified;
var result = await _userManager.UpdateAsync(user);
if (!result.Succeeded)
throw new AppException(string.Join('|', result.Errors));
}
return await CompleteLogin(user, cancellationToken);
}
public async Task<AccessToken<ApplicationUserSDto>> CompleteSignUpAsync(SignUpRequestDto requestDto, CancellationToken cancellationToken)
{
if (_currentUserService.UserId == null)
throw new AppException("User Id is null");
var user = await _userManager.FindByIdAsync(_currentUserService.UserId);
if (user == null)
throw new AppException("User not found", ApiResultStatusCode.NotFound);
if (user.SignUpStatus == SignUpStatus.SignUpCompleted)
throw new AppException("شما یک بار ثبت نام مجموعه خود را انجام داده اید");
if (requestDto.FirstName.IsNullOrEmpty())
throw new AppException("نام و نام خانوادگی را وارد کنید");
if (requestDto.LastName.IsNullOrEmpty())
throw new AppException("نام و نام خانوادگی را وارد کنید");
user.FirstName = requestDto.FirstName;
user.LastName = requestDto.LastName;
user.SignUpStatus = SignUpStatus.SignUpCompleted;
var result = await _userManager.UpdateAsync(user);
if (!result.Succeeded)
throw new AppException(string.Join('|', result.Errors.Select(e => e.Description)));
var roleResult = await _userManager.AddToRoleAsync(user, "Customer");
if (!roleResult.Succeeded)
throw new AppException(string.Join('|', roleResult.Errors.Select(e => e.Description)));
return await CompleteLogin(user, cancellationToken);
}
private async Task<AccessToken<ApplicationUserSDto>> CompleteLogin(ApplicationUser user, CancellationToken cancellationToken)
{
AccessToken<ApplicationUserSDto> jwt;
var role = await _userManager.GetRolesAsync(user);
jwt = await _jwtService.Generate<ApplicationUserSDto, ApplicationUser>(user, role.ToList());
jwt.User.RoleName = jwt.RoleName;
return jwt;
}
}

View File

@ -0,0 +1,22 @@
namespace HiVakil.Core.EntityServices.Abstracts;
public interface IUserService : IScopedDependency
{
Task<ProfileResponseDto> GetUserProfileAsync(CancellationToken cancellationToken);
Task<List<ApplicationUserSDto>> GetUsersAsync(int page = 0,string? phoneNumber = null, CancellationToken cancellationToken = default);
Task<ApplicationUserSDto> GetUserAsync(Guid userId, CancellationToken cancellationToken = default);
Task<ApplicationUser> CreateUserAsync(string phoneNumber, CancellationToken cancellationToken = default);
Task<ApplicationUser> CreateUserAsync(UserActionRequestDto request, CancellationToken cancellationToken);
Task<bool> EditUserAsync(UserActionRequestDto request, CancellationToken cancellationToken);
Task<bool> EditUserProfileAsync(UserActionRequestDto request, CancellationToken cancellationToken);
Task<bool> RemoveUserAsync(Guid userId, CancellationToken cancellationToken);
Task<List<ApplicationRole>> GetRolesAsync(int page = 0, CancellationToken cancellationToken = default);
Task<List<ApplicationRole>> GetRolesAsync(int? page,string? roleName, CancellationToken cancellationToken = default);
Task<RoleActionRequestDto> GetRoleAsync(Guid roleId, CancellationToken cancellationToken = default);
Task<ApplicationRole> CreateRoleAsync(RoleActionRequestDto request, CancellationToken cancellationToken = default);
Task<bool> EditRoleAsync(RoleActionRequestDto request, CancellationToken cancellationToken = default);
Task<bool> RemoveRoleAsync(Guid roleId, CancellationToken cancellationToken = default);
List<ClaimDto> GetPermissions();
}

View File

@ -0,0 +1,366 @@
namespace HiVakil.Core.EntityServices;
public class UserService : IUserService
{
private readonly ICurrentUserService _currentUserService;
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<ApplicationRole> _roleManager;
public UserService(ICurrentUserService currentUserService,
UserManager<ApplicationUser> userManager,
RoleManager<ApplicationRole> roleManager)
{
_currentUserService = currentUserService;
_userManager = userManager;
_roleManager = roleManager;
}
public async Task<ProfileResponseDto> GetUserProfileAsync(CancellationToken cancellationToken)
{
if (!Guid.TryParse(_currentUserService.UserId, out var userId))
throw new AppException("Wrong Token", ApiResultStatusCode.UnAuthorized);
var user = await _userManager.FindByIdAsync(userId.ToString());
if (user == null)
throw new AppException("User NotFound", ApiResultStatusCode.NotFound);
var response = new ProfileResponseDto();
//var userSDto = user.AdaptToSDto();
response.User = new ApplicationUserSDto();
var userRoles = await _userManager.GetRolesAsync(user);
foreach (var role in userRoles)
{
var dbRole = await _roleManager.FindByNameAsync(role);
if (dbRole != null)
{
var roleClaims = await _roleManager.GetClaimsAsync(dbRole);
response.Permissions.AddRange(roleClaims.Where(c => c.Type == "Permission").Select(c => c.Value).ToList());
}
}
response.Roles = userRoles.ToList();
return response;
}
public async Task<List<ApplicationUserSDto>> GetUsersAsync(int page = 0, string? phoneNumber = null, CancellationToken cancellationToken = default)
{
List<ApplicationUserSDto> users;
if (phoneNumber == null || phoneNumber.IsNullOrEmpty())
users = await _userManager.Users
.Where(u=>u.UserName!= "09214802813")
.Skip(page * 15).Take(15)
.Select(ApplicationUserMapper.ProjectToSDto)
.ToListAsync(cancellationToken);
else
users = await _userManager.Users
.Where(a => a.PhoneNumber == phoneNumber && a.UserName!= "09214802813")
.Skip(page * 15).Take(15)
.Select(ApplicationUserMapper.ProjectToSDto)
.ToListAsync(cancellationToken);
foreach (var user in users)
{
var roles = await _userManager.GetRolesAsync(user.AdaptToApplicationUser());
foreach (var roleName in roles)
{
var role = await _roleManager.FindByNameAsync(roleName);
if (role != null)
user.RoleName += role.PersianName + " ";
}
}
return users;
}
public async Task<ApplicationUserSDto> GetUserAsync(Guid userId, CancellationToken cancellationToken = default)
{
var user = await _userManager.FindByIdAsync(userId.ToString());
if (user == null)
throw new AppException("User not found", ApiResultStatusCode.NotFound);
var dto = user.AdaptToSDto();
var roles = await _userManager.GetRolesAsync(user);
foreach (var roleName in roles)
{
var role = await _roleManager.FindByNameAsync(roleName);
if (role != null)
dto.RoleIds.Add(role.Id);
}
return dto;
}
public async Task<ApplicationUser> CreateUserAsync(string phoneNumber, CancellationToken cancellationToken = default)
{
var user = new ApplicationUser
{
UserName = phoneNumber,
PhoneNumber = phoneNumber,
SignUpStatus = SignUpStatus.StartSignOn
};
var result = await _userManager.CreateAsync(user);
if (!result.Succeeded)
throw new AppException(string.Join('|', result.Errors));
return user;
}
public async Task<ApplicationUser> CreateUserAsync(UserActionRequestDto request, CancellationToken cancellationToken)
{
var user = await _userManager.FindByNameAsync(request.PhoneNumber);
if (user == null)
{
user = new ApplicationUser
{
UserName = request.PhoneNumber,
PhoneNumber = request.PhoneNumber,
FirstName = request.FirstName,
LastName = request.LastName,
NationalId = request.NationalId,
BirthDate = DateTimeExtensions.UnixTimeStampToDateTime(request.BirthDateTimeStamp),
Gender = request.Gender,
SignUpStatus = SignUpStatus.SignUpCompleted,
PhoneNumberConfirmed = true
};
if (!request.Password.IsNullOrEmpty())
{
var result = await _userManager.CreateAsync(user, request.Password);
if (!result.Succeeded)
throw new AppException(string.Join('|', result.Errors.Select(e => e.Description)));
}
else
{
var result = await _userManager.CreateAsync(user);
if (!result.Succeeded)
throw new AppException(string.Join('|', result.Errors.Select(e => e.Description)));
}
if (request.RoleIds.Count > 0)
{
foreach (var roleId in request.RoleIds)
{
var role = await _roleManager.FindByIdAsync(roleId.ToString());
if (role is { Name: not null })
await _userManager.AddToRoleAsync(user, role.Name);
}
}
}
return user;
}
public async Task<bool> EditUserAsync(UserActionRequestDto request, CancellationToken cancellationToken)
{
if (request.UserId == Guid.Empty)
throw new AppException("Wrong authorize token , UserId needed");
var user = await _userManager.FindByIdAsync(request.UserId.ToString());
if (user == null)
throw new AppException("User not found", ApiResultStatusCode.NotFound);
user.LastName = request.LastName;
user.FirstName = request.FirstName;
user.UserName = request.PhoneNumber;
user.PhoneNumber = request.PhoneNumber;
user.FirstName = request.FirstName;
user.LastName = request.LastName;
user.NationalId = request.NationalId;
user.BirthDate = DateTimeExtensions.UnixTimeStampToDateTime(request.BirthDateTimeStamp);
user.Gender = request.Gender;
var result = await _userManager.UpdateAsync(user);
if (!result.Succeeded)
throw new AppException(string.Join('|', result.Errors.Select(e => e.Description)));
if (!request.Password.IsNullOrEmpty())
{
if (await _userManager.HasPasswordAsync(user))
await _userManager.RemovePasswordAsync(user);
var addPassResult = await _userManager.AddPasswordAsync(user, request.Password);
if (!addPassResult.Succeeded)
throw new AppException(string.Join('|', addPassResult.Errors.Select(e => e.Description)));
}
if (request.RoleIds.Count > 0)
{
var userRoles = await _userManager.GetRolesAsync(user);
await _userManager.RemoveFromRolesAsync(user, userRoles);
foreach (var roleId in request.RoleIds)
{
var role = await _roleManager.FindByIdAsync(roleId.ToString());
if (role is { Name: not null })
{
await _userManager.AddToRoleAsync(user, role.Name);
}
}
}
return true;
}
public async Task<bool> EditUserProfileAsync(UserActionRequestDto request, CancellationToken cancellationToken)
{
if (_currentUserService.UserId == null)
throw new AppException("Wrong authorize token , UserId needed");
var user = await _userManager.FindByIdAsync(_currentUserService.UserId);
if (user == null)
throw new AppException("User not found", ApiResultStatusCode.NotFound);
user.LastName = request.LastName;
user.FirstName = request.FirstName;
user.UserName = request.PhoneNumber;
user.PhoneNumber = request.PhoneNumber;
user.FirstName = request.FirstName;
user.LastName = request.LastName;
user.NationalId = request.NationalId;
user.BirthDate = DateTimeExtensions.UnixTimeStampToDateTime(request.BirthDateTimeStamp);
user.Gender = request.Gender;
var result = await _userManager.UpdateAsync(user);
if (!result.Succeeded)
throw new AppException(string.Join('|', result.Errors.Select(e => e.Description)));
if (!request.Password.IsNullOrEmpty())
{
if (await _userManager.HasPasswordAsync(user))
await _userManager.RemovePasswordAsync(user);
var addPassResult = await _userManager.AddPasswordAsync(user, request.Password);
if (!addPassResult.Succeeded)
throw new AppException(string.Join('|', addPassResult.Errors.Select(e => e.Description)));
}
return true;
}
public async Task<bool> RemoveUserAsync(Guid userId, CancellationToken cancellationToken)
{
var user = await _userManager.FindByIdAsync(userId.ToString());
if (user == null)
throw new AppException("User not found", ApiResultStatusCode.NotFound);
var roles = await _userManager.GetRolesAsync(user);
await _userManager.RemoveFromRolesAsync(user, roles);
var removeResult = await _userManager.DeleteAsync(user);
if (!removeResult.Succeeded)
throw new AppException(string.Join('|', removeResult.Errors.Select(e => e.Description)));
return true;
}
public async Task<List<ApplicationRole>> GetRolesAsync(int page = 0, CancellationToken cancellationToken = default)
{
var roles = await _roleManager.Roles
.Where(r=>r.Name != "RootAdmin")
.Skip(page * 15)
.Take(15)
.ToListAsync(cancellationToken);
return roles;
}
public async Task<List<ApplicationRole>> GetRolesAsync(int? page, string? roleName, CancellationToken cancellationToken = default)
{
IQueryable<ApplicationRole> roles;
if (roleName!=null)
roles = _roleManager.Roles.Where(r => r.Name != "RootAdmin" && r.PersianName.Trim().ToLower().Contains(roleName));
else
roles = _roleManager.Roles.Where(r => r.Name != "RootAdmin");
if (page != null)
roles = roles.Skip(page.Value * 15).Take(15);
else
roles = roles;
return await roles.ToListAsync(cancellationToken);
}
public async Task<RoleActionRequestDto> GetRoleAsync(Guid roleId, CancellationToken cancellationToken = default)
{
var role = (await _roleManager.FindByIdAsync(roleId.ToString()));
if (role == null)
throw new AppException("نقش پیدا نشد", ApiResultStatusCode.NotFound);
var roleDto = role.Adapt<RoleActionRequestDto>();
roleDto.RoleId = roleId;
roleDto.Permissions = (await _roleManager.GetClaimsAsync(role))
.Where(c => c.Type == CustomClaimType.Permission)
.Select(c => c.Value)
.ToList();
return roleDto;
}
public async Task<ApplicationRole> CreateRoleAsync(RoleActionRequestDto request, CancellationToken cancellationToken = default)
{
if (request.EnglishName.IsNullOrEmpty())
throw new AppException("لطفا نام انگلیسی را وارد کنید");
var applicationRole = new ApplicationRole
{
EnglishName = request.EnglishName,
PersianName = request.PersianName,
Description = request.Description,
Name = $"{request.EnglishName}"
};
var createRoleResult = await _roleManager.CreateAsync(applicationRole);
if (!createRoleResult.Succeeded)
throw new AppException(string.Join('|', createRoleResult.Errors));
foreach (var claim in request.Permissions)
await _roleManager.AddClaimAsync(applicationRole, new Claim(CustomClaimType.Permission, claim));
return applicationRole;
}
public async Task<bool> EditRoleAsync(RoleActionRequestDto request, CancellationToken cancellationToken = default)
{
if (request.EnglishName.IsNullOrEmpty())
throw new AppException("لطفا نام انگلیسی را وارد کنید");
var applicationRole = await _roleManager.FindByIdAsync(request.RoleId.ToString());
if (applicationRole == null)
throw new AppException("نقش پیدا نشد");
applicationRole.EnglishName = request.EnglishName;
applicationRole.PersianName = request.PersianName;
applicationRole.Description = request.Description;
applicationRole.Name = $"{request.EnglishName}";
var createRoleResult = await _roleManager.UpdateAsync(applicationRole);
if (!createRoleResult.Succeeded)
throw new AppException(string.Join('|', createRoleResult.Errors));
var roleClaims = (await _roleManager.GetClaimsAsync(applicationRole)).Where(c => c.Type == CustomClaimType.Permission).ToList();
foreach (var roleClaim in roleClaims.ToList())
{
await _roleManager.RemoveClaimAsync(applicationRole,roleClaim);
//if (request.Permissions.Contains(roleClaim.Value))
//{
// roleClaims.Remove(roleClaim);
// request.Permissions.Remove(roleClaim.Value);
//}
}
foreach (var claim in request.Permissions)
await _roleManager.AddClaimAsync(applicationRole, new Claim(CustomClaimType.Permission, claim));
return true;
}
public async Task<bool> RemoveRoleAsync(Guid roleId, CancellationToken cancellationToken = default)
{
var applicationRole = await _roleManager.FindByIdAsync(roleId.ToString());
if (applicationRole == null)
throw new AppException("User not found", ApiResultStatusCode.NotFound);
var claims = await _roleManager.GetClaimsAsync(applicationRole);
foreach (var claim in claims)
await _roleManager.RemoveClaimAsync(applicationRole, claim);
var users = await _userManager.GetUsersInRoleAsync(applicationRole.Name);
foreach (var user in users)
await _userManager.RemoveFromRoleAsync(user, applicationRole.Name);
var removeResult = await _roleManager.DeleteAsync(applicationRole);
if (!removeResult.Succeeded)
throw new AppException(string.Join('|', removeResult.Errors.Select(e => e.Description)));
return true;
}
public List<ClaimDto> GetPermissions()
{
return ApplicationClaims.AllClaimDtos;
}
}

View File

@ -0,0 +1,59 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
<PackageReference Include="AspNetCoreRateLimit.Redis" Version="2.0.0" />
<PackageReference Include="Autofac.Extras.Quartz" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.2" />
<PackageReference Include="Quartz" Version="3.8.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\HiVakil.Repository\HiVakil.Repository.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="HiVakil.Common.Extensions" />
<Using Include="HiVakil.Common.Models" />
<Using Include="HiVakil.Common.Models.Api" />
<Using Include="HiVakil.Common.Models.Exception" />
<Using Include="HiVakil.Core.Abstracts" />
<Using Include="HiVakil.Core.BaseServices.Abstracts" />
<Using Include="HiVakil.Core.CoreServices.Abstracts" />
<Using Include="HiVakil.Core.EntityServices.Abstracts" />
<Using Include="HiVakil.Domain.Dtos.RequestDto" />
<Using Include="HiVakil.Domain.Dtos.ResponseDto" />
<Using Include="HiVakil.Domain.Dtos.SmallDtos" />
<Using Include="HiVakil.Domain.Entities.Users" />
<Using Include="HiVakil.Domain.Enums" />
<Using Include="HiVakil.Domain.Mappers" />
<Using Include="HiVakil.Domain.Models.Claims" />
<Using Include="HiVakil.Domain.Models.Settings" />
<Using Include="HiVakil.Repository.Abstracts" />
<Using Include="HiVakil.Repository.Repositories.Base.Contracts" />
<Using Include="Mapster" />
<Using Include="MediatR" />
<Using Include="Microsoft.AspNetCore.Identity" />
<Using Include="Microsoft.AspNetCore.Mvc" />
<Using Include="Microsoft.EntityFrameworkCore" />
<Using Include="Microsoft.Extensions.Options" />
<Using Include="Microsoft.IdentityModel.Tokens" />
<Using Include="NetinaShop.Domain.Dtos.RequestDtos" />
<Using Include="Newtonsoft.Json" />
<Using Include="System.IdentityModel.Tokens.Jwt" />
<Using Include="System.Security.Claims" />
<Using Include="System.Text" />
</ItemGroup>
</Project>

View File

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

View File

@ -0,0 +1,18 @@
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
namespace HiVakil.Core.Utilities;
public static class ImageConvertor
{
public static async Task<Stream> ImageResize(this FileUploadRequest fileUpload, Stream input, Stream output, int newWidth)
{
using var image = await Image.LoadAsync(input);
var height_width = image.Height / image.Width;
var new_Height = newWidth * height_width;
image.Mutate(x => x.Resize(newWidth, new_Height));
image.Mutate(x => x.Resize(newWidth, new_Height));
await image.SaveAsJpegAsync(output);
return output;
}
}

View File

@ -0,0 +1,7 @@
namespace HiVakil.Domain
{
public class DomainConfig
{
}
}

View File

@ -0,0 +1,10 @@
namespace NetinaShop.Domain.Dtos.RequestDtos;
public class RoleActionRequestDto
{
public Guid RoleId { get; set; }
public string Description { get; set; } = string.Empty;
public string EnglishName { get; set; } = string.Empty;
public string PersianName { get; set; } = string.Empty;
public List<string> Permissions { get; set; } = new();
}

View File

@ -0,0 +1,7 @@
namespace HiVakil.Domain.Dtos.RequestDto;
public class SignUpRequestDto
{
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
}

View File

@ -0,0 +1,14 @@
namespace HiVakil.Domain.Dtos.RequestDto;
public class UserActionRequestDto
{
public Guid UserId { get; set; }
public string PhoneNumber { get; set; } = string.Empty;
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public long BirthDateTimeStamp { get; set; }
public Gender Gender { get; set; }
public string NationalId { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
public List<Guid> RoleIds { get; set; } = new();
}

View File

@ -0,0 +1,8 @@
namespace HiVakil.Domain.Dtos.ResponseDto;
public class ProfileResponseDto
{
public List<string> Roles { get; set; } = new();
public ApplicationUserSDto? User { get; set; }
public List<string> Permissions { get; set; } = new();
}

View File

@ -0,0 +1,7 @@
namespace HiVakil.Domain.Dtos.ResponseDto;
public class VerifyCodeResponseDto
{
public SignUpStatus SignUpStatus { get; set; }
}

View File

@ -0,0 +1,19 @@
namespace HiVakil.Domain.Dtos.SmallDtos;
public class ApplicationUserSDto : BaseDto<ApplicationUserSDto, ApplicationUser>
{
public string PhoneNumber { get; set; } = string.Empty;
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public DateTime BirthDate { get; set; }
public Gender Gender { get; set; }
public SignUpStatus SignUpStatus { get; set; }
public string NationalId { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string FullName => FirstName + " " + LastName;
public string RoleName { get; set; } = string.Empty;
public List<Guid> RoleIds { get; set; } = new();
public long BirthDateTimeStamp => DateTimeExtensions.DateTimeToUnixTimeStamp(BirthDate);
}

View File

@ -0,0 +1,24 @@
namespace HiVakil.Domain.Dtos.SmallDtos;
public class StorageFileSDto : BaseDto<StorageFileSDto, StorageFile>
{
public string Name { get; set; } = string.Empty;
public string FileLocation { get; set; } = string.Empty;
public string FileName { get; set; } = string.Empty;
public bool IsHeader { get; set; }
public bool IsPrimary { get; set; }
public StorageFileType FileType { get; set; }
public bool Selected { get; set; }
public DateTime CreatedAt
{
get
{
string date = FileName.Split('.').First().Split('_').Last();
if (!date.IsNullOrEmpty() && long.TryParse(date, out long longDate))
return DateTimeExtensions.UnixTimeStampToDateTime(longDate);
return DateTime.MinValue;
}
}
}

View File

@ -0,0 +1,28 @@
namespace HiVakil.Domain.Entities.StorageFiles;
[AdaptTwoWays("[name]LDto", IgnoreAttributes = new[] { typeof(AdaptIgnoreAttribute) }, MapType = MapType.Map | MapType.MapToTarget | MapType.Projection)]
[AdaptTwoWays("[name]SDto", IgnoreAttributes = new[] { typeof(AdaptIgnoreAttribute) }, MapType = MapType.Map | MapType.MapToTarget)]
[AdaptTo("[name]SDto", IgnoreAttributes = new[] { typeof(AdaptIgnoreAttribute) }, MapType = MapType.Projection)]
[GenerateMapper]
public partial class StorageFile : ApiEntity
{
public StorageFile()
{
}
public StorageFile(string name, string fileLocation, string fileName, bool isHeader, bool isPrimary, StorageFileType fileType)
{
Name = name;
FileLocation = fileLocation;
FileName = fileName;
IsHeader = isHeader;
IsPrimary = isPrimary;
FileType = fileType;
}
public string Name { get; internal set; } = string.Empty;
public string FileLocation { get; internal set; } = string.Empty;
public string FileName { get; internal set; } = string.Empty;
public bool IsHeader { get; internal set; }
public bool IsPrimary { get; internal set; }
public StorageFileType FileType { get; internal set; }
}

View File

@ -0,0 +1,8 @@
namespace HiVakil.Domain.Entities.Users;
public class ApplicationRole : IdentityRole<Guid>
{
public string Description { get; set; } = string.Empty;
public string EnglishName { get; set; } = string.Empty;
public string PersianName { get; set; } = string.Empty;
}

View File

@ -0,0 +1,18 @@
using HiVakil.Domain.Enums;
namespace HiVakil.Domain.Entities.Users;
[AdaptTwoWays("[name]SDto", IgnoreAttributes = new[] { typeof(AdaptIgnoreAttribute) }, MapType = MapType.Map | MapType.MapToTarget )]
[AdaptTo("[name]SDto", IgnoreAttributes = new[] { typeof(AdaptIgnoreAttribute) }, MapType = MapType.Projection)]
[GenerateMapper]
public class ApplicationUser : IdentityUser<Guid>
{
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string NationalId { get; set; } = string.Empty;
public DateTime BirthDate { get; set; }
public Gender Gender { get; set; }
public SignUpStatus SignUpStatus { get; set; }
}

View File

@ -0,0 +1,9 @@
namespace HiVakil.Domain.Enums;
public enum Gender
{
[Display(Name = "مرد")]
Male,
[Display(Name = "بانو")]
Female
}

View File

@ -0,0 +1,8 @@
namespace HiVakil.Domain.Enums;
public enum SignUpStatus
{
StartSignOn = 0,
PhoneNumberVerified = 1,
SignUpCompleted = 10,
}

View File

@ -0,0 +1,9 @@
namespace HiVakil.Domain.Enums;
public enum StorageFileType
{
[Display(Name = "Images")]
Image,
[Display(Name = "Videos")]
Video
}

View File

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<PropertyChanged />
</Weavers>

View File

@ -0,0 +1,78 @@
<Project Sdk="Microsoft.NET.Sdk">
<!--<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mapster" Version="7.4.0" />
<PackageReference Include="Mapster.Core" Version="1.2.1" />
<PackageReference Include="MediatR" Version="12.2.0" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="8.0.2" />
<PackageReference Include="PropertyChanged.Fody" Version="4.1.0" />
</ItemGroup>-->
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>10</LangVersion>
<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="MediatR" Version="12.1.1" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="5.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.0" />
<PackageReference Include="PropertyChanged.Fody" Version="4.1.0" />
</ItemGroup>
<Target Name="Mapster">
<Exec WorkingDirectory="$(ProjectDir)" Command="dotnet build -p:CopyLocalLockFileAssemblies=true" />
<Exec WorkingDirectory="$(ProjectDir)" Command="dotnet tool restore" />
<Exec WorkingDirectory="$(ProjectDir)" Command="dotnet mapster extension -a &quot;$(TargetDir)$(ProjectName).dll&quot; -n HiVakil.Domain.Mappers -o Mappers" />
<Exec WorkingDirectory="$(ProjectDir)" Command="dotnet mapster mapper -a &quot;$(TargetDir)$(ProjectName).dll&quot; -n HiVakil.Domain.Mappers -o Mappers" />
</Target>
<ItemGroup>
<ProjectReference Include="..\HiVakil.Common\HiVakil.Common.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="HiVakil.Common.Extensions" />
<Using Include="HiVakil.Common.Models.Entity" />
<Using Include="HiVakil.Common.Models.Mapper" />
<Using Include="HiVakil.Domain.Dtos.SmallDtos" />
<Using Include="HiVakil.Domain.Entities.StorageFiles" />
<Using Include="HiVakil.Domain.Entities.Users" />
<Using Include="HiVakil.Domain.Enums" />
<Using Include="Mapster" />
<Using Include="MediatR" />
<Using Include="Microsoft.AspNetCore.Identity" />
<Using Include="Microsoft.IdentityModel.Tokens" />
<Using Include="System.ComponentModel" />
<Using Include="System.ComponentModel.DataAnnotations" />
<Using Include="System.Diagnostics" />
<Using Include="System.Reflection" />
<Using Include="System.Runtime.CompilerServices" />
<Using Include="System.Security.Claims" />
</ItemGroup>
<ItemGroup>
<Folder Include="Dtos\ResponseDto\" />
<Folder Include="Dtos\SmallDtos\" />
<Folder Include="Entities\StorageFiles\" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,93 @@
using System;
using System.Linq.Expressions;
using HiVakil.Domain.Dtos.SmallDtos;
using HiVakil.Domain.Entities.Users;
namespace HiVakil.Domain.Mappers
{
public static partial class ApplicationUserMapper
{
public static ApplicationUser AdaptToApplicationUser(this ApplicationUserSDto p1)
{
return p1 == null ? null : new ApplicationUser()
{
FirstName = p1.FirstName,
LastName = p1.LastName,
NationalId = p1.NationalId,
BirthDate = p1.BirthDate,
Gender = p1.Gender,
SignUpStatus = p1.SignUpStatus,
Id = p1.Id,
Email = p1.Email,
PhoneNumber = p1.PhoneNumber
};
}
public static ApplicationUser AdaptTo(this ApplicationUserSDto p2, ApplicationUser p3)
{
if (p2 == null)
{
return null;
}
ApplicationUser result = p3 ?? new ApplicationUser();
result.FirstName = p2.FirstName;
result.LastName = p2.LastName;
result.NationalId = p2.NationalId;
result.BirthDate = p2.BirthDate;
result.Gender = p2.Gender;
result.SignUpStatus = p2.SignUpStatus;
result.Id = p2.Id;
result.Email = p2.Email;
result.PhoneNumber = p2.PhoneNumber;
return result;
}
public static ApplicationUserSDto AdaptToSDto(this ApplicationUser p4)
{
return p4 == null ? null : new ApplicationUserSDto()
{
PhoneNumber = p4.PhoneNumber,
FirstName = p4.FirstName,
LastName = p4.LastName,
BirthDate = p4.BirthDate,
Gender = p4.Gender,
SignUpStatus = p4.SignUpStatus,
NationalId = p4.NationalId,
Email = p4.Email,
Id = p4.Id
};
}
public static ApplicationUserSDto AdaptTo(this ApplicationUser p5, ApplicationUserSDto p6)
{
if (p5 == null)
{
return null;
}
ApplicationUserSDto result = p6 ?? new ApplicationUserSDto();
result.PhoneNumber = p5.PhoneNumber;
result.FirstName = p5.FirstName;
result.LastName = p5.LastName;
result.BirthDate = p5.BirthDate;
result.Gender = p5.Gender;
result.SignUpStatus = p5.SignUpStatus;
result.NationalId = p5.NationalId;
result.Email = p5.Email;
result.Id = p5.Id;
return result;
}
public static Expression<Func<ApplicationUser, ApplicationUserSDto>> ProjectToSDto => p7 => new ApplicationUserSDto()
{
PhoneNumber = p7.PhoneNumber,
FirstName = p7.FirstName,
LastName = p7.LastName,
BirthDate = p7.BirthDate,
Gender = p7.Gender,
SignUpStatus = p7.SignUpStatus,
NationalId = p7.NationalId,
Email = p7.Email,
Id = p7.Id
};
}
}

View File

@ -0,0 +1,85 @@
using System;
using System.Linq.Expressions;
using HiVakil.Domain.Dtos.SmallDtos;
using HiVakil.Domain.Entities.StorageFiles;
namespace HiVakil.Domain.Mappers
{
public static partial class StorageFileMapper
{
public static StorageFile AdaptToStorageFile(this StorageFileSDto p1)
{
return p1 == null ? null : new StorageFile()
{
Name = p1.Name,
FileLocation = p1.FileLocation,
FileName = p1.FileName,
IsHeader = p1.IsHeader,
IsPrimary = p1.IsPrimary,
FileType = p1.FileType,
Id = p1.Id,
CreatedAt = p1.CreatedAt
};
}
public static StorageFile AdaptTo(this StorageFileSDto p2, StorageFile p3)
{
if (p2 == null)
{
return null;
}
StorageFile result = p3 ?? new StorageFile();
result.Name = p2.Name;
result.FileLocation = p2.FileLocation;
result.FileName = p2.FileName;
result.IsHeader = p2.IsHeader;
result.IsPrimary = p2.IsPrimary;
result.FileType = p2.FileType;
result.Id = p2.Id;
result.CreatedAt = p2.CreatedAt;
return result;
}
public static StorageFileSDto AdaptToSDto(this StorageFile p4)
{
return p4 == null ? null : new StorageFileSDto()
{
Name = p4.Name,
FileLocation = p4.FileLocation,
FileName = p4.FileName,
IsHeader = p4.IsHeader,
IsPrimary = p4.IsPrimary,
FileType = p4.FileType,
Id = p4.Id
};
}
public static StorageFileSDto AdaptTo(this StorageFile p5, StorageFileSDto p6)
{
if (p5 == null)
{
return null;
}
StorageFileSDto result = p6 ?? new StorageFileSDto();
result.Name = p5.Name;
result.FileLocation = p5.FileLocation;
result.FileName = p5.FileName;
result.IsHeader = p5.IsHeader;
result.IsPrimary = p5.IsPrimary;
result.FileType = p5.FileType;
result.Id = p5.Id;
return result;
}
public static Expression<Func<StorageFile, StorageFileSDto>> ProjectToSDto => p7 => new StorageFileSDto()
{
Name = p7.Name,
FileLocation = p7.FileLocation,
FileName = p7.FileName,
IsHeader = p7.IsHeader,
IsPrimary = p7.IsPrimary,
FileType = p7.FileType,
Id = p7.Id
};
}
}

View File

@ -0,0 +1,253 @@
namespace HiVakil.Domain.Models.Claims;
public static class ApplicationClaims
{
public static ClaimDto ManageBlogs { get; } = new ClaimDto
{
Title = "مدیریت بلاگ ها",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ManageBlogs,
};
public static ClaimDto ViewBlogs { get; } = new ClaimDto
{
Title = "مشاهده بلاگ ها",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ViewBlogs,
};
public static ClaimDto ManageBrands { get; } = new ClaimDto
{
Title = "مدیریت برند ها",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ManageBrands,
};
public static ClaimDto ViewBrands { get; } = new ClaimDto
{
Title = "مشاهده برند ها",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ViewBrands,
};
public static ClaimDto ManageCategories { get; } = new ClaimDto
{
Title = "مدیریت دسته بندی ها",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ManageCategories,
};
public static ClaimDto ViewCategories { get; } = new ClaimDto
{
Title = "مشاهده دسته بندی ها",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ViewCategories,
};
public static ClaimDto ManageDiscounts { get; } = new ClaimDto
{
Title = "مدیریت تخفیف ها",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ManageDiscounts,
};
public static ClaimDto ViewDiscounts { get; } = new ClaimDto
{
Title = "مشاهده تخفیف ها",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ViewDiscounts,
};
public static ClaimDto ManageOrders { get; } = new ClaimDto
{
Title = "مدیریت فروش ها",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ManageOrders,
};
public static ClaimDto ViewAllOrders { get; } = new ClaimDto
{
Title = "مشاهده فروش ها",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ViewAllOrders,
};
public static ClaimDto ViewMineOrders { get; } = new ClaimDto
{
Title = "مشاهده فروش های خود",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ViewMineOrders,
};
public static ClaimDto CreateOrder { get; } = new ClaimDto
{
Title = "ثبت سفارش",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.CreateOrder,
};
public static ClaimDto ManageProducts { get; } = new ClaimDto
{
Title = "مدیریت محصولات",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ManageProducts,
};
public static ClaimDto ViewProducts { get; } = new ClaimDto
{
Title = "مشاهده محصولات",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ViewProducts,
};
public static ClaimDto ManageReview { get; } = new ClaimDto
{
Title = "مدیریت کامنت ها",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ManageReview,
};
public static ClaimDto ViewAllReviews { get; } = new ClaimDto
{
Title = "مشاهده کامنت ها",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ViewAllReviews,
};
public static ClaimDto ViewMineReviews { get; } = new ClaimDto
{
Title = "مشاهده کامنت های خود",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ViewMineReviews,
};
public static ClaimDto AddReview { get; } = new ClaimDto
{
Title = "ثبت کامنت جدید",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.AddReview,
};
public static ClaimDto ConfirmReview { get; } = new ClaimDto
{
Title = "تائید کامنت ها",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ConfirmReview,
};
public static ClaimDto ViewWarehouses { get; } = new ClaimDto
{
Title = "مشاهده انبار ها",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ViewWarehouses,
};
public static ClaimDto ManageWarehouses { get; } = new ClaimDto
{
Title = "مدیریت انبار ها",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ManageWarehouses,
};
public static ClaimDto ViewShipping { get; } = new ClaimDto
{
Title = "مشاهده روش های ارسال",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ViewShipping,
};
public static ClaimDto ManageShipping { get; } = new ClaimDto
{
Title = "مدیریت روش های ارسال",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ManageShipping,
};
public static ClaimDto ManageUsers { get; } = new ClaimDto
{
Title = "مدیریت کاربران",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ManageUsers,
};
public static ClaimDto ViewUsers { get; } = new ClaimDto
{
Title = "مشاهده کاربران",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ViewUsers,
};
public static ClaimDto ManageFiles { get; } = new ClaimDto
{
Title = "مدیریت فایل ها",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ManageFiles,
};
public static ClaimDto ViewFiles { get; } = new ClaimDto
{
Title = "مشاهده فایل ها",
Type = CustomClaimType.Permission,
Value = ApplicationPermission.ViewFiles,
};
public static List<ClaimDto> AllClaimDtos = new List<ClaimDto>
{
ManageBlogs,
ViewBlogs,
ManageBrands,
ViewBrands,
ManageCategories,
ViewCategories,
ManageDiscounts,
ViewDiscounts,
ManageOrders,
ViewAllOrders,
ViewMineOrders,
CreateOrder,
ManageProducts,
ViewProducts,
ManageReview,
AddReview,
ConfirmReview,
ViewAllReviews,
ViewMineReviews,
ManageWarehouses,
ViewWarehouses,
ManageShipping,
ViewShipping,
ManageUsers,
ViewUsers,
ManageFiles,
ViewFiles
};
public static List<Claim> AllClaims = new List<Claim>
{
ManageBlogs.GetClaim,
ViewBlogs.GetClaim,
ManageBrands.GetClaim,
ViewBrands.GetClaim,
ManageCategories.GetClaim,
ViewCategories.GetClaim,
ManageDiscounts.GetClaim,
ViewDiscounts.GetClaim,
ManageOrders.GetClaim,
ViewAllOrders.GetClaim,
ViewMineOrders.GetClaim,
CreateOrder.GetClaim,
ManageProducts.GetClaim,
ViewProducts.GetClaim,
ManageReview.GetClaim,
AddReview.GetClaim,
ConfirmReview.GetClaim,
ViewAllReviews.GetClaim,
ViewMineReviews.GetClaim,
ManageWarehouses.GetClaim,
ViewWarehouses.GetClaim,
ManageShipping.GetClaim,
ViewShipping.GetClaim,
ManageUsers.GetClaim,
ViewUsers.GetClaim,
ManageFiles.GetClaim,
ViewFiles.GetClaim
};
public static List<Claim> CustomerClaims = new List<Claim>
{
ViewBlogs.GetClaim,
ViewBrands.GetClaim,
ViewCategories.GetClaim,
ViewMineOrders.GetClaim,
CreateOrder.GetClaim,
ViewProducts.GetClaim,
AddReview.GetClaim,
ViewMineReviews.GetClaim,
};
}

View File

@ -0,0 +1,42 @@
namespace HiVakil.Domain.Models.Claims;
public static class ApplicationPermission
{
public const string ManageBlogs = nameof(ManageBlogs);
public const string ViewBlogs = nameof(ManageBlogs);
public const string ManageBrands = nameof(ManageBrands);
public const string ViewBrands = nameof(ViewBrands);
public const string ManageCategories = nameof(ManageCategories);
public const string ViewCategories = nameof(ViewCategories);
public const string ManageDiscounts = nameof(ManageDiscounts);
public const string ViewDiscounts = nameof(ViewDiscounts);
public const string ManageOrders = nameof(ManageOrders);
public const string ViewAllOrders = nameof(ViewAllOrders);
public const string ViewMineOrders = nameof(ViewMineOrders);
public const string CreateOrder = nameof(CreateOrder);
public const string ManageProducts = nameof(ManageProducts);
public const string ViewProducts = nameof(ViewProducts);
public const string ManageReview = nameof(AddReview);
public const string AddReview = nameof(AddReview);
public const string ConfirmReview = nameof(ConfirmReview);
public const string ViewAllReviews = nameof(ViewAllReviews);
public const string ViewMineReviews = nameof(ViewMineReviews);
public const string ManageWarehouses = nameof(ManageWarehouses);
public const string ViewWarehouses = nameof(ViewWarehouses);
public const string ManageShipping = nameof(ManageShipping);
public const string ViewShipping = nameof(ViewShipping);
public const string ManageUsers = nameof(ManageUsers);
public const string ViewUsers = nameof(ViewUsers);
public const string ManageFiles = nameof(ManageFiles);
public const string ViewFiles = nameof(ViewFiles);
}

View File

@ -0,0 +1,16 @@
namespace HiVakil.Domain.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);
}

View File

@ -0,0 +1,7 @@
namespace HiVakil.Domain.Models.Claims
{
public static class CustomClaimType
{
public static string Permission { get; } = "Permission";
}
}

View File

@ -0,0 +1,45 @@
namespace HiVakil.Domain.Models.Settings;
public class SiteSettings
{
public JwtSettings JwtSettings { get; set; } = new JwtSettings();
public string BaseUrl { get; set; } = string.Empty;
public RedisSettings MasterRedisConfiguration { get; set; } = new RedisSettings();
public StorageSetting StorageSetting { get; set; } = new StorageSetting();
public UserSetting RootUser { get; set; } = new UserSetting();
public UserSetting ManagerUser { get; set; } = new UserSetting();
public string KaveNegarApiKey { get; set; } = string.Empty;
}
public class StorageSetting
{
public string AccessKey { get; set; } = string.Empty;
public string SecretKey { get; set; } = string.Empty;
}
public class RedisSettings
{
public string Password { get; set; } = string.Empty;
public string ServiceName { get; set; } = string.Empty;
public string Host { get; set; } = string.Empty;
public int Port { get; set; }
}
public class JwtSettings
{
public string SecretKey { get; set; } = string.Empty;
public string Issuer { get; set; } = string.Empty;
public string Audience { get; set; } = string.Empty;
public int ExpireAddDay { get; set; }
}
public class UserSetting
{
public string Username { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string RoleName { get; set; } = string.Empty;
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string Phone { get; set; } = string.Empty;
}

View File

@ -0,0 +1,45 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Marten" Version="7.0.0-beta.5" />
<PackageReference Include="AWSSDK.S3" Version="3.7.305.28" />
<PackageReference Include="Refit" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\HiVakil.Core\HiVakil.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="Amazon.S3" />
<Using Include="Amazon.S3.Model" />
<Using Include="HiVakil.Common.Extensions" />
<Using Include="HiVakil.Common.Models" />
<Using Include="HiVakil.Common.Models.Api" />
<Using Include="HiVakil.Common.Models.Exception" />
<Using Include="HiVakil.Core.Abstracts" />
<Using Include="HiVakil.Core.Utilities" />
<Using Include="HiVakil.Domain.Dtos.SmallDtos" />
<Using Include="HiVakil.Domain.Enums" />
<Using Include="HiVakil.Domain.Models.Settings" />
<Using Include="HiVakil.Infrastructure.Models" />
<Using Include="HiVakil.Infrastructure.Models.RestApi.KaveNegar" />
<Using Include="HiVakil.Infrastructure.Models.RestApi.Zarinpal" />
<Using Include="HiVakil.Infrastructure.RestServices" />
<Using Include="MediatR" />
<Using Include="Microsoft.Extensions.Hosting" />
<Using Include="Microsoft.Extensions.Logging" />
<Using Include="Microsoft.Extensions.Options" />
<Using Include="Refit" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,7 @@
namespace HiVakil.Infrastructure
{
public class InfrastructureConfig
{
}
}

View File

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

View File

@ -0,0 +1,7 @@
namespace HiVakil.Infrastructure.Models;
public static class RestAddress
{
public static string BaseKaveNegar => "https://api.kavenegar.com/v1/";
public static string BaseZarinpal => "https://api.zarinpal.com/pg";
}

View File

@ -0,0 +1,7 @@
namespace HiVakil.Infrastructure.Models.RestApi.KaveNegar;
public class KaveNegarResponse
{
public KaveNegarReturn Return { get; set; }
public KaveNegarResponseEntry[] entries { get; set; }
}

View File

@ -0,0 +1,13 @@
namespace HiVakil.Infrastructure.Models.RestApi.KaveNegar;
public class KaveNegarResponseEntry
{
public int messageid { get; set; }
public string message { get; set; }
public int status { get; set; }
public string statustext { get; set; }
public string sender { get; set; }
public string receptor { get; set; }
public int date { get; set; }
public int cost { get; set; }
}

View File

@ -0,0 +1,7 @@
namespace HiVakil.Infrastructure.Models.RestApi.KaveNegar;
public class KaveNegarReturn
{
public int status { get; set; }
public string message { get; set; }
}

View File

@ -0,0 +1,17 @@
namespace HiVakil.Infrastructure.Models.RestApi.Zarinpal;
public class ZarinaplPaymentLinkRequest
{
public string merchant_id { get; set; } = string.Empty;
public int amount { get; set; }
public string callback_url { get; set; } = string.Empty;
public string description { get; set; } = string.Empty;
public ZarinaplPaymentLinkRequestMetadata metadata { get; set; } = new();
}
public class ZarinaplPaymentLinkRequestMetadata
{
public string mobile { get; set; } = string.Empty;
public string email { get; set; } = string.Empty;
}

View File

@ -0,0 +1,16 @@
namespace HiVakil.Infrastructure.Models.RestApi.Zarinpal;
public class ZarinaplPaymentLinkResponse
{
public ZarinaplPaymentLinkResponseData data { get; set; }
public List<object> errors { get; set; }
}
public class ZarinaplPaymentLinkResponseData
{
public int code { get; set; }
public string message { get; set; } = string.Empty;
public string authority { get; set; } = string.Empty;
public string fee_type { get; set; } = string.Empty;
public int fee { get; set; }
}

View File

@ -0,0 +1,20 @@
namespace HiVakil.Infrastructure.Models.RestApi.Zarinpal;
public class ZarinaplPaymentVerifyResponse
{
public ZarinaplPaymentVerifyResponseData data { get; set; }
public List<object> errors { get; set; }
}
public class ZarinaplPaymentVerifyResponseData
{
public int code { get; set; }
public string message { get; set; } = string.Empty;
public string card_hash { get; set; } = string.Empty;
public string card_pan { get; set; } = string.Empty;
public int ref_id { get; set; }
public string fee_type { get; set; } = string.Empty;
public int fee { get; set; }
}

View File

@ -0,0 +1,8 @@
namespace HiVakil.Infrastructure.Models.RestApi.Zarinpal;
public class ZarinaplVerifyPaymentRequest
{
public string merchant_id { get; set; } = string.Empty;
public int amount { get; set; }
public string authority { get; set; } = string.Empty;
}

View File

@ -0,0 +1,10 @@
namespace HiVakil.Infrastructure.RestServices;
public interface IKaveNegarRestApi
{
[Post("/{apiKey}/verify/lookup.json")]
Task<KaveNegarResponse> SendLookUp(string apiKey, [Query] string receptor, [Query] string token, [Query] string token2, [Query] string token10, [Query] string token20, [Query] string template);
[Post("/{apiKey}/sms/send.json")]
Task<KaveNegarResponse> SendSms(string apiKey, [Query] string receptor, [Query] string message, [Query] string sender);
}

View File

@ -0,0 +1,14 @@
namespace HiVakil.Infrastructure.RestServices;
public interface IRestApiWrapper : IScopedDependency
{
IKaveNegarRestApi KaveNegarRestApi { get; }
IZarinpalRestApi ZarinpalRestApi { get; }
}
public class RestApiWrapper : IRestApiWrapper
{
public IKaveNegarRestApi KaveNegarRestApi => RestService.For<IKaveNegarRestApi>(RestAddress.BaseKaveNegar);
public IZarinpalRestApi ZarinpalRestApi => RestService.For<IZarinpalRestApi>(RestAddress.BaseZarinpal);
}

View File

@ -0,0 +1,12 @@
using HiVakil.Infrastructure.Models.RestApi.Zarinpal;
namespace HiVakil.Infrastructure.RestServices;
public interface IZarinpalRestApi
{
[Post("/v4/payment/request.json")]
Task<ZarinaplPaymentLinkResponse> GetPaymentLinkAsync([Body] ZarinaplPaymentLinkRequest request);
[Post("/v4/payment/verify.json")]
Task<ZarinaplPaymentVerifyResponse> VerifyPaymentAsync([Body] ZarinaplVerifyPaymentRequest request);
}

View File

@ -0,0 +1,55 @@
namespace HiVakil.Infrastructure.Services;
public class SmsService : ISmsService
{
private readonly IRestApiWrapper _restApiWrapper;
private readonly ILogger<SmsService> _logger;
private readonly IHostEnvironment _environment;
private readonly SiteSettings _siteSettings;
public SmsService(
IRestApiWrapper restApiWrapper,
IOptionsSnapshot<SiteSettings> optionsSnapshot,
ILogger<SmsService> logger,
IHostEnvironment environment)
{
_restApiWrapper = restApiWrapper;
_logger = logger;
_environment = environment;
_siteSettings = optionsSnapshot.Value;
}
public async Task SendForgerPasswordAsync(string phoneNumber, string newPassword)
{
var rest = await _restApiWrapper.KaveNegarRestApi.SendLookUp(_siteSettings.KaveNegarApiKey, phoneNumber, newPassword, null, null, null, "forgetPassword");
if (rest.Return.status != 200)
throw new BaseApiException(ApiResultStatusCode.SendSmsError, rest.Return.message);
}
public async Task SendVerifyCodeAsync(string phoneNumber, string verifyCode)
{
try
{
var rest = await _restApiWrapper.KaveNegarRestApi.SendLookUp(_siteSettings.KaveNegarApiKey, phoneNumber,
verifyCode, null, null, null, "login");
if (rest.Return.status != 200 && _environment.IsProduction())
throw new BaseApiException(ApiResultStatusCode.SendSmsError, rest.Return.message);
}
catch (ApiException apiException)
{
if (_environment.IsProduction())
throw ;
else
_logger.LogError(apiException.Message);
}
catch (Exception apiException)
{
if (_environment.IsProduction())
throw;
else
_logger.LogError(apiException.Message);
}
}
}

View File

@ -0,0 +1,120 @@
namespace HiVakil.Infrastructure.Services;
public class StorageService : IStorageService
{
private IAmazonS3? _s3Client;
private string _bucketName = "vesmeh-content";
private readonly StorageSetting _storageSettings;
public StorageService(IOptionsSnapshot<SiteSettings> snapshot)
{
_storageSettings = snapshot.Value.StorageSetting;
}
private IAmazonS3 GetClientAsync()
{
var awsCredentials = new Amazon.Runtime.BasicAWSCredentials(_storageSettings.AccessKey, _storageSettings.SecretKey);
var config = new AmazonS3Config { ServiceURL = "https://s3.ir-thr-at1.arvanstorage.ir" };
_s3Client = new AmazonS3Client(awsCredentials, config);
return _s3Client;
}
public async Task<string> UploadObjectFromFileAsync(string fileName, string filePath, string contentType, byte[] fileBytes)
{
using var memorySteam = new MemoryStream(fileBytes);
var client = GetClientAsync();
var fileEx = fileName.Split('.').Last();
fileName = fileName.Split('.').First();
fileName = fileName + "_" + StringExtensions.GetId(5) + "_" + DateTime.Today.ToString("yyyy-MM-dd-HH-mm-ss") + "." + fileEx;
var putRequest = new PutObjectRequest
{
BucketName = _bucketName,
Key = Path.Combine(filePath, fileName),
ContentType = contentType,
InputStream = memorySteam,
CannedACL = S3CannedACL.PublicRead
};
putRequest.Metadata.Add("x-amz-meta-title", fileName);
PutObjectResponse response = await client.PutObjectAsync(putRequest);
return fileName;
}
public async Task<string> UploadObjectFromFileAsync(string fileName, string filePath, string contentType, Stream fileStream , bool fixName = true)
{
var client = GetClientAsync();
if (fixName)
{
var fileEx = fileName.Split('.').Last();
fileName = fileName.Split('.').First();
fileName = fileName + "_" + StringExtensions.GetId(5) + "_" + DateTimeExtensions.DateTimeToUnixTimeStamp(DateTime.Now) + "." + fileEx;
}
var putRequest = new PutObjectRequest
{
BucketName = _bucketName,
Key = Path.Combine(filePath, fileName),
ContentType = contentType,
InputStream = fileStream,
CannedACL = S3CannedACL.PublicRead
};
//putRequest.Metadata.Add("x-amz-meta-title", fileName);
PutObjectResponse response = await client.PutObjectAsync(putRequest);
return fileName;
}
public async Task<List<StorageFileSDto>> GetStorageFiles(StorageFileType fileType)
{
var client = GetClientAsync();
ListObjectsRequest request = new() { BucketName = _bucketName};
switch (fileType)
{
case StorageFileType.Image:
request.Prefix = "Images/Med";
break;
case StorageFileType.Video:
request.Prefix = "Videos";
break;
default:
break;
}
var files = new List<StorageFileSDto>();
do
{
ListObjectsResponse response = await client.ListObjectsAsync(request);
// Process the response.
foreach (var s3Object in response.S3Objects.Where(s=>s.Size>0))
{
files.Add(new StorageFileSDto
{
FileLocation = s3Object.Key,
FileName = s3Object.Key.Split('/').Last()
});
}
// If the response is truncated, set the marker to get the next
// set of keys.
if (response.IsTruncated)
{
request.Marker = response.NextMarker;
}
else
{
request = null;
}
} while (request != null);
return files.OrderByDescending(o=>o.CreatedAt).ToList();
}
}

View File

@ -0,0 +1,31 @@
namespace HiVakil.Infrastructure.Services;
public class UploadFileService : IUploadFileService
{
private readonly IStorageService _storageService;
public UploadFileService(IStorageService storageService)
{
_storageService = storageService;
}
public async Task<FileUploadResponse> UploadImageAsync(FileUploadRequest uploadRequest)
{
var bytes = Convert.FromBase64String(uploadRequest.StringBaseFile);
using var originalMedFileStream = new MemoryStream(bytes);
using var originalThumbFileStream = new MemoryStream(bytes);
using var thumbnailFileStream = new MemoryStream();
using var mediumFileStream = new MemoryStream();
await uploadRequest.ImageResize(originalMedFileStream, mediumFileStream, 1280);
await uploadRequest.ImageResize(originalThumbFileStream, thumbnailFileStream, 200);
var medFileName = await _storageService.UploadObjectFromFileAsync(uploadRequest.FileName, $"{uploadRequest.FileUploadType.ToDisplay()}/Med", uploadRequest.ContentType, mediumFileStream);
await _storageService.UploadObjectFromFileAsync(medFileName, $"{uploadRequest.FileUploadType.ToDisplay()}/Thumb", uploadRequest.ContentType, thumbnailFileStream, false);
var response = new FileUploadResponse
{
FileName = medFileName,
FileLocation = $"{uploadRequest.FileUploadType.ToDisplay()}/Med/{medFileName}",
FileUrl = $"https://storage.vesmook.com/{uploadRequest.FileUploadType.ToDisplay()}/Med/{medFileName}"
};
return response;
}
}

View File

@ -0,0 +1,39 @@
namespace HiVakil.Infrastructure.Services;
public class ZarinpalService : IPaymentService
{
private readonly IRestApiWrapper _restApiWrapper;
private readonly IMediator _mediator;
private readonly SiteSettings _siteSettings;
public ZarinpalService(IRestApiWrapper restApiWrapper,
IOptionsSnapshot<SiteSettings> snapshot,
IMediator mediator)
{
_restApiWrapper = restApiWrapper;
_mediator = mediator;
_siteSettings = snapshot.Value;
}
public async Task<string> GetPaymentLinkAsync(double amount,string factorNumber, Guid orderId, Guid userId, string phoneNumber , string fullName , CancellationToken cancellationToken = default)
{
var request = new ZarinaplPaymentLinkRequest
{
description = $"پرداخت {amount.ToString("N0")} ریال توسط {fullName} با شماره تماس {phoneNumber} برای سفارش با شماره {factorNumber}",
amount = (int)amount,
callback_url = Path.Combine(_siteSettings.BaseUrl, "api", "accounting", "pay", "verify"),
//merchant_id = "4292b845-b510-4d1d-8ee2-097499b198e5",
metadata = new ZarinaplPaymentLinkRequestMetadata { mobile = phoneNumber }
};
var response = await _restApiWrapper.ZarinpalRestApi.GetPaymentLinkAsync(request);
if (response.data.code != 100)
throw new AppException($"Exception in get link from zarinpal | {response.data.message}");
string link = $"https://www.zarinpal.com/pg/StartPay/{response.data.authority}";
return link;
}
public async Task<Tuple<string,string>> VerifyPaymentAsync(string authority, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
}

View File

@ -0,0 +1,11 @@
namespace HiVakil.Repository.Abstracts;
public interface ICurrentUserService : IScopedDependency
{
string? UserId { get; }
string? RoleName { get; }
string? UserName { get; }
string? DeviceId { get; }
bool IsAuthorized { get; }
public List<string>? Permissions { get; }
}

View File

@ -0,0 +1,33 @@
using ValidationException = HiVakil.Common.Models.Exception.ValidationException;
namespace HiVakil.Repository.Behaviors;
public class ValidationBehavior <TRequest,TResponse> : IPipelineBehavior<TRequest,TResponse> where TRequest : notnull
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
{
var context = new ValidationContext<TRequest>(request);
List<ValidationError> errors = new List<ValidationError>();
foreach (IValidator<TRequest> validator in _validators)
{
var result = await validator.ValidateAsync(context, cancellationToken);
if (!result.IsValid)
errors.AddRange(result.Errors.Select(v => new ValidationError(v.PropertyName, v.ErrorMessage)).Distinct());
}
if (errors.Any())
{
throw new ValidationException(errors);
}
var response = await next();
return response;
}
}

View File

@ -0,0 +1,59 @@
namespace HiVakil.Repository.Extensions;
public class DbContextOptionCustomExtensionsInfo : DbContextOptionsExtensionInfo
{
public DbContextOptionCustomExtensionsInfo(IDbContextOptionsExtension extension) : base(extension)
{
}
public override bool IsDatabaseProvider { get; }
public override string LogFragment { get; } = string.Empty;
public override int GetServiceProviderHashCode()
{
return Extension.GetHashCode();
}
public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
{
}
public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
{
return true;
}
}
public class DbContextOptionCustomExtensions : IDbContextOptionsExtension
{
public DbContextOptionCustomExtensions()
{
Info = new DbContextOptionCustomExtensionsInfo(this);
}
public Assembly ProjectAssembly { get; set; } = Assembly.GetExecutingAssembly();
public void ApplyServices(IServiceCollection services)
{
}
public void Validate(IDbContextOptions options)
{
}
public DbContextOptionsExtensionInfo Info { get; }
}
public static class ApplicationContextExtensions
{
public static DbContextOptionsBuilder UseProjectAssembly(this DbContextOptionsBuilder contextOptions,
Assembly projectAssembly)
{
var extension = new DbContextOptionCustomExtensions
{
ProjectAssembly = projectAssembly
};
((IDbContextOptionsBuilderInfrastructure)contextOptions).AddOrUpdateExtension(extension);
return contextOptions;
}
}

View File

@ -0,0 +1,190 @@
namespace HiVakil.Repository.Extensions;
public class ModelBuilderQueryFilter
{
public void AddQueryFilterToModelBuilder(ModelBuilder modelBuilder, Type type)
{
var method = GetType().GetMethod("RegisterQueryFilter").MakeGenericMethod(type);
method.Invoke(this, new object[] { modelBuilder });
}
public void RegisterQueryFilter<TQFilter>(ModelBuilder modelBuilder) where TQFilter : ApiEntity
{
var tt = typeof(TQFilter);
if (tt.BaseType == typeof(ApiEntity))
modelBuilder.Entity<TQFilter>().HasQueryFilter(e => e.IsRemoved == false);
}
}
public static class ModelBuilderExtensions
{
/// <summary>
/// Singularizin table name like Posts to Post or People to Person
/// </summary>
/// <param name="modelBuilder"></param>
public static void AddSingularizingTableNameConvention(this ModelBuilder modelBuilder)
{
var pluralizer = new Pluralizer();
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
var tableName = entityType.GetTableName();
entityType.SetTableName(pluralizer.Singularize(tableName));
}
}
/// <summary>
/// Set NEWSEQUENTIALID() sql function for all columns named "Id"
/// </summary>
/// <param name="modelBuilder"></param>
/// <param name="mustBeIdentity">Set to true if you want only "Identity" guid fields that named "Id"</param>
public static void AddSequentialGuidForIdConvention(this ModelBuilder modelBuilder)
{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
var property = entityType.GetProperties()
.Where(p => p.Name.Contains("Id", StringComparison.OrdinalIgnoreCase))
.ToArray();
foreach (var mutableProperty in property)
modelBuilder.AddDefaultValueSqlConvention(mutableProperty?.Name, typeof(Guid), "gen_random_uuid()");
}
}
/// <summary>
/// Set DefaultValueSql for sepecific property name and type
/// </summary>
/// <param name="modelBuilder"></param>
/// <param name="propertyName">Name of property wants to set DefaultValueSql for</param>
/// <param name="propertyType">Type of property wants to set DefaultValueSql for </param>
/// <param name="defaultValueSql">DefaultValueSql like "NEWSEQUENTIALID()"</param>
public static void AddDefaultValueSqlConvention(this ModelBuilder modelBuilder, string propertyName,
Type propertyType, string defaultValueSql)
{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
var property = entityType.GetProperties()
.SingleOrDefault(p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase));
if (property != null && property.ClrType == propertyType)
property.SetDefaultValueSql(defaultValueSql);
}
}
/// <summary>
/// Set DeleteBehavior.Restrict by default for relations
/// </summary>
/// <param name="modelBuilder"></param>
public static void AddRestrictDeleteBehaviorConvention(this ModelBuilder modelBuilder)
{
var cascadeFKs = modelBuilder.Model.GetEntityTypes()
.SelectMany(t => t.GetForeignKeys())
.Where(fk => !fk.IsOwnership && fk.DeleteBehavior == DeleteBehavior.Cascade);
foreach (var fk in cascadeFKs)
{
fk.DeleteBehavior = DeleteBehavior.Restrict;
fk.IsRequired = false;
}
}
/// <summary>
/// Dynamicaly load all IEntityTypeConfiguration with Reflection
/// </summary>
/// <param name="modelBuilder"></param>
/// <param name="assemblies">Assemblies contains Entities</param>
public static void RegisterEntityTypeConfiguration(this ModelBuilder modelBuilder, params Assembly[] assemblies)
{
var applyGenericMethod = typeof(ModelBuilder)
.GetMethods()
.First(m => m.Name == nameof(ModelBuilder.ApplyConfiguration));
var types = assemblies.SelectMany(a => a.GetExportedTypes())
.Where(c => c.IsClass && !c.IsAbstract && c.IsPublic);
foreach (var type in types)
foreach (var iface in type.GetInterfaces())
if (iface.IsConstructedGenericType &&
iface.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>))
{
var applyConcreteMethod = applyGenericMethod.MakeGenericMethod(iface.GenericTypeArguments[0]);
applyConcreteMethod.Invoke(modelBuilder, new[] { Activator.CreateInstance(type) });
}
}
/// <summary>
/// Pluralizing table name like Post to Posts or Person to People
/// </summary>
/// <param name="modelBuilder"></param>
public static void AddPluralizingTableNameConvention(this ModelBuilder modelBuilder)
{
var pluralizer = new Pluralizer();
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
if (entityType.BaseType == null)
{
var tableName = entityType.GetTableName();
entityType.SetTableName(pluralizer.Pluralize(tableName));
}
}
/// <summary>
/// Dynamicaly register all Entities that inherit from specific BaseType
/// </summary>
/// <param name="modelBuilder"></param>
/// <param name="baseType">Base type that Entities inherit from this</param>
/// <param name="assemblies">Assemblies contains Entities</param>
public static void RegisterAllEntities<BaseType>(this ModelBuilder modelBuilder,
ILogger<ApplicationContext> _logger, params Assembly[] assemblies) where BaseType : ApiEntity
{
var types = assemblies.SelectMany(a => a.GetExportedTypes())
.Where(c => c.IsClass && !c.IsAbstract && c.IsPublic && typeof(BaseType)
.IsAssignableFrom(c));
var builderQueryFilter = new ModelBuilderQueryFilter();
foreach (var type in types)
{
modelBuilder.Entity(type);
builderQueryFilter.AddQueryFilterToModelBuilder(modelBuilder, type);
}
}
/// <summary>
/// Dynamicaly register all Entities that inherit from specific BaseType
/// </summary>
/// <param name="modelBuilder"></param>
/// <param name="baseType">Base type that Entities inherit from this</param>
/// <param name="assemblies">Assemblies contains Entities</param>
public static void RegisterAllEntitiesV02<BaseType>(this ModelBuilder builder, ILogger<ApplicationContext> _logger, params Assembly[] assemblies) where BaseType : ApiEntity
{
var types = assemblies.SelectMany(a => a.GetExportedTypes())
.Where(c => c.IsClass && !c.IsAbstract && c.IsPublic && typeof(BaseType)
.IsAssignableFrom(c));
var builderQueryFilter = new ModelBuilderQueryFilter();
foreach (var type in types)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
// On Model Creating
var onModelCreatingMethod =
type.GetMethods().FirstOrDefault(x => x.Name == "OnModelCreating");
if (onModelCreatingMethod != null)
onModelCreatingMethod.Invoke(type, new object[] { builder });
else
{
// On Base Model Creating
if (type.BaseType == null || type.BaseType != typeof(BaseType)) continue;
var baseOnModelCreatingMethod = type.BaseType.GetMethods()
.FirstOrDefault(x => x.Name == "OnModelCreating");
if (baseOnModelCreatingMethod == null)
continue;
baseOnModelCreatingMethod.Invoke(typeof(BaseType), new object[] { builder });
}
builderQueryFilter.AddQueryFilterToModelBuilder(builder, type);
stopwatch.Stop();
_logger.LogInformation($"MODEL BUILDER {type.Name} In : {stopwatch.ElapsedMilliseconds}ms");
}
}
}

View File

@ -0,0 +1,68 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="12.2.0" />
<PackageReference Include="FluentValidation" Version="11.9.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.2">
<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="8.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.2" />
<PackageReference Include="StackExchange.Redis" Version="2.7.20" />
<PackageReference Include="StackExchange.Redis.Extensions.Core" Version="10.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\HiVakil.Domain\HiVakil.Domain.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="FluentValidation" />
<Using Include="HiVakil.Common.Extensions" />
<Using Include="HiVakil.Common.Models" />
<Using Include="HiVakil.Common.Models.Api" />
<Using Include="HiVakil.Common.Models.Entity" />
<Using Include="HiVakil.Common.Models.Exception" />
<Using Include="HiVakil.Domain.Entities.Users" />
<Using Include="HiVakil.Domain.Enums" />
<Using Include="HiVakil.Domain.Models.Claims" />
<Using Include="HiVakil.Domain.Models.Settings" />
<Using Include="HiVakil.Repository.Abstracts" />
<Using Include="HiVakil.Repository.Extensions" />
<Using Include="HiVakil.Repository.Models" />
<Using Include="HiVakil.Repository.Repositories.Base.Contracts" />
<Using Include="HiVakil.Repository.Services.Abstracts" />
<Using Include="Mapster" />
<Using Include="MediatR" />
<Using Include="Microsoft.AspNetCore.Builder" />
<Using Include="Microsoft.AspNetCore.Identity" />
<Using Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
<Using Include="Microsoft.EntityFrameworkCore" />
<Using Include="Microsoft.EntityFrameworkCore.ChangeTracking" />
<Using Include="Microsoft.EntityFrameworkCore.Infrastructure" />
<Using Include="Microsoft.EntityFrameworkCore.Storage" />
<Using Include="Microsoft.Extensions.DependencyInjection" />
<Using Include="Microsoft.Extensions.Logging" />
<Using Include="Microsoft.Extensions.Options" />
<Using Include="Pluralize.NET" />
<Using Include="System.Diagnostics" />
<Using Include="System.Reflection" />
</ItemGroup>
</Project>

Some files were not shown because too many files have changed in this diff Show More