diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 0000000..db4df7b --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "mapster.tool": { + "version": "8.4.0", + "commands": [ + "dotnet-mapster" + ] + } + } +} \ No newline at end of file diff --git a/HiVakil.Api/AppSettings/Production/appsettings.Production.json b/HiVakil.Api/AppSettings/Production/appsettings.Production.json new file mode 100644 index 0000000..95d6f3a --- /dev/null +++ b/HiVakil.Api/AppSettings/Production/appsettings.Production.json @@ -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": "*" +} \ No newline at end of file diff --git a/HiVakil.Api/appsettings.json b/HiVakil.Api/AppSettings/Production/appsettings.json similarity index 100% rename from HiVakil.Api/appsettings.json rename to HiVakil.Api/AppSettings/Production/appsettings.json diff --git a/HiVakil.Api/AppSettings/appsettings.Development.json b/HiVakil.Api/AppSettings/appsettings.Development.json new file mode 100644 index 0000000..fd97c48 --- /dev/null +++ b/HiVakil.Api/AppSettings/appsettings.Development.json @@ -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": "*" +} \ No newline at end of file diff --git a/HiVakil.Api/appsettings.Development.json b/HiVakil.Api/AppSettings/appsettings.json similarity index 80% rename from HiVakil.Api/appsettings.Development.json rename to HiVakil.Api/AppSettings/appsettings.json index 0c208ae..10f68b8 100644 --- a/HiVakil.Api/appsettings.Development.json +++ b/HiVakil.Api/AppSettings/appsettings.json @@ -4,5 +4,6 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } - } + }, + "AllowedHosts": "*" } diff --git a/HiVakil.Api/HiVakil.Api.csproj b/HiVakil.Api/HiVakil.Api.csproj index eaef27d..fa91777 100644 --- a/HiVakil.Api/HiVakil.Api.csproj +++ b/HiVakil.Api/HiVakil.Api.csproj @@ -9,9 +9,9 @@ - - - + + + diff --git a/HiVakil.Common/CommonConfig.cs b/HiVakil.Common/CommonConfig.cs new file mode 100644 index 0000000..84700a3 --- /dev/null +++ b/HiVakil.Common/CommonConfig.cs @@ -0,0 +1,7 @@ +namespace HiVakil.Common +{ + public class CommonConfig + { + + } +} diff --git a/HiVakil.Common/Extensions/AssertExtensions.cs b/HiVakil.Common/Extensions/AssertExtensions.cs new file mode 100644 index 0000000..e530383 --- /dev/null +++ b/HiVakil.Common/Extensions/AssertExtensions.cs @@ -0,0 +1,30 @@ +using System.Collections; + +namespace HiVakil.Common.Extensions +{ + public static class AssertExtensions + { + public static void NotNull(T obj, string name, string message = null) + where T : class + { + if (obj is null) + throw new ArgumentNullException($"{name} : {typeof(T)}", message); + } + + public static void NotNull(T? obj, string name, string message = null) + where T : struct + { + if (!obj.HasValue) + throw new ArgumentNullException($"{name} : {typeof(T)}", message); + } + + public static void NotEmpty(T obj, string name, string message = null, T defaultValue = null) + where T : class + { + if (obj == defaultValue + || obj is string str && string.IsNullOrWhiteSpace(str) + || obj is IEnumerable list && !list.Cast().Any()) + throw new ArgumentException("Argument is empty : " + message, $"{name} : {typeof(T)}"); + } + } +} \ No newline at end of file diff --git a/HiVakil.Common/Extensions/BoolExtensions.cs b/HiVakil.Common/Extensions/BoolExtensions.cs new file mode 100644 index 0000000..aa76c87 --- /dev/null +++ b/HiVakil.Common/Extensions/BoolExtensions.cs @@ -0,0 +1,9 @@ +namespace HiVakil.Common.Extensions; + +public static class BoolExtensions +{ + public static string ToPersianString(this bool value) + { + return value ? "بله" : "خیر"; + } +} \ No newline at end of file diff --git a/HiVakil.Common/Extensions/ClassDisplayExtensions.cs b/HiVakil.Common/Extensions/ClassDisplayExtensions.cs new file mode 100644 index 0000000..488e50b --- /dev/null +++ b/HiVakil.Common/Extensions/ClassDisplayExtensions.cs @@ -0,0 +1,39 @@ +using HiVakil.Common.Models.Entity; + +namespace HiVakil.Common.Extensions +{ + public static class ClassDisplayExtensions + { + public static string GetDisplayAttributeName() + { + var attrs = + Attribute.GetCustomAttributes(typeof(T)); + + foreach (var attr in attrs) + { + var displayAttribute = attr as PageClassDisplay; + if (displayAttribute == null) + continue; + return displayAttribute.GetName(); + } + + return null; + } + + public static string GetDisplayAttributeDescription() + { + var attrs = + Attribute.GetCustomAttributes(typeof(T)); + + foreach (var attr in attrs) + { + var displayAttribute = attr as PageClassDisplay; + if (displayAttribute == null) + continue; + return displayAttribute.GetDescription(); + } + + return null; + } + } +} \ No newline at end of file diff --git a/HiVakil.Common/Extensions/DateTimeExtensions.cs b/HiVakil.Common/Extensions/DateTimeExtensions.cs new file mode 100644 index 0000000..f4b55a6 --- /dev/null +++ b/HiVakil.Common/Extensions/DateTimeExtensions.cs @@ -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); + } +} \ No newline at end of file diff --git a/HiVakil.Common/Extensions/EnumExtensions.cs b/HiVakil.Common/Extensions/EnumExtensions.cs new file mode 100644 index 0000000..fb56ca3 --- /dev/null +++ b/HiVakil.Common/Extensions/EnumExtensions.cs @@ -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 GetEnumValues(this T input) where T : struct + { + if (!typeof(T).IsEnum) + throw new NotSupportedException(); + + return Enum.GetValues(input.GetType()).Cast(); + } + + public static IEnumerable GetEnumFlags(this T input) where T : struct + { + if (!typeof(T).IsEnum) + throw new NotSupportedException(); + + foreach (var value in Enum.GetValues(input.GetType())) + if ((input as Enum).HasFlag(value as Enum)) + yield return (T)value; + } + + public static string ToDisplay(this Enum value, DisplayProperty property = DisplayProperty.Name) + { + AssertExtensions.NotNull(value, nameof(value)); + + var attribute = value.GetType().GetField(value.ToString()) + .GetCustomAttributes(false).FirstOrDefault(); + + if (attribute == null) + return value.ToString(); + + var propValue = attribute.GetType().GetProperty(property.ToString()).GetValue(attribute, null); + return propValue.ToString(); + } + + public static Dictionary ToDictionary(this Enum value) + { + return Enum.GetValues(value.GetType()).Cast().ToDictionary(p => Convert.ToInt32(p), q => ToDisplay(q)); + } + } +} \ No newline at end of file diff --git a/HiVakil.Common/Extensions/NewtonJsonExtensions.cs b/HiVakil.Common/Extensions/NewtonJsonExtensions.cs new file mode 100644 index 0000000..4c687e8 --- /dev/null +++ b/HiVakil.Common/Extensions/NewtonJsonExtensions.cs @@ -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 + }; + } +} \ No newline at end of file diff --git a/HiVakil.Common/Extensions/PhoneNumberExtensions.cs b/HiVakil.Common/Extensions/PhoneNumberExtensions.cs new file mode 100644 index 0000000..f15f5af --- /dev/null +++ b/HiVakil.Common/Extensions/PhoneNumberExtensions.cs @@ -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; + } + + +} \ No newline at end of file diff --git a/HiVakil.Common/Extensions/PropertyExtensions.cs b/HiVakil.Common/Extensions/PropertyExtensions.cs new file mode 100644 index 0000000..224fcc2 --- /dev/null +++ b/HiVakil.Common/Extensions/PropertyExtensions.cs @@ -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().FirstOrDefault(); + if (attr == null) return memberInfo.Name; + + return attr.Name; + } + } +} \ No newline at end of file diff --git a/HiVakil.Common/Extensions/RandomExtensions.cs b/HiVakil.Common/Extensions/RandomExtensions.cs new file mode 100644 index 0000000..b9d3c0f --- /dev/null +++ b/HiVakil.Common/Extensions/RandomExtensions.cs @@ -0,0 +1,12 @@ +namespace HiVakil.Common.Extensions +{ + public static class RandomExtensions + { + public static T RandomItem(List originList) + { + var random = new Random(DateTime.Now.Millisecond); + var rand = random.Next(0, originList.Count - 1); + return originList[rand]; + } + } +} \ No newline at end of file diff --git a/HiVakil.Common/Extensions/StringExtensions.cs b/HiVakil.Common/Extensions/StringExtensions.cs new file mode 100644 index 0000000..c266ebf --- /dev/null +++ b/HiVakil.Common/Extensions/StringExtensions.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/HiVakil.Common/Extensions/ValidationExtensions.cs b/HiVakil.Common/Extensions/ValidationExtensions.cs new file mode 100644 index 0000000..ce457c1 --- /dev/null +++ b/HiVakil.Common/Extensions/ValidationExtensions.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/HiVakil.Common/HiVakil.Common.csproj b/HiVakil.Common/HiVakil.Common.csproj new file mode 100644 index 0000000..74ba3af --- /dev/null +++ b/HiVakil.Common/HiVakil.Common.csproj @@ -0,0 +1,39 @@ + + + + + + net5.0 + 10 + enable + + + + + + + + + + + + + + + + + + + diff --git a/HiVakil.Common/Models/Api/AccessToken.cs b/HiVakil.Common/Models/Api/AccessToken.cs new file mode 100644 index 0000000..561b9ba --- /dev/null +++ b/HiVakil.Common/Models/Api/AccessToken.cs @@ -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 + { + public AccessToken() + { + } + + public AccessToken(JwtSecurityToken securityToken) + { + access_token = new JwtSecurityTokenHandler().WriteToken(securityToken); + token_type = "Bearer"; + expires_in = (int)(securityToken.ValidTo - DateTime.UtcNow).TotalSeconds; + } + + public string access_token { get; set; } = string.Empty; + public string ig_access_token { get; set; } = string.Empty; + public string refresh_token { get; set; } = string.Empty; + public string token_type { get; set; } = string.Empty; + public int expires_in { get; set; } + + public TUser User { get; set; } + public string BearerToken => $"Bearer {access_token}"; + public List Permissions { get; set; } + public string RoleName { get; set; } + } + + + public class AccessToken + { + public AccessToken() + { + } + + public AccessToken(JwtSecurityToken securityToken) + { + access_token = new JwtSecurityTokenHandler().WriteToken(securityToken); + token_type = "Bearer"; + expires_in = (int)(securityToken.ValidTo - DateTime.UtcNow).TotalSeconds; + } + + public string access_token { get; set; } = string.Empty; + public string ig_access_token { get; set; } = string.Empty; + public string refresh_token { get; set; } = string.Empty; + public string token_type { get; set; } = string.Empty; + public int expires_in { get; set; } + + public TUser User { get; set; } + public string BearerToken => $"Bearer {access_token}"; + public List Permissions { get; set; } + public List Roles { get; set; } = new(); + } +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Api/ApiResultStatusCode.cs b/HiVakil.Common/Models/Api/ApiResultStatusCode.cs new file mode 100644 index 0000000..5fa7e9d --- /dev/null +++ b/HiVakil.Common/Models/Api/ApiResultStatusCode.cs @@ -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 + } +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Api/AppSettings.cs b/HiVakil.Common/Models/Api/AppSettings.cs new file mode 100644 index 0000000..bcdad73 --- /dev/null +++ b/HiVakil.Common/Models/Api/AppSettings.cs @@ -0,0 +1,7 @@ +namespace HiVakil.Common.Models.Api +{ + public class AppSettings + { + public bool Seeded { get; set; } + } +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Api/FileUploadRequest.cs b/HiVakil.Common/Models/Api/FileUploadRequest.cs new file mode 100644 index 0000000..325d973 --- /dev/null +++ b/HiVakil.Common/Models/Api/FileUploadRequest.cs @@ -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; } + } +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Api/FileUploadResponse.cs b/HiVakil.Common/Models/Api/FileUploadResponse.cs new file mode 100644 index 0000000..044837a --- /dev/null +++ b/HiVakil.Common/Models/Api/FileUploadResponse.cs @@ -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; +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Api/HealthCheck.cs b/HiVakil.Common/Models/Api/HealthCheck.cs new file mode 100644 index 0000000..272b145 --- /dev/null +++ b/HiVakil.Common/Models/Api/HealthCheck.cs @@ -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; + } +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Api/ResponseFile.cs b/HiVakil.Common/Models/Api/ResponseFile.cs new file mode 100644 index 0000000..5f71c5c --- /dev/null +++ b/HiVakil.Common/Models/Api/ResponseFile.cs @@ -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; } + } +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Api/TokenRequest.cs b/HiVakil.Common/Models/Api/TokenRequest.cs new file mode 100644 index 0000000..bedba00 --- /dev/null +++ b/HiVakil.Common/Models/Api/TokenRequest.cs @@ -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; } + } +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Entity/ApiEntity.cs b/HiVakil.Common/Models/Entity/ApiEntity.cs new file mode 100644 index 0000000..195f455 --- /dev/null +++ b/HiVakil.Common/Models/Entity/ApiEntity.cs @@ -0,0 +1,48 @@ +namespace HiVakil.Common.Models.Entity; +public abstract class ApiEntity : IApiEntity , IEquatable +{ + [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(); + } + +} diff --git a/HiVakil.Common/Models/Entity/IApiEntity.cs b/HiVakil.Common/Models/Entity/IApiEntity.cs new file mode 100644 index 0000000..77e50c6 --- /dev/null +++ b/HiVakil.Common/Models/Entity/IApiEntity.cs @@ -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; } + } +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Entity/PageClassDisplay.cs b/HiVakil.Common/Models/Entity/PageClassDisplay.cs new file mode 100644 index 0000000..195f604 --- /dev/null +++ b/HiVakil.Common/Models/Entity/PageClassDisplay.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Exception/AppException.cs b/HiVakil.Common/Models/Exception/AppException.cs new file mode 100644 index 0000000..566a5c2 --- /dev/null +++ b/HiVakil.Common/Models/Exception/AppException.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Exception/BaseApiException.cs b/HiVakil.Common/Models/Exception/BaseApiException.cs new file mode 100644 index 0000000..3c7ec70 --- /dev/null +++ b/HiVakil.Common/Models/Exception/BaseApiException.cs @@ -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; } + } +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Exception/ValidationException.cs b/HiVakil.Common/Models/Exception/ValidationException.cs new file mode 100644 index 0000000..f8fd4b7 --- /dev/null +++ b/HiVakil.Common/Models/Exception/ValidationException.cs @@ -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 validationErrors) : base($"{string.Join(",", validationErrors.Select(v => v.ErrorMessage))}") + { + + } +} +public sealed record ValidationError(string PropertyName, string ErrorMessage); \ No newline at end of file diff --git a/HiVakil.Common/Models/IScopedDependency.cs b/HiVakil.Common/Models/IScopedDependency.cs new file mode 100644 index 0000000..319252d --- /dev/null +++ b/HiVakil.Common/Models/IScopedDependency.cs @@ -0,0 +1,6 @@ +namespace HiVakil.Common.Models +{ + public interface IScopedDependency + { + } +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Mapper/BaseDto.cs b/HiVakil.Common/Models/Mapper/BaseDto.cs new file mode 100644 index 0000000..534b81e --- /dev/null +++ b/HiVakil.Common/Models/Mapper/BaseDto.cs @@ -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 +{ + /// + /// Base Dto Class initial map config between entity and dto + /// + /// Type of Dto Class + /// Type of Entity Class + public abstract class BaseDto : INotifyPropertyChanged, IBaseDto where TEntity : class where TDto : class + { + public Guid Id { get; set; } + public DateTime CreatedAt { get; set; } + public static Expression> ProjectToDto + { + get => GetProjectToDto(); + } + private static Expression> GetProjectToDto() + { + var assembly = typeof(TEntity).Assembly; + var mapperName = $"{typeof(TEntity).Name}Mapper"; + var mapperType = assembly.GetTypes()?.FirstOrDefault(t => t.Name.Contains(mapperName)); + if (mapperType == null) + throw new AppException($"{typeof(TEntity).Name}Mapper Not Found!"); + if (typeof(TDto).Name.Contains("SDto")) + { + var projectProperty = mapperType.GetProperty("ProjectToSDto"); + if (projectProperty == null) + throw new AppException($"{typeof(TEntity).Name}Mapper Dont Have ProjectTo"); + return projectProperty.GetValue(null, null) as Expression>; + } + else if (typeof(TDto).Name.Contains("LDto")) + { + var projectProperty = mapperType.GetProperty("ProjectToLDto"); + if (projectProperty == null) + throw new AppException($"{typeof(TEntity).Name}Mapper Dont Have ProjectTo"); + return projectProperty.GetValue(null, null) as Expression>; + } + else + throw new AppException($"{typeof(TDto).Name} Projection Not Implemented"); + } + public virtual bool Compare(object obj) + { + if(obj is BaseDto objDto) + return objDto.Id == this.Id; + return Equals(obj); + } + + public TDto Clone() + { + return (TDto)MemberwiseClone(); + } + + public TEntity ToEntity() + { + var assembly = typeof(TEntity).Assembly; + var mapperName = $"{typeof(TEntity).Name}Mapper"; + var mapperType = assembly.GetTypes()?.FirstOrDefault(t => t.Name.Contains(mapperName)); + var toEntityMethodInfo = mapperType.GetMethod($"AdaptTo{typeof(TEntity).Name}"); + var parms = new[] { this }; + var entity = toEntityMethodInfo.Invoke(null, parms); + if (entity is TEntity o) + return o; + return null; + } + + public static TDto FromEntity(TEntity model) + { + var assembly = typeof(TEntity).Assembly; + var mapperName = $"{typeof(TEntity).Name}Mapper"; + var mapperType = assembly.GetTypes()?.FirstOrDefault(t => t.Name.Contains(mapperName)); + var toDtoMethodInfo = mapperType.GetMethod("AdaptToDto"); + var parms = new[] { model }; + var dto = toDtoMethodInfo.Invoke(null, parms); + if (dto is TDto o) + return o; + return null; + } + + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + protected bool SetField(ref T field, T value, [CallerMemberName] string propertyName = null) + { + if (EqualityComparer.Default.Equals(field, value)) return false; + field = value; + OnPropertyChanged(propertyName); + return true; + } + } +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Mapper/IBaseDto.cs b/HiVakil.Common/Models/Mapper/IBaseDto.cs new file mode 100644 index 0000000..26db83c --- /dev/null +++ b/HiVakil.Common/Models/Mapper/IBaseDto.cs @@ -0,0 +1,11 @@ +using System.Linq.Expressions; + +namespace HiVakil.Common.Models.Mapper +{ + public interface IBaseDto + { + Guid Id { get; set; } + bool Compare(object obj); + static Expression> ProjectToDto; + } +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Report/ChartUnit.cs b/HiVakil.Common/Models/Report/ChartUnit.cs new file mode 100644 index 0000000..2ea887c --- /dev/null +++ b/HiVakil.Common/Models/Report/ChartUnit.cs @@ -0,0 +1,38 @@ +namespace HiVakil.Common.Models.Report +{ + public class ChartUnit + { + public List Values { get; set; } = new(); + + public List ValuesStr + { + get { return Values.Select(v => v.ToString()).ToList(); } + } + + public List Labels { get; set; } = new(); + } + + public class ChartUnitIQuery + { + public IQueryable Values { get; set; } + + public List ValuesStr + { + get { return Values.Select(v => v.ToString()).ToList(); } + } + + public IQueryable Labels { get; set; } + } + + public class ChartUnit + { + public List Values { get; set; } = new(); + + public List ValuesStr + { + get { return Values.Select(v => v.ToString()).ToList(); } + } + + public List Labels { get; set; } = new(); + } +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Report/ReportRequest.cs b/HiVakil.Common/Models/Report/ReportRequest.cs new file mode 100644 index 0000000..d70a79d --- /dev/null +++ b/HiVakil.Common/Models/Report/ReportRequest.cs @@ -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 ReportRequestProps { get; set; } = new(); + } +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Report/ReportResult.cs b/HiVakil.Common/Models/Report/ReportResult.cs new file mode 100644 index 0000000..4ae0f3d --- /dev/null +++ b/HiVakil.Common/Models/Report/ReportResult.cs @@ -0,0 +1,34 @@ +namespace HiVakil.Common.Models.Report +{ + public class ReportResult + { + private List _rows; + + public List Rows + { + get + { + if (_rows == null) + _rows = new List(); + return _rows; + } + set => _rows = value; + } + } + + public class ReportRow + { + private List _cells; + + public List Cells + { + get + { + if (_cells == null) + _cells = new List(); + return _cells; + } + set => _cells = value; + } + } +} \ No newline at end of file diff --git a/HiVakil.Common/Models/Report/ReportableItem.cs b/HiVakil.Common/Models/Report/ReportableItem.cs new file mode 100644 index 0000000..c619411 --- /dev/null +++ b/HiVakil.Common/Models/Report/ReportableItem.cs @@ -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; } + } +} \ No newline at end of file diff --git a/HiVakil.Core/Abstracts/IPaymentService.cs b/HiVakil.Core/Abstracts/IPaymentService.cs new file mode 100644 index 0000000..605352c --- /dev/null +++ b/HiVakil.Core/Abstracts/IPaymentService.cs @@ -0,0 +1,7 @@ +namespace HiVakil.Core.Abstracts; + +public interface IPaymentService : IScopedDependency +{ + Task GetPaymentLinkAsync(double amount, string factorNumber, Guid orderId, Guid userId, string phoneNumber, string fullName, CancellationToken cancellationToken = default); + Task> VerifyPaymentAsync(string authority, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/HiVakil.Core/Abstracts/ISmsService.cs b/HiVakil.Core/Abstracts/ISmsService.cs new file mode 100644 index 0000000..ea6672c --- /dev/null +++ b/HiVakil.Core/Abstracts/ISmsService.cs @@ -0,0 +1,7 @@ +namespace HiVakil.Core.Abstracts; + +public interface ISmsService : IScopedDependency +{ + Task SendVerifyCodeAsync(string phoneNumber, string verifyCode); + Task SendForgerPasswordAsync(string phoneNumber, string newPassword); +} \ No newline at end of file diff --git a/HiVakil.Core/Abstracts/IStorageService.cs b/HiVakil.Core/Abstracts/IStorageService.cs new file mode 100644 index 0000000..12cbf17 --- /dev/null +++ b/HiVakil.Core/Abstracts/IStorageService.cs @@ -0,0 +1,9 @@ +namespace HiVakil.Core.Abstracts; + +public interface IStorageService : IScopedDependency +{ + Task UploadObjectFromFileAsync(string fileName, string filePath, string contentType, byte[] fileBytes); + Task UploadObjectFromFileAsync(string fileName, string filePath, string contentType, Stream fileStream, bool fixName = true); + + Task> GetStorageFiles(StorageFileType fileType); +} \ No newline at end of file diff --git a/HiVakil.Core/Abstracts/IUploadFileService.cs b/HiVakil.Core/Abstracts/IUploadFileService.cs new file mode 100644 index 0000000..c34def1 --- /dev/null +++ b/HiVakil.Core/Abstracts/IUploadFileService.cs @@ -0,0 +1,6 @@ +namespace HiVakil.Core.Abstracts; + +public interface IUploadFileService : IScopedDependency +{ + Task UploadImageAsync(FileUploadRequest uploadRequest); +} \ No newline at end of file diff --git a/HiVakil.Core/BaseServices/Abstracts/IJwtService.cs b/HiVakil.Core/BaseServices/Abstracts/IJwtService.cs new file mode 100644 index 0000000..d09dbf7 --- /dev/null +++ b/HiVakil.Core/BaseServices/Abstracts/IJwtService.cs @@ -0,0 +1,9 @@ +namespace HiVakil.Core.BaseServices.Abstracts; + +public interface IJwtService : IScopedDependency +{ + Task> Generate(TUser user) where TUser : ApplicationUser; + + Task> Generate(TUser user, List roleNames) where TUser : ApplicationUser; + Task> Generate(TUser user) where TUser : ApplicationUser; +} \ No newline at end of file diff --git a/HiVakil.Core/BaseServices/JwtService.cs b/HiVakil.Core/BaseServices/JwtService.cs new file mode 100644 index 0000000..316322e --- /dev/null +++ b/HiVakil.Core/BaseServices/JwtService.cs @@ -0,0 +1,130 @@ +namespace HiVakil.Core.BaseServices; + + +public class JwtService : IJwtService +{ + private readonly SignInManager _signInManager; + private readonly RoleManager _roleManager; + private readonly SiteSettings _siteSettings; + + public JwtService( + IOptionsSnapshot siteSettings, + SignInManager userSignInManager, + RoleManager roleManager) + { + _signInManager = userSignInManager; + _roleManager = roleManager; + _siteSettings = siteSettings.Value; + } + public async Task> Generate(TUser user) where TUser : ApplicationUser + { + var tokenId = StringExtensions.GetId(8); + var claims = await GetClaims(user, tokenId); + return BaseGenerate(user, claims); + + } + + public async Task> Generate(TUser user, List roleNames) where TUser : ApplicationUser + { + var tokenId = StringExtensions.GetId(8); + var claims = await GetClaims(user, tokenId, roleNames.ToArray()); + + var token = BaseGenerate(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> Generate(TUser user) where TUser : ApplicationUser + { + var tokenId = StringExtensions.GetId(8); + var claims = await GetClaims(user, tokenId); + return BaseGenerate(user, claims); + } + + + + + private AccessToken BaseGenerate(TUser user, List 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(handler.CreateJwtSecurityToken(desctiptor)); + token.User = user; + return token; + } + private AccessToken BaseGenerate(TUser user, List 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(handler.CreateJwtSecurityToken(desctiptor)); + token.User = user.Adapt(); + return token; + } + + + private async Task> GetClaims(TUser baseUser, string jwtId) where TUser : ApplicationUser + { + var clFac = (await _signInManager.ClaimsFactory.CreateAsync(baseUser)); + var claims = new List(); + 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> GetClaims(TUser baseUser, string jwtId, params string[] roleNames) where TUser : ApplicationUser + { + var claims = new List(); + + 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; + + } + +} \ No newline at end of file diff --git a/HiVakil.Core/CoreConfig.cs b/HiVakil.Core/CoreConfig.cs new file mode 100644 index 0000000..2792901 --- /dev/null +++ b/HiVakil.Core/CoreConfig.cs @@ -0,0 +1,7 @@ +namespace HiVakil.Core +{ + public class CoreConfig + { + + } +} diff --git a/HiVakil.Core/CoreServices/Abstracts/IAccountService.cs b/HiVakil.Core/CoreServices/Abstracts/IAccountService.cs new file mode 100644 index 0000000..2717c0e --- /dev/null +++ b/HiVakil.Core/CoreServices/Abstracts/IAccountService.cs @@ -0,0 +1,11 @@ +namespace HiVakil.Core.CoreServices.Abstracts; + +public interface IAccountService : IScopedDependency +{ + public Task> LoginWithPasswordAsync(string userName, string password, CancellationToken cancellationToken); + public Task> LoginWithVerifyCodeAsync(string userName, string verifyCode, CancellationToken cancellationToken); + public Task GetVerifyCodeAsync(string phoneNumber); + public Task ForgetPasswordAsync(string phoneNumber); + public Task CheckMemberShipAsync(string phoneNumber); + public Task> CompleteSignUpAsync(SignUpRequestDto requestDto, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/HiVakil.Core/CoreServices/AccountService.cs b/HiVakil.Core/CoreServices/AccountService.cs new file mode 100644 index 0000000..6b1b1fd --- /dev/null +++ b/HiVakil.Core/CoreServices/AccountService.cs @@ -0,0 +1,156 @@ +namespace HiVakil.Core.CoreServices; + + +public class AccountService : IAccountService +{ + + private readonly UserManager _userManager; + private readonly SignInManager _userSignInManager; + private readonly IJwtService _jwtService; + private readonly ICurrentUserService _currentUserService; + private readonly IRepositoryWrapper _repositoryWrapper; + private readonly ISmsService _smsService; + private readonly IUserService _userService; + + public AccountService( + UserManager userManager, + SignInManager 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 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 CheckMemberShipAsync(string phoneNumber) + { + var user = await _userManager.FindByNameAsync(phoneNumber); + if (user == null) + return false; + return true; + } + + public async Task 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> 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> 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> 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> CompleteLogin(ApplicationUser user, CancellationToken cancellationToken) + { + AccessToken jwt; + var role = await _userManager.GetRolesAsync(user); + jwt = await _jwtService.Generate(user, role.ToList()); + jwt.User.RoleName = jwt.RoleName; + return jwt; + } + + + +} \ No newline at end of file diff --git a/HiVakil.Core/EntityServices/Abstracts/IUserService.cs b/HiVakil.Core/EntityServices/Abstracts/IUserService.cs new file mode 100644 index 0000000..6d93d42 --- /dev/null +++ b/HiVakil.Core/EntityServices/Abstracts/IUserService.cs @@ -0,0 +1,22 @@ +namespace HiVakil.Core.EntityServices.Abstracts; + +public interface IUserService : IScopedDependency +{ + Task GetUserProfileAsync(CancellationToken cancellationToken); + Task> GetUsersAsync(int page = 0,string? phoneNumber = null, CancellationToken cancellationToken = default); + Task GetUserAsync(Guid userId, CancellationToken cancellationToken = default); + Task CreateUserAsync(string phoneNumber, CancellationToken cancellationToken = default); + Task CreateUserAsync(UserActionRequestDto request, CancellationToken cancellationToken); + Task EditUserAsync(UserActionRequestDto request, CancellationToken cancellationToken); + Task EditUserProfileAsync(UserActionRequestDto request, CancellationToken cancellationToken); + Task RemoveUserAsync(Guid userId, CancellationToken cancellationToken); + + + Task> GetRolesAsync(int page = 0, CancellationToken cancellationToken = default); + Task> GetRolesAsync(int? page,string? roleName, CancellationToken cancellationToken = default); + Task GetRoleAsync(Guid roleId, CancellationToken cancellationToken = default); + Task CreateRoleAsync(RoleActionRequestDto request, CancellationToken cancellationToken = default); + Task EditRoleAsync(RoleActionRequestDto request, CancellationToken cancellationToken = default); + Task RemoveRoleAsync(Guid roleId, CancellationToken cancellationToken = default); + List GetPermissions(); +} \ No newline at end of file diff --git a/HiVakil.Core/EntityServices/UserService.cs b/HiVakil.Core/EntityServices/UserService.cs new file mode 100644 index 0000000..a4674e7 --- /dev/null +++ b/HiVakil.Core/EntityServices/UserService.cs @@ -0,0 +1,366 @@ +namespace HiVakil.Core.EntityServices; + + +public class UserService : IUserService +{ + private readonly ICurrentUserService _currentUserService; + private readonly UserManager _userManager; + private readonly RoleManager _roleManager; + + public UserService(ICurrentUserService currentUserService, + UserManager userManager, + RoleManager roleManager) + { + _currentUserService = currentUserService; + _userManager = userManager; + _roleManager = roleManager; + } + + + public async Task 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> GetUsersAsync(int page = 0, string? phoneNumber = null, CancellationToken cancellationToken = default) + { + List 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 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 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 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 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 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 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> 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> GetRolesAsync(int? page, string? roleName, CancellationToken cancellationToken = default) + { + IQueryable 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 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(); + 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 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 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 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 GetPermissions() + { + return ApplicationClaims.AllClaimDtos; + } +} \ No newline at end of file diff --git a/HiVakil.Core/HiVakil.Core.csproj b/HiVakil.Core/HiVakil.Core.csproj new file mode 100644 index 0000000..288c39d --- /dev/null +++ b/HiVakil.Core/HiVakil.Core.csproj @@ -0,0 +1,59 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HiVakil.Core/Models/Api/ApiResult.cs b/HiVakil.Core/Models/Api/ApiResult.cs new file mode 100644 index 0000000..0801b70 --- /dev/null +++ b/HiVakil.Core/Models/Api/ApiResult.cs @@ -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 : 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 data) + { + return new ApiResult(true, ApiResultStatusCode.Success, data); + } + + public static implicit operator ApiResult(OkResult result) + { + return new ApiResult(true, ApiResultStatusCode.Success, null); + } + + public static implicit operator ApiResult(OkObjectResult result) + { + return new ApiResult(true, ApiResultStatusCode.Success, (TData)result.Value); + } + + public static implicit operator ApiResult(BadRequestResult result) + { + return new ApiResult(false, ApiResultStatusCode.BadRequest, null); + } + + public static implicit operator ApiResult(BadRequestObjectResult result) + { + var message = result.Value.ToString(); + if (result.Value is SerializableError errors) + { + var errorMessages = errors.SelectMany(p => (string[])p.Value).Distinct(); + message = string.Join(" | ", errorMessages); + } + + return new ApiResult(false, ApiResultStatusCode.BadRequest, null, message); + } + + public static implicit operator ApiResult(ContentResult result) + { + return new ApiResult(true, ApiResultStatusCode.Success, null, result.Content); + } + + public static implicit operator ApiResult(NotFoundResult result) + { + return new ApiResult(false, ApiResultStatusCode.NotFound, null); + } + + public static implicit operator ApiResult(NotFoundObjectResult result) + { + return new ApiResult(false, ApiResultStatusCode.NotFound, (TData)result.Value); + } + + #endregion +} \ No newline at end of file diff --git a/HiVakil.Core/Utilities/ImageConvertor.cs b/HiVakil.Core/Utilities/ImageConvertor.cs new file mode 100644 index 0000000..49aa87c --- /dev/null +++ b/HiVakil.Core/Utilities/ImageConvertor.cs @@ -0,0 +1,18 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Processing; + +namespace HiVakil.Core.Utilities; + +public static class ImageConvertor +{ + public static async Task 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; + } +} \ No newline at end of file diff --git a/HiVakil.Domain/DomainConfig.cs b/HiVakil.Domain/DomainConfig.cs new file mode 100644 index 0000000..cba1d40 --- /dev/null +++ b/HiVakil.Domain/DomainConfig.cs @@ -0,0 +1,7 @@ +namespace HiVakil.Domain +{ + public class DomainConfig + { + + } +} diff --git a/HiVakil.Domain/Dtos/RequestDto/RoleActionRequestDto.cs b/HiVakil.Domain/Dtos/RequestDto/RoleActionRequestDto.cs new file mode 100644 index 0000000..a809ea1 --- /dev/null +++ b/HiVakil.Domain/Dtos/RequestDto/RoleActionRequestDto.cs @@ -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 Permissions { get; set; } = new(); +} \ No newline at end of file diff --git a/HiVakil.Domain/Dtos/RequestDto/SignUpRequestDto.cs b/HiVakil.Domain/Dtos/RequestDto/SignUpRequestDto.cs new file mode 100644 index 0000000..02dd4de --- /dev/null +++ b/HiVakil.Domain/Dtos/RequestDto/SignUpRequestDto.cs @@ -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; +} \ No newline at end of file diff --git a/HiVakil.Domain/Dtos/RequestDto/UserActionRequestDto.cs b/HiVakil.Domain/Dtos/RequestDto/UserActionRequestDto.cs new file mode 100644 index 0000000..a9841e0 --- /dev/null +++ b/HiVakil.Domain/Dtos/RequestDto/UserActionRequestDto.cs @@ -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 RoleIds { get; set; } = new(); +} \ No newline at end of file diff --git a/HiVakil.Domain/Dtos/ResponseDto/ProfileResponseDto.cs b/HiVakil.Domain/Dtos/ResponseDto/ProfileResponseDto.cs new file mode 100644 index 0000000..460bc54 --- /dev/null +++ b/HiVakil.Domain/Dtos/ResponseDto/ProfileResponseDto.cs @@ -0,0 +1,8 @@ +namespace HiVakil.Domain.Dtos.ResponseDto; + +public class ProfileResponseDto +{ + public List Roles { get; set; } = new(); + public ApplicationUserSDto? User { get; set; } + public List Permissions { get; set; } = new(); +} \ No newline at end of file diff --git a/HiVakil.Domain/Dtos/ResponseDto/VerifyCodeResponseDto.cs b/HiVakil.Domain/Dtos/ResponseDto/VerifyCodeResponseDto.cs new file mode 100644 index 0000000..8cd1f55 --- /dev/null +++ b/HiVakil.Domain/Dtos/ResponseDto/VerifyCodeResponseDto.cs @@ -0,0 +1,7 @@ +namespace HiVakil.Domain.Dtos.ResponseDto; + +public class VerifyCodeResponseDto +{ + public SignUpStatus SignUpStatus { get; set; } + +} \ No newline at end of file diff --git a/HiVakil.Domain/Dtos/SmallDtos/ApplicationUserSDto.cs b/HiVakil.Domain/Dtos/SmallDtos/ApplicationUserSDto.cs new file mode 100644 index 0000000..4b2436e --- /dev/null +++ b/HiVakil.Domain/Dtos/SmallDtos/ApplicationUserSDto.cs @@ -0,0 +1,19 @@ +namespace HiVakil.Domain.Dtos.SmallDtos; + +public class ApplicationUserSDto : BaseDto +{ + + 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 RoleIds { get; set; } = new(); + public long BirthDateTimeStamp => DateTimeExtensions.DateTimeToUnixTimeStamp(BirthDate); +} \ No newline at end of file diff --git a/HiVakil.Domain/Dtos/SmallDtos/StorageFileSDto.cs b/HiVakil.Domain/Dtos/SmallDtos/StorageFileSDto.cs new file mode 100644 index 0000000..e6d5454 --- /dev/null +++ b/HiVakil.Domain/Dtos/SmallDtos/StorageFileSDto.cs @@ -0,0 +1,24 @@ +namespace HiVakil.Domain.Dtos.SmallDtos; + +public class StorageFileSDto : BaseDto +{ + 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; + } + } + +} \ No newline at end of file diff --git a/HiVakil.Domain/Entities/StorageFiles/StorageFile.cs b/HiVakil.Domain/Entities/StorageFiles/StorageFile.cs new file mode 100644 index 0000000..9e7493a --- /dev/null +++ b/HiVakil.Domain/Entities/StorageFiles/StorageFile.cs @@ -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; } +} \ No newline at end of file diff --git a/HiVakil.Domain/Entities/Users/ApplicationRole.cs b/HiVakil.Domain/Entities/Users/ApplicationRole.cs new file mode 100644 index 0000000..3faeeb6 --- /dev/null +++ b/HiVakil.Domain/Entities/Users/ApplicationRole.cs @@ -0,0 +1,8 @@ +namespace HiVakil.Domain.Entities.Users; + +public class ApplicationRole : IdentityRole +{ + public string Description { get; set; } = string.Empty; + public string EnglishName { get; set; } = string.Empty; + public string PersianName { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/HiVakil.Domain/Entities/Users/ApplicationUser.cs b/HiVakil.Domain/Entities/Users/ApplicationUser.cs new file mode 100644 index 0000000..87441fc --- /dev/null +++ b/HiVakil.Domain/Entities/Users/ApplicationUser.cs @@ -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 +{ + 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; } +} diff --git a/HiVakil.Domain/Enums/Gender.cs b/HiVakil.Domain/Enums/Gender.cs new file mode 100644 index 0000000..0760749 --- /dev/null +++ b/HiVakil.Domain/Enums/Gender.cs @@ -0,0 +1,9 @@ +namespace HiVakil.Domain.Enums; + +public enum Gender +{ + [Display(Name = "مرد")] + Male, + [Display(Name = "بانو")] + Female +} \ No newline at end of file diff --git a/HiVakil.Domain/Enums/SignUpStatus.cs b/HiVakil.Domain/Enums/SignUpStatus.cs new file mode 100644 index 0000000..19bb2ec --- /dev/null +++ b/HiVakil.Domain/Enums/SignUpStatus.cs @@ -0,0 +1,8 @@ +namespace HiVakil.Domain.Enums; + +public enum SignUpStatus +{ + StartSignOn = 0, + PhoneNumberVerified = 1, + SignUpCompleted = 10, +} \ No newline at end of file diff --git a/HiVakil.Domain/Enums/StorageFileType.cs b/HiVakil.Domain/Enums/StorageFileType.cs new file mode 100644 index 0000000..3201c34 --- /dev/null +++ b/HiVakil.Domain/Enums/StorageFileType.cs @@ -0,0 +1,9 @@ +namespace HiVakil.Domain.Enums; + +public enum StorageFileType +{ + [Display(Name = "Images")] + Image, + [Display(Name = "Videos")] + Video +} \ No newline at end of file diff --git a/HiVakil.Domain/FodyWeavers.xml b/HiVakil.Domain/FodyWeavers.xml new file mode 100644 index 0000000..d5abfed --- /dev/null +++ b/HiVakil.Domain/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/HiVakil.Domain/HiVakil.Domain.csproj b/HiVakil.Domain/HiVakil.Domain.csproj new file mode 100644 index 0000000..5da02f7 --- /dev/null +++ b/HiVakil.Domain/HiVakil.Domain.csproj @@ -0,0 +1,78 @@ + + + + + + net5.0 + 10 + enable + enable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HiVakil.Domain/Mappers/ApplicationUserMapper.g.cs b/HiVakil.Domain/Mappers/ApplicationUserMapper.g.cs new file mode 100644 index 0000000..64a9ed8 --- /dev/null +++ b/HiVakil.Domain/Mappers/ApplicationUserMapper.g.cs @@ -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> 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 + }; + } +} \ No newline at end of file diff --git a/HiVakil.Domain/Mappers/StorageFileMapper.g.cs b/HiVakil.Domain/Mappers/StorageFileMapper.g.cs new file mode 100644 index 0000000..e4446f2 --- /dev/null +++ b/HiVakil.Domain/Mappers/StorageFileMapper.g.cs @@ -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> 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 + }; + } +} \ No newline at end of file diff --git a/HiVakil.Domain/Models/Claims/ApplicationClaims.cs b/HiVakil.Domain/Models/Claims/ApplicationClaims.cs new file mode 100644 index 0000000..89808a4 --- /dev/null +++ b/HiVakil.Domain/Models/Claims/ApplicationClaims.cs @@ -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 AllClaimDtos = new List + { + 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 AllClaims = new List + { + 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 CustomerClaims = new List + { + ViewBlogs.GetClaim, + ViewBrands.GetClaim, + ViewCategories.GetClaim, + ViewMineOrders.GetClaim, + CreateOrder.GetClaim, + ViewProducts.GetClaim, + AddReview.GetClaim, + ViewMineReviews.GetClaim, + }; + +} diff --git a/HiVakil.Domain/Models/Claims/ApplicationPermission.cs b/HiVakil.Domain/Models/Claims/ApplicationPermission.cs new file mode 100644 index 0000000..1e35888 --- /dev/null +++ b/HiVakil.Domain/Models/Claims/ApplicationPermission.cs @@ -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); +} \ No newline at end of file diff --git a/HiVakil.Domain/Models/Claims/ClaimDto.cs b/HiVakil.Domain/Models/Claims/ClaimDto.cs new file mode 100644 index 0000000..63863b1 --- /dev/null +++ b/HiVakil.Domain/Models/Claims/ClaimDto.cs @@ -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); +} diff --git a/HiVakil.Domain/Models/Claims/CustomClaimType.cs b/HiVakil.Domain/Models/Claims/CustomClaimType.cs new file mode 100644 index 0000000..bd18fde --- /dev/null +++ b/HiVakil.Domain/Models/Claims/CustomClaimType.cs @@ -0,0 +1,7 @@ +namespace HiVakil.Domain.Models.Claims +{ + public static class CustomClaimType + { + public static string Permission { get; } = "Permission"; + } +} \ No newline at end of file diff --git a/HiVakil.Domain/Models/Settings/SiteSettings.cs b/HiVakil.Domain/Models/Settings/SiteSettings.cs new file mode 100644 index 0000000..1da9711 --- /dev/null +++ b/HiVakil.Domain/Models/Settings/SiteSettings.cs @@ -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; +} \ No newline at end of file diff --git a/HiVakil.Infrastructure/HiVakil.Infrastructure.csproj b/HiVakil.Infrastructure/HiVakil.Infrastructure.csproj new file mode 100644 index 0000000..49be90b --- /dev/null +++ b/HiVakil.Infrastructure/HiVakil.Infrastructure.csproj @@ -0,0 +1,45 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HiVakil.Infrastructure/InfrastructureConfig.cs b/HiVakil.Infrastructure/InfrastructureConfig.cs new file mode 100644 index 0000000..6730082 --- /dev/null +++ b/HiVakil.Infrastructure/InfrastructureConfig.cs @@ -0,0 +1,7 @@ +namespace HiVakil.Infrastructure +{ + public class InfrastructureConfig + { + + } +} diff --git a/HiVakil.Infrastructure/Models/DirectoryAddress.cs b/HiVakil.Infrastructure/Models/DirectoryAddress.cs new file mode 100644 index 0000000..e6f0bb5 --- /dev/null +++ b/HiVakil.Infrastructure/Models/DirectoryAddress.cs @@ -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"; +} \ No newline at end of file diff --git a/HiVakil.Infrastructure/Models/RestAddress.cs b/HiVakil.Infrastructure/Models/RestAddress.cs new file mode 100644 index 0000000..e0e846d --- /dev/null +++ b/HiVakil.Infrastructure/Models/RestAddress.cs @@ -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"; +} \ No newline at end of file diff --git a/HiVakil.Infrastructure/Models/RestApi/KaveNegar/KaveNegarResponse.cs b/HiVakil.Infrastructure/Models/RestApi/KaveNegar/KaveNegarResponse.cs new file mode 100644 index 0000000..8e1ca15 --- /dev/null +++ b/HiVakil.Infrastructure/Models/RestApi/KaveNegar/KaveNegarResponse.cs @@ -0,0 +1,7 @@ +namespace HiVakil.Infrastructure.Models.RestApi.KaveNegar; + +public class KaveNegarResponse +{ + public KaveNegarReturn Return { get; set; } + public KaveNegarResponseEntry[] entries { get; set; } +} \ No newline at end of file diff --git a/HiVakil.Infrastructure/Models/RestApi/KaveNegar/KaveNegarResponseEntry.cs b/HiVakil.Infrastructure/Models/RestApi/KaveNegar/KaveNegarResponseEntry.cs new file mode 100644 index 0000000..a6bbfc4 --- /dev/null +++ b/HiVakil.Infrastructure/Models/RestApi/KaveNegar/KaveNegarResponseEntry.cs @@ -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; } +} \ No newline at end of file diff --git a/HiVakil.Infrastructure/Models/RestApi/KaveNegar/KaveNegarReturn.cs b/HiVakil.Infrastructure/Models/RestApi/KaveNegar/KaveNegarReturn.cs new file mode 100644 index 0000000..6a1d71e --- /dev/null +++ b/HiVakil.Infrastructure/Models/RestApi/KaveNegar/KaveNegarReturn.cs @@ -0,0 +1,7 @@ +namespace HiVakil.Infrastructure.Models.RestApi.KaveNegar; + +public class KaveNegarReturn +{ + public int status { get; set; } + public string message { get; set; } +} \ No newline at end of file diff --git a/HiVakil.Infrastructure/Models/RestApi/Zarinpal/ZarinaplPaymentLinkRequest.cs b/HiVakil.Infrastructure/Models/RestApi/Zarinpal/ZarinaplPaymentLinkRequest.cs new file mode 100644 index 0000000..69f16b9 --- /dev/null +++ b/HiVakil.Infrastructure/Models/RestApi/Zarinpal/ZarinaplPaymentLinkRequest.cs @@ -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; +} \ No newline at end of file diff --git a/HiVakil.Infrastructure/Models/RestApi/Zarinpal/ZarinaplPaymentLinkResponse.cs b/HiVakil.Infrastructure/Models/RestApi/Zarinpal/ZarinaplPaymentLinkResponse.cs new file mode 100644 index 0000000..6195cfb --- /dev/null +++ b/HiVakil.Infrastructure/Models/RestApi/Zarinpal/ZarinaplPaymentLinkResponse.cs @@ -0,0 +1,16 @@ +namespace HiVakil.Infrastructure.Models.RestApi.Zarinpal; + +public class ZarinaplPaymentLinkResponse +{ + public ZarinaplPaymentLinkResponseData data { get; set; } + public List 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; } +} \ No newline at end of file diff --git a/HiVakil.Infrastructure/Models/RestApi/Zarinpal/ZarinaplPaymentVerifyResponse.cs b/HiVakil.Infrastructure/Models/RestApi/Zarinpal/ZarinaplPaymentVerifyResponse.cs new file mode 100644 index 0000000..f9fa091 --- /dev/null +++ b/HiVakil.Infrastructure/Models/RestApi/Zarinpal/ZarinaplPaymentVerifyResponse.cs @@ -0,0 +1,20 @@ +namespace HiVakil.Infrastructure.Models.RestApi.Zarinpal; + +public class ZarinaplPaymentVerifyResponse +{ + + public ZarinaplPaymentVerifyResponseData data { get; set; } + public List 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; } +} \ No newline at end of file diff --git a/HiVakil.Infrastructure/Models/RestApi/Zarinpal/ZarinaplVerifyPaymentRequest.cs b/HiVakil.Infrastructure/Models/RestApi/Zarinpal/ZarinaplVerifyPaymentRequest.cs new file mode 100644 index 0000000..02b51d2 --- /dev/null +++ b/HiVakil.Infrastructure/Models/RestApi/Zarinpal/ZarinaplVerifyPaymentRequest.cs @@ -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; +} \ No newline at end of file diff --git a/HiVakil.Infrastructure/RestServices/IKaveNegarRestApi.cs b/HiVakil.Infrastructure/RestServices/IKaveNegarRestApi.cs new file mode 100644 index 0000000..b5959ed --- /dev/null +++ b/HiVakil.Infrastructure/RestServices/IKaveNegarRestApi.cs @@ -0,0 +1,10 @@ +namespace HiVakil.Infrastructure.RestServices; + +public interface IKaveNegarRestApi +{ + + [Post("/{apiKey}/verify/lookup.json")] + Task 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 SendSms(string apiKey, [Query] string receptor, [Query] string message, [Query] string sender); +} \ No newline at end of file diff --git a/HiVakil.Infrastructure/RestServices/IRestApiWrapper.cs b/HiVakil.Infrastructure/RestServices/IRestApiWrapper.cs new file mode 100644 index 0000000..d7aaf69 --- /dev/null +++ b/HiVakil.Infrastructure/RestServices/IRestApiWrapper.cs @@ -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(RestAddress.BaseKaveNegar); + public IZarinpalRestApi ZarinpalRestApi => RestService.For(RestAddress.BaseZarinpal); + +} \ No newline at end of file diff --git a/HiVakil.Infrastructure/RestServices/IZarinpalRestApi.cs b/HiVakil.Infrastructure/RestServices/IZarinpalRestApi.cs new file mode 100644 index 0000000..a5f4ff7 --- /dev/null +++ b/HiVakil.Infrastructure/RestServices/IZarinpalRestApi.cs @@ -0,0 +1,12 @@ +using HiVakil.Infrastructure.Models.RestApi.Zarinpal; + +namespace HiVakil.Infrastructure.RestServices; + +public interface IZarinpalRestApi +{ + + [Post("/v4/payment/request.json")] + Task GetPaymentLinkAsync([Body] ZarinaplPaymentLinkRequest request); + [Post("/v4/payment/verify.json")] + Task VerifyPaymentAsync([Body] ZarinaplVerifyPaymentRequest request); +} \ No newline at end of file diff --git a/HiVakil.Infrastructure/Services/SmsService.cs b/HiVakil.Infrastructure/Services/SmsService.cs new file mode 100644 index 0000000..aa3330c --- /dev/null +++ b/HiVakil.Infrastructure/Services/SmsService.cs @@ -0,0 +1,55 @@ +namespace HiVakil.Infrastructure.Services; + +public class SmsService : ISmsService +{ + private readonly IRestApiWrapper _restApiWrapper; + private readonly ILogger _logger; + private readonly IHostEnvironment _environment; + private readonly SiteSettings _siteSettings; + public SmsService( + IRestApiWrapper restApiWrapper, + IOptionsSnapshot optionsSnapshot, + ILogger 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); + } + } + +} \ No newline at end of file diff --git a/HiVakil.Infrastructure/Services/StorageService.cs b/HiVakil.Infrastructure/Services/StorageService.cs new file mode 100644 index 0000000..2e78c60 --- /dev/null +++ b/HiVakil.Infrastructure/Services/StorageService.cs @@ -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 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 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 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> 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(); + 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(); + } +} \ No newline at end of file diff --git a/HiVakil.Infrastructure/Services/UploadFileService.cs b/HiVakil.Infrastructure/Services/UploadFileService.cs new file mode 100644 index 0000000..ea078e0 --- /dev/null +++ b/HiVakil.Infrastructure/Services/UploadFileService.cs @@ -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 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; + } +} \ No newline at end of file diff --git a/HiVakil.Infrastructure/Services/ZarinpalService.cs b/HiVakil.Infrastructure/Services/ZarinpalService.cs new file mode 100644 index 0000000..e4a4f81 --- /dev/null +++ b/HiVakil.Infrastructure/Services/ZarinpalService.cs @@ -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 snapshot, + IMediator mediator) + { + _restApiWrapper = restApiWrapper; + _mediator = mediator; + _siteSettings = snapshot.Value; + } + public async Task 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> VerifyPaymentAsync(string authority, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/HiVakil.Repository/Abstracts/ICurrentUserService.cs b/HiVakil.Repository/Abstracts/ICurrentUserService.cs new file mode 100644 index 0000000..1f4f578 --- /dev/null +++ b/HiVakil.Repository/Abstracts/ICurrentUserService.cs @@ -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? Permissions { get; } +} \ No newline at end of file diff --git a/HiVakil.Repository/Behaviors/ValidationBehavior.cs b/HiVakil.Repository/Behaviors/ValidationBehavior.cs new file mode 100644 index 0000000..fab33f9 --- /dev/null +++ b/HiVakil.Repository/Behaviors/ValidationBehavior.cs @@ -0,0 +1,33 @@ +using ValidationException = HiVakil.Common.Models.Exception.ValidationException; + +namespace HiVakil.Repository.Behaviors; + +public class ValidationBehavior : IPipelineBehavior where TRequest : notnull +{ + private readonly IEnumerable> _validators; + + public ValidationBehavior(IEnumerable> validators) + { + _validators = validators; + } + public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) + { + var context = new ValidationContext(request); + List errors = new List(); + foreach (IValidator 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; + } +} \ No newline at end of file diff --git a/HiVakil.Repository/Extensions/DbContextOptionCustomExtensionsInfo.cs b/HiVakil.Repository/Extensions/DbContextOptionCustomExtensionsInfo.cs new file mode 100644 index 0000000..4d5e40d --- /dev/null +++ b/HiVakil.Repository/Extensions/DbContextOptionCustomExtensionsInfo.cs @@ -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 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; + } +} \ No newline at end of file diff --git a/HiVakil.Repository/Extensions/ModelBuilderExtensions.cs b/HiVakil.Repository/Extensions/ModelBuilderExtensions.cs new file mode 100644 index 0000000..127f67f --- /dev/null +++ b/HiVakil.Repository/Extensions/ModelBuilderExtensions.cs @@ -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(ModelBuilder modelBuilder) where TQFilter : ApiEntity + { + var tt = typeof(TQFilter); + if (tt.BaseType == typeof(ApiEntity)) + modelBuilder.Entity().HasQueryFilter(e => e.IsRemoved == false); + } +} + +public static class ModelBuilderExtensions +{ + /// + /// Singularizin table name like Posts to Post or People to Person + /// + /// + 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)); + } + } + + + /// + /// Set NEWSEQUENTIALID() sql function for all columns named "Id" + /// + /// + /// Set to true if you want only "Identity" guid fields that named "Id" + 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()"); + } + } + + /// + /// Set DefaultValueSql for sepecific property name and type + /// + /// + /// Name of property wants to set DefaultValueSql for + /// Type of property wants to set DefaultValueSql for + /// DefaultValueSql like "NEWSEQUENTIALID()" + 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); + } + } + + /// + /// Set DeleteBehavior.Restrict by default for relations + /// + /// + 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; + } + } + + /// + /// Dynamicaly load all IEntityTypeConfiguration with Reflection + /// + /// + /// Assemblies contains Entities + 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) }); + } + } + + + /// + /// Pluralizing table name like Post to Posts or Person to People + /// + /// + 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)); + } + } + + /// + /// Dynamicaly register all Entities that inherit from specific BaseType + /// + /// + /// Base type that Entities inherit from this + /// Assemblies contains Entities + public static void RegisterAllEntities(this ModelBuilder modelBuilder, + ILogger _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); + } + } + + /// + /// Dynamicaly register all Entities that inherit from specific BaseType + /// + /// + /// Base type that Entities inherit from this + /// Assemblies contains Entities + public static void RegisterAllEntitiesV02(this ModelBuilder builder, ILogger _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"); + } + } +} \ No newline at end of file diff --git a/HiVakil.Repository/HiVakil.Repository.csproj b/HiVakil.Repository/HiVakil.Repository.csproj new file mode 100644 index 0000000..17f0500 --- /dev/null +++ b/HiVakil.Repository/HiVakil.Repository.csproj @@ -0,0 +1,68 @@ + + + + net8.0 + enable + enable + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HiVakil.Repository/Models/ApplicationContext.cs b/HiVakil.Repository/Models/ApplicationContext.cs new file mode 100644 index 0000000..4c82549 --- /dev/null +++ b/HiVakil.Repository/Models/ApplicationContext.cs @@ -0,0 +1,47 @@ +namespace HiVakil.Repository.Models; + + +public class ApplicationContext : IdentityDbContext +{ + private readonly ILogger _logger; + private readonly Assembly _projectAssembly; + + public ApplicationContext(DbContextOptions options, ILogger logger) : base(options) + { + _logger = logger; + _projectAssembly = options.GetExtension().ProjectAssembly; + } + + + protected override void OnModelCreating(ModelBuilder builder) + { + var stopwatch = new Stopwatch(); + stopwatch.Start(); + base.OnModelCreating(builder); + var entitiesAssembly = _projectAssembly; + builder.RegisterAllEntities(_logger, entitiesAssembly); + stopwatch.Stop(); + _logger.LogInformation($"!!!!!!! RegisterAllEntities : {stopwatch.ElapsedMilliseconds}ms !!!!!!!"); + + + RenameIdentityTables(builder); + builder.RegisterEntityTypeConfiguration(entitiesAssembly); + builder.AddPluralizingTableNameConvention(); + builder.AddRestrictDeleteBehaviorConvention(); + //builder.AddSequentialGuidForIdConvention(); + } + + protected void RenameIdentityTables(ModelBuilder builder) + { + builder.HasDefaultSchema("public"); + + builder.Entity().ToTable("Users"); + builder.Entity().ToTable("Roles"); + builder.Entity>().ToTable("RoleClaims"); + builder.Entity>().ToTable("UserRoles"); + builder.Entity>().ToTable("Claims"); + builder.Entity>().ToTable("Logins"); + builder.Entity>().ToTable("Tokens"); + + } +} \ No newline at end of file diff --git a/HiVakil.Repository/Repositories/Base/BaseRepository.cs b/HiVakil.Repository/Repositories/Base/BaseRepository.cs new file mode 100644 index 0000000..d67d005 --- /dev/null +++ b/HiVakil.Repository/Repositories/Base/BaseRepository.cs @@ -0,0 +1,115 @@ +namespace HiVakil.Repository.Repositories.Base +{ + public class BaseRepository : Repository, IBaseRepository where T : class, IApiEntity + { + private readonly ICurrentUserService _currentUserService; + + public BaseRepository(ApplicationContext dbContext, ICurrentUserService currentUserService) : base(dbContext) + { + _currentUserService = currentUserService; + } + + public virtual async ValueTask GetByIdAsync(CancellationToken cancellationToken, params object[] ids) + { + var founded = await Entities.FindAsync(ids, cancellationToken); + if (founded == null) + throw new AppException($"{nameof(T)} Not Found", ApiResultStatusCode.NotFound); + return founded; + } + + #region Sync Methods + + public virtual T GetById(params object[] ids) + { + var ent = Entities.Find(ids); + if (ent == null) + throw new AppException($"{nameof(T)} Not Found", ApiResultStatusCode.NotFound); + Detach(ent); + return ent; + } + + public virtual void Add(T entity) + { + AssertExtensions.NotNull(entity, nameof(entity)); + Entities.Add(entity); + } + + public virtual void AddRange(IEnumerable entities) + { + var apiEntities = entities as T[] ?? entities.ToArray(); + AssertExtensions.NotNull(apiEntities, nameof(entities)); + Entities.AddRange(apiEntities); + } + + public virtual void Update(T entity) + { + AssertExtensions.NotNull(entity, nameof(entity)); + Detach(entity); + Entities.Update(entity); + } + + public virtual void UpdateRange(IEnumerable entities) + { + var apiEntities = entities as T[] ?? entities.ToArray(); + AssertExtensions.NotNull(apiEntities, nameof(entities)); + Entities.UpdateRange(apiEntities); + } + + public void HardDelete(T entity) + { + AssertExtensions.NotNull(entity, nameof(entity)); + Entities.Remove(entity); + } + + public virtual void Delete(T entity) + { + AssertExtensions.NotNull(entity, nameof(entity)); + + Entities.Entry(entity).Property(e => e.RemovedAt) + .CurrentValue = DateTime.Now; + Entities.Entry(entity).Property(e => e.IsRemoved) + .CurrentValue = true; + if (_currentUserService.UserName != null) + Entities.Entry(entity).Property(e => e.RemovedBy) + .CurrentValue = _currentUserService.UserName; + Entities.Update(entity); + } + + public virtual void DeleteRange(IEnumerable entities) + { + var apiEntities = entities.ToList(); + AssertExtensions.NotNull(apiEntities, nameof(entities)); + foreach (var entity in apiEntities) + { + Entities.Entry(entity).Property(e => e.RemovedAt) + .CurrentValue = DateTime.Now; + Entities.Entry(entity).Property(e => e.IsRemoved) + .CurrentValue = true; + if (_currentUserService.UserName != null) + Entities.Entry(entity).Property(e => e.RemovedBy) + .CurrentValue = _currentUserService.UserName; + Entities.Update(entity); + } + } + + #endregion + + #region Attach & Detach + + public virtual void Detach(T entity) + { + AssertExtensions.NotNull(entity, nameof(entity)); + var entry = DbContext.Entry(entity); + entry.State = EntityState.Detached; + } + + public virtual void Attach(T entity) + { + AssertExtensions.NotNull(entity, nameof(entity)); + if (DbContext.Entry(entity).State == EntityState.Detached) + Entities.Attach(entity); + } + + #endregion + } +} \ No newline at end of file diff --git a/HiVakil.Repository/Repositories/Base/Contracts/IBaseRepository.cs b/HiVakil.Repository/Repositories/Base/Contracts/IBaseRepository.cs new file mode 100644 index 0000000..fe8e069 --- /dev/null +++ b/HiVakil.Repository/Repositories/Base/Contracts/IBaseRepository.cs @@ -0,0 +1,6 @@ +namespace HiVakil.Repository.Repositories.Base.Contracts +{ + public interface IBaseRepository : IDisposable, IReadRepository, IWriteRepository where T : class, IApiEntity + { + } +} \ No newline at end of file diff --git a/HiVakil.Repository/Repositories/Base/Contracts/IReadRepository.cs b/HiVakil.Repository/Repositories/Base/Contracts/IReadRepository.cs new file mode 100644 index 0000000..947cd8f --- /dev/null +++ b/HiVakil.Repository/Repositories/Base/Contracts/IReadRepository.cs @@ -0,0 +1,13 @@ +namespace HiVakil.Repository.Repositories.Base.Contracts +{ + public interface IReadRepository where T : class, IApiEntity + { + DbSet Entities { get; } + IQueryable ExecuteCommand(FormattableString command); + IQueryable Table { get; } + IQueryable TableNoTracking { get; } + T GetById(params object[] ids); + ValueTask GetByIdAsync(CancellationToken cancellationToken, params object[] ids); + + } +} \ No newline at end of file diff --git a/HiVakil.Repository/Repositories/Base/Contracts/IRepository.cs b/HiVakil.Repository/Repositories/Base/Contracts/IRepository.cs new file mode 100644 index 0000000..4df4baf --- /dev/null +++ b/HiVakil.Repository/Repositories/Base/Contracts/IRepository.cs @@ -0,0 +1,10 @@ +namespace HiVakil.Repository.Repositories.Base.Contracts +{ + internal interface IRepository where T : class, IApiEntity + { + DbSet Entities { get; } + IQueryable Table { get; } + IQueryable TableNoTracking { get; } + + } +} \ No newline at end of file diff --git a/HiVakil.Repository/Repositories/Base/Contracts/IRepositoryWrapper.cs b/HiVakil.Repository/Repositories/Base/Contracts/IRepositoryWrapper.cs new file mode 100644 index 0000000..3aca65f --- /dev/null +++ b/HiVakil.Repository/Repositories/Base/Contracts/IRepositoryWrapper.cs @@ -0,0 +1,11 @@ +namespace HiVakil.Repository.Repositories.Base.Contracts +{ + public interface IRepositoryWrapper : IDisposable , IScopedDependency + { + IBaseRepository SetRepository() where T : ApiEntity; + Task BeginTransaction(CancellationToken cancellationToken); + Task RollBackAsync(CancellationToken cancellationToken); + Task CommitAsync(CancellationToken cancellationToken); + Task SaveChangesAsync(CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/HiVakil.Repository/Repositories/Base/Contracts/IWriteRepository.cs b/HiVakil.Repository/Repositories/Base/Contracts/IWriteRepository.cs new file mode 100644 index 0000000..fcc8eb8 --- /dev/null +++ b/HiVakil.Repository/Repositories/Base/Contracts/IWriteRepository.cs @@ -0,0 +1,15 @@ +namespace HiVakil.Repository.Repositories.Base.Contracts +{ + public interface IWriteRepository where T : class, IApiEntity + { + void Add(T entity); + void AddRange(IEnumerable entities); + void Delete(T entity); + void HardDelete(T entity); + void DeleteRange(IEnumerable entities); + void Update(T entity); + void UpdateRange(IEnumerable entities); + void Detach(T entity); + void Attach(T entity); + } +} \ No newline at end of file diff --git a/HiVakil.Repository/Repositories/Base/ReadRepository.cs b/HiVakil.Repository/Repositories/Base/ReadRepository.cs new file mode 100644 index 0000000..4affd3d --- /dev/null +++ b/HiVakil.Repository/Repositories/Base/ReadRepository.cs @@ -0,0 +1,44 @@ +namespace HiVakil.Repository.Repositories.Base +{ + public class ReadRepository : Repository, IDisposable, IReadRepository where T : class, IApiEntity + { + public ReadRepository( + ApplicationContext dbContext) : base(dbContext) + { + } + + public new void Dispose() + { + DbContext?.Dispose(); + } + + + public virtual T GetById(params object[] ids) + { + var ent = Entities.Find(ids); + if (ent == null) + throw new AppException("Ent not found"); + Detach(ent); + return ent; + } + + public virtual ValueTask GetByIdAsync(CancellationToken cancellationToken, params object[] ids) + { + return Entities.FindAsync(ids, cancellationToken); + } + + public virtual void Detach(T entity) + { + AssertExtensions.NotNull(entity, nameof(entity)); + var entry = DbContext.Entry(entity); + entry.State = EntityState.Detached; + } + + public virtual void Attach(T entity) + { + AssertExtensions.NotNull(entity, nameof(entity)); + if (DbContext.Entry(entity).State == EntityState.Detached) + Entities.Attach(entity); + } + } +} \ No newline at end of file diff --git a/HiVakil.Repository/Repositories/Base/Repository.cs b/HiVakil.Repository/Repositories/Base/Repository.cs new file mode 100644 index 0000000..dd1fa44 --- /dev/null +++ b/HiVakil.Repository/Repositories/Base/Repository.cs @@ -0,0 +1,36 @@ +namespace HiVakil.Repository.Repositories.Base +{ + public class Repository : IRepository where T : class, IApiEntity + { + protected readonly ApplicationContext DbContext; + + public Repository(ApplicationContext dbContext) + { + DbContext = dbContext; + Entities = DbContext.Set(); + DbContext.ChangeTracker.Clear(); + } + + public DbSet Entities { get; } + + public virtual IQueryable Table => Entities.Where(e => !e.IsRemoved); + + public virtual IQueryable TableNoTracking => Table.AsNoTracking(); + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + DbContext?.Dispose(); + } + + public IQueryable ExecuteCommand(FormattableString command) + { + return DbContext.Set().FromSql(command); + } + } +} \ No newline at end of file diff --git a/HiVakil.Repository/Repositories/Base/RepositoryWrapper.cs b/HiVakil.Repository/Repositories/Base/RepositoryWrapper.cs new file mode 100644 index 0000000..83d1c86 --- /dev/null +++ b/HiVakil.Repository/Repositories/Base/RepositoryWrapper.cs @@ -0,0 +1,64 @@ +namespace HiVakil.Repository.Repositories.Base; +public class RepositoryWrapper : IRepositoryWrapper +{ + private readonly ApplicationContext _context; + private readonly ICurrentUserService _currentUserService; + private IDbContextTransaction? _currentTransaction; + public RepositoryWrapper(ApplicationContext context, ICurrentUserService currentUserService) + { + _context = context; + _currentUserService = currentUserService; + } + + public IBaseRepository SetRepository() where T : ApiEntity => new BaseRepository(_context, _currentUserService); + + + public async Task RollBackAsync(CancellationToken cancellationToken) + { + if (_currentTransaction == null) + throw new ArgumentNullException(nameof(_currentTransaction)); + await _currentTransaction.RollbackAsync(cancellationToken); + } + public async Task CommitAsync(CancellationToken cancellationToken) + { + if (_currentTransaction == null) + throw new ArgumentNullException(nameof(_currentTransaction)); + await _currentTransaction.CommitAsync(cancellationToken); + } + public async Task BeginTransaction(CancellationToken cancellationToken) + { + _currentTransaction = await _context.Database.BeginTransactionAsync(cancellationToken); + } + public async Task SaveChangesAsync(CancellationToken cancellationToken = default) + { + SetAuditables(); + await _context.SaveChangesAsync(cancellationToken); + } + + private void SetAuditables() + { + IEnumerable> entries = _context.ChangeTracker.Entries(); + foreach (EntityEntry entity in entries) + { + if (entity.State == EntityState.Added) + { + entity.Property(e => e.CreatedAt) + .CurrentValue = DateTime.Now; + } + + if (entity.State == EntityState.Modified) + { + entity.Property(e => e.ModifiedAt) + .CurrentValue = DateTime.Now; + if (_currentUserService.UserName != null) + entity.Property(e => e.ModifiedBy) + .CurrentValue = _currentUserService.UserName; + } + } + } + public void Dispose() + { + _currentTransaction?.Dispose(); + _context?.Dispose(); + } +} diff --git a/HiVakil.Repository/Repositories/Base/WriteRepository.cs b/HiVakil.Repository/Repositories/Base/WriteRepository.cs new file mode 100644 index 0000000..53029ac --- /dev/null +++ b/HiVakil.Repository/Repositories/Base/WriteRepository.cs @@ -0,0 +1,95 @@ + +namespace HiVakil.Repository.Repositories.Base +{ + public class WriteRepository : Repository, IDisposable, IWriteRepository where T : class, IApiEntity + { + private readonly ICurrentUserService _currentUserService; + + public WriteRepository(ApplicationContext dbContext,ICurrentUserService currentUserService) : base(dbContext) + { + _currentUserService = currentUserService; + } + + public void Dispose() + { + DbContext?.Dispose(); + } + + public void HardDelete(T entity) + { + AssertExtensions.NotNull(entity, nameof(entity)); + Entities.Remove(entity); + } + + public virtual void Add(T entity) + { + AssertExtensions.NotNull(entity, nameof(entity)); + Entities.Add(entity); + } + + public virtual void AddRange(IEnumerable entities) + { + AssertExtensions.NotNull(entities, nameof(entities)); + Entities.AddRange(entities); + } + + public virtual void Update(T entity) + { + AssertExtensions.NotNull(entity, nameof(entity)); + Detach(entity); + Entities.Update(entity); + } + + public virtual void UpdateRange(IEnumerable entities) + { + AssertExtensions.NotNull(entities, nameof(entities)); + Entities.UpdateRange(entities); + } + + public virtual void Delete(T entity) + { + AssertExtensions.NotNull(entity, nameof(entity)); + + Entities.Entry(entity).Property(e => e.RemovedAt) + .CurrentValue = DateTime.Now; + Entities.Entry(entity).Property(e => e.IsRemoved) + .CurrentValue = true; + if (_currentUserService.UserName != null) + Entities.Entry(entity).Property(e => e.RemovedBy) + .CurrentValue = _currentUserService.UserName; + Entities.Update(entity); + } + + public virtual void DeleteRange(IEnumerable entities) + { + var apiEntities = entities.ToList(); + AssertExtensions.NotNull(apiEntities, nameof(entities)); + foreach (var entity in apiEntities) + { + Entities.Entry(entity).Property(e => e.RemovedAt) + .CurrentValue = DateTime.Now; + Entities.Entry(entity).Property(e => e.IsRemoved) + .CurrentValue = true; + if (_currentUserService.UserName != null) + Entities.Entry(entity).Property(e => e.RemovedBy) + .CurrentValue = _currentUserService.UserName; + Entities.Update(entity); + } + } + + public virtual void Detach(T entity) + { + AssertExtensions.NotNull(entity, nameof(entity)); + var entry = DbContext.Entry(entity); + if (entry != null) + entry.State = EntityState.Detached; + } + + public virtual void Attach(T entity) + { + AssertExtensions.NotNull(entity, nameof(entity)); + if (DbContext.Entry(entity).State == EntityState.Detached) + Entities.Attach(entity); + } + } +} \ No newline at end of file diff --git a/HiVakil.Repository/Repositories/UnitOfWork/IUnitOfWork.cs b/HiVakil.Repository/Repositories/UnitOfWork/IUnitOfWork.cs new file mode 100644 index 0000000..6e3ceef --- /dev/null +++ b/HiVakil.Repository/Repositories/UnitOfWork/IUnitOfWork.cs @@ -0,0 +1,9 @@ +namespace HiVakil.Repository.Repositories.UnitOfWork; + +public interface IUnitOfWork : IScopedDependency +{ + Task BeginTransaction(); + Task RollBackAsync(); + Task CommitAsync(); + Task SaveChangesAsync(CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/HiVakil.Repository/Repositories/UnitOfWork/UnitOfWork.cs b/HiVakil.Repository/Repositories/UnitOfWork/UnitOfWork.cs new file mode 100644 index 0000000..ac9c956 --- /dev/null +++ b/HiVakil.Repository/Repositories/UnitOfWork/UnitOfWork.cs @@ -0,0 +1,61 @@ +namespace HiVakil.Repository.Repositories.UnitOfWork; + +public class UnitOfWork : IUnitOfWork +{ + private readonly ApplicationContext _applicationContext; + private IDbContextTransaction? _currentTransaction ; + public UnitOfWork(ApplicationContext applicationContext) + { + _applicationContext = applicationContext; + } + + public async Task RollBackAsync() + { + if (_currentTransaction == null) + throw new ArgumentNullException(nameof(_currentTransaction)); + await _currentTransaction.RollbackAsync(); + } + public async Task CommitAsync() + { + if (_currentTransaction == null) + throw new ArgumentNullException(nameof(_currentTransaction)); + await _currentTransaction.CommitAsync(); + } + public async Task BeginTransaction() + { + _currentTransaction = await _applicationContext.Database.BeginTransactionAsync(); + } + public async Task SaveChangesAsync(CancellationToken cancellationToken = default) + { + SetAuditables(); + await _applicationContext.SaveChangesAsync(cancellationToken); + } + + private void SetAuditables() + { + IEnumerable> entries = _applicationContext.ChangeTracker.Entries(); + foreach (EntityEntry entity in entries) + { + if (entity.State == EntityState.Added) + { + entity.Property(e=>e.CreatedAt) + .CurrentValue = DateTime.Now; + } + + if (entity.State == EntityState.Modified) + { + entity.Property(e => e.ModifiedAt) + .CurrentValue = DateTime.Now; + } + + if (entity.State == EntityState.Deleted) + { + entity.Property(e => e.RemovedAt) + .CurrentValue = DateTime.Now; + entity.Property(e => e.IsRemoved) + .CurrentValue = true; + entity.State = EntityState.Modified; + } + } + } +} \ No newline at end of file diff --git a/HiVakil.Repository/RepositoryConfig.cs b/HiVakil.Repository/RepositoryConfig.cs new file mode 100644 index 0000000..f056f7c --- /dev/null +++ b/HiVakil.Repository/RepositoryConfig.cs @@ -0,0 +1,19 @@ +namespace HiVakil.Repository +{ + public static class RepositoryConfig + { + public static async Task InitialDb(this IApplicationBuilder app) + { + var scopeFactory = app.ApplicationServices.GetRequiredService(); + using (var scope = scopeFactory.CreateScope()) + { + var identityDbInitialize = scope.ServiceProvider.GetService(); + if (identityDbInitialize != null) + { + identityDbInitialize.Initialize(); + await identityDbInitialize.SeedDate(); + } + } + } + } +} diff --git a/HiVakil.Repository/Services/Abstracts/IDbInitializerService.cs b/HiVakil.Repository/Services/Abstracts/IDbInitializerService.cs new file mode 100644 index 0000000..d951a5d --- /dev/null +++ b/HiVakil.Repository/Services/Abstracts/IDbInitializerService.cs @@ -0,0 +1,9 @@ +namespace HiVakil.Repository.Services.Abstracts; + + +public interface IDbInitializerService : IScopedDependency +{ + void Initialize(); + Task SeedDate(bool force = false); + Task SeedRoles(); +} \ No newline at end of file diff --git a/HiVakil.Repository/Services/DbInitializerService.cs b/HiVakil.Repository/Services/DbInitializerService.cs new file mode 100644 index 0000000..1f7ee2a --- /dev/null +++ b/HiVakil.Repository/Services/DbInitializerService.cs @@ -0,0 +1,145 @@ +namespace HiVakil.Repository.Services; + + +public class DbInitializerService : IDbInitializerService +{ + private readonly IOptionsSnapshot _adminUserSeedOptions; + private readonly ApplicationContext _context; + private readonly ILogger _logger; + private readonly RoleManager _roleManager; + private readonly UserManager _userManager; + + public DbInitializerService( + ApplicationContext context, + RoleManager roleManager, + UserManager userManager, + IOptionsSnapshot adminUserSeedOptions, + ILogger logger) + { + _context = context; + _roleManager = roleManager; + _userManager = userManager; + _adminUserSeedOptions = adminUserSeedOptions; + _logger = logger; + } + + public void Initialize() + { + try + { + _context.Database.Migrate(); + _logger.LogInformation("Migration SUCCESS !!!!"); + } + catch (Exception e) + { + _logger.LogError(e, e.Message); + } + } + + public async Task SeedDate(bool force = false) + { + try + { + await SeedRoles(); + + var seedRoot = _adminUserSeedOptions.Value.RootUser; + var rootUser = await _userManager.FindByNameAsync(seedRoot.Username); + if (rootUser == null) + { + rootUser = new ApplicationUser + { + UserName = seedRoot.Username, + Email = seedRoot.Email, + EmailConfirmed = true, + LockoutEnabled = true, + FirstName = seedRoot.FirstName, + LastName = seedRoot.LastName, + Gender = Gender.Male, + PhoneNumberConfirmed = true, + PhoneNumber = seedRoot.Phone, + BirthDate = DateTime.Now.AddYears(-23) + }; + var rootUserResult = await _userManager.CreateAsync(rootUser, seedRoot.Password); + if (rootUserResult.Succeeded) await _userManager.AddToRoleAsync(rootUser, seedRoot.RoleName); + } + + + var seedAdmin = _adminUserSeedOptions.Value.ManagerUser; + var adminUser = await _userManager.FindByNameAsync(seedAdmin.Username); + if (adminUser == null) + { + adminUser = new ApplicationUser + { + UserName = seedAdmin.Username, + Email = seedAdmin.Email, + EmailConfirmed = true, + LockoutEnabled = true, + FirstName = seedAdmin.FirstName, + LastName = seedAdmin.LastName, + Gender = Gender.Male, + PhoneNumberConfirmed = true, + PhoneNumber = seedAdmin.Phone, + BirthDate = DateTime.Now.AddYears(-23) + }; + var rootUserResult = await _userManager.CreateAsync(adminUser, seedAdmin.Password); + if (rootUserResult.Succeeded) await _userManager.AddToRoleAsync(adminUser, seedAdmin.RoleName); + } + + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } + + public async Task SeedRoles() + { + var seedAdmin = _adminUserSeedOptions.Value.RootUser; + var rootRole = await _roleManager.FindByNameAsync(seedAdmin.RoleName); + + if (rootRole == null) + { + rootRole = new ApplicationRole + { + Name = seedAdmin.RoleName, + EnglishName = seedAdmin.RoleName, + Description = "root admin role" + }; + var adminRoleResult = await _roleManager.CreateAsync(rootRole); + foreach (var claim in ApplicationClaims.AllClaims) + await _roleManager.AddClaimAsync(rootRole, claim); + } + + var managerRole = await _roleManager.FindByNameAsync("Manager"); + + if (managerRole == null) + { + managerRole = new ApplicationRole + { + Name = "Manager", + EnglishName = "Manager", + PersianName = "مدیریتـــ", + Description = "admin role" + }; + var adminRoleResult = await _roleManager.CreateAsync(managerRole); + foreach (var claim in ApplicationClaims.AllClaims) + await _roleManager.AddClaimAsync(managerRole, claim); + } + + var customerRole = await _roleManager.FindByNameAsync("Customer"); + if (customerRole == null) + { + customerRole = new ApplicationRole + { + Name = "Customer", + PersianName = "مشتری", + EnglishName = "Customer", + + }; + var customerRoleResult = await _roleManager.CreateAsync(customerRole); + foreach (var claim in ApplicationClaims.CustomerClaims) + await _roleManager.AddClaimAsync(customerRole, claim); + } + } +} \ No newline at end of file diff --git a/HiVakil.sln b/HiVakil.sln index 4ce751f..8b9f80b 100644 --- a/HiVakil.sln +++ b/HiVakil.sln @@ -5,6 +5,16 @@ VisualStudioVersion = 17.8.34316.72 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HiVakil.Api", "HiVakil.Api\HiVakil.Api.csproj", "{E38E9183-01E9-4481-A770-68EAD4AD89C0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HiVakil.Common", "HiVakil.Common\HiVakil.Common.csproj", "{19D031A0-FEE2-44BA-808F-612453407646}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HiVakil.Core", "HiVakil.Core\HiVakil.Core.csproj", "{7BF41325-E416-4255-94FD-B1ECFF022CD1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HiVakil.Domain", "HiVakil.Domain\HiVakil.Domain.csproj", "{D02B41D3-8F0C-4476-A32B-98F2005221B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HiVakil.Infrastructure", "HiVakil.Infrastructure\HiVakil.Infrastructure.csproj", "{13161C87-0910-4682-BB00-55DD7EC72063}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HiVakil.Repository", "HiVakil.Repository\HiVakil.Repository.csproj", "{6D41BC11-AF11-47AF-BF38-8D53F217D775}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +25,26 @@ Global {E38E9183-01E9-4481-A770-68EAD4AD89C0}.Debug|Any CPU.Build.0 = Debug|Any CPU {E38E9183-01E9-4481-A770-68EAD4AD89C0}.Release|Any CPU.ActiveCfg = Release|Any CPU {E38E9183-01E9-4481-A770-68EAD4AD89C0}.Release|Any CPU.Build.0 = Release|Any CPU + {19D031A0-FEE2-44BA-808F-612453407646}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {19D031A0-FEE2-44BA-808F-612453407646}.Debug|Any CPU.Build.0 = Debug|Any CPU + {19D031A0-FEE2-44BA-808F-612453407646}.Release|Any CPU.ActiveCfg = Release|Any CPU + {19D031A0-FEE2-44BA-808F-612453407646}.Release|Any CPU.Build.0 = Release|Any CPU + {7BF41325-E416-4255-94FD-B1ECFF022CD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7BF41325-E416-4255-94FD-B1ECFF022CD1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7BF41325-E416-4255-94FD-B1ECFF022CD1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7BF41325-E416-4255-94FD-B1ECFF022CD1}.Release|Any CPU.Build.0 = Release|Any CPU + {D02B41D3-8F0C-4476-A32B-98F2005221B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D02B41D3-8F0C-4476-A32B-98F2005221B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D02B41D3-8F0C-4476-A32B-98F2005221B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D02B41D3-8F0C-4476-A32B-98F2005221B8}.Release|Any CPU.Build.0 = Release|Any CPU + {13161C87-0910-4682-BB00-55DD7EC72063}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {13161C87-0910-4682-BB00-55DD7EC72063}.Debug|Any CPU.Build.0 = Debug|Any CPU + {13161C87-0910-4682-BB00-55DD7EC72063}.Release|Any CPU.ActiveCfg = Release|Any CPU + {13161C87-0910-4682-BB00-55DD7EC72063}.Release|Any CPU.Build.0 = Release|Any CPU + {6D41BC11-AF11-47AF-BF38-8D53F217D775}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6D41BC11-AF11-47AF-BF38-8D53F217D775}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D41BC11-AF11-47AF-BF38-8D53F217D775}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6D41BC11-AF11-47AF-BF38-8D53F217D775}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE