CHANGE PROJECT NAME TO "NetinaCMS"
|
@ -1,17 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<InvariantGlobalization>true</InvariantGlobalization>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,42 +0,0 @@
|
|||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
var summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
app.MapGet("/weatherforecast", () =>
|
||||
{
|
||||
var forecast = Enumerable.Range(1, 5).Select(index =>
|
||||
new WeatherForecast
|
||||
(
|
||||
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
||||
Random.Shared.Next(-20, 55),
|
||||
summaries[Random.Shared.Next(summaries.Length)]
|
||||
))
|
||||
.ToArray();
|
||||
return forecast;
|
||||
})
|
||||
.WithName("GetWeatherForecast")
|
||||
.WithOpenApi();
|
||||
|
||||
app.Run();
|
||||
|
||||
internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
||||
{
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
||||
<PackageReference Include="AspNetCoreRateLimit.Redis" Version="2.0.0" />
|
||||
<PackageReference Include="Autofac.Extras.Quartz" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.2" />
|
||||
<PackageReference Include="Quartz" Version="3.8.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\HamyanEdalat.Repository\HamyanEdalat.Repository.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="HamyanEdalat.Common.Extensions" />
|
||||
<Using Include="HamyanEdalat.Common.Models" />
|
||||
<Using Include="HamyanEdalat.Common.Models.Api" />
|
||||
<Using Include="HamyanEdalat.Common.Models.Exception" />
|
||||
<Using Include="HamyanEdalat.Core.Abstracts" />
|
||||
<Using Include="HamyanEdalat.Core.BaseServices.Abstracts" />
|
||||
<Using Include="HamyanEdalat.Core.CoreServices.Abstracts" />
|
||||
<Using Include="HamyanEdalat.Core.EntityServices.Abstracts" />
|
||||
<Using Include="HamyanEdalat.Domain.Dtos.RequestDtos" />
|
||||
<Using Include="HamyanEdalat.Domain.Dtos.ResponseDtos" />
|
||||
<Using Include="HamyanEdalat.Domain.Dtos.SmallDto" />
|
||||
<Using Include="HamyanEdalat.Domain.Entities.Users" />
|
||||
<Using Include="HamyanEdalat.Domain.Enums" />
|
||||
<Using Include="HamyanEdalat.Domain.Mappers" />
|
||||
<Using Include="HamyanEdalat.Domain.Models.Claims" />
|
||||
<Using Include="HamyanEdalat.Domain.Models.Settings" />
|
||||
<Using Include="HamyanEdalat.Repository.Abstracts" />
|
||||
<Using Include="HamyanEdalat.Repository.Repositories.Base.Contracts" />
|
||||
<Using Include="Mapster" />
|
||||
<Using Include="MediatR" />
|
||||
<Using Include="Microsoft.AspNetCore.Identity" />
|
||||
<Using Include="Microsoft.AspNetCore.Mvc" />
|
||||
<Using Include="Microsoft.EntityFrameworkCore" />
|
||||
<Using Include="Microsoft.Extensions.Options" />
|
||||
<Using Include="Microsoft.IdentityModel.Tokens" />
|
||||
<Using Include="Newtonsoft.Json" />
|
||||
<Using Include="System.IdentityModel.Tokens.Jwt" />
|
||||
<Using Include="System.Security.Claims" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,6 +0,0 @@
|
|||
using HamyanEdalat.Domain.Dtos.ResponseDtos;
|
||||
|
||||
namespace HamyanEdalat.Domain.CommandQueries.Queries;
|
||||
|
||||
public record GetBlogsQuery(int Page = 0) : IRequest<GetBlogsResponseDto>;
|
||||
public record GetBlogQuery(Guid Id) : IRequest<BlogLDto>;
|
|
@ -1,7 +0,0 @@
|
|||
namespace HamyanEdalat.Repository
|
||||
{
|
||||
public class RepositoryConfig
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"ConnectionStrings": {
|
||||
"PostgresServer": "User ID=postgres;Password=root;Host=localhost;Port=5432;Database=HamyanEdalatDB;",
|
||||
"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": "https://api.hamyanedalat.com",
|
||||
"AdminPanelBaseUrl": "https://admin.hamyanedalat.com",
|
||||
"KaveNegarApiKey": "3735494B4143727A794346457461576A2B4B6668414973424E333561505A694B",
|
||||
"RootUser": {
|
||||
"Username": "09214802813",
|
||||
"Email": "avvampier@gmail.com",
|
||||
"Password": "KqBGMExTe42yHUiz",
|
||||
"Phone": "09214802813",
|
||||
"RoleName": "RootAdmin",
|
||||
"FirstName": "همه کاره",
|
||||
"LastName": "سیستم"
|
||||
},
|
||||
"ManagerUser": {
|
||||
"Username": "hamyanedalat",
|
||||
"Email": "info@hivakil.io",
|
||||
"Password": "GPDBxpk28yKo0L3v",
|
||||
"Phone": "09211111111",
|
||||
"RoleName": "Manager",
|
||||
"FirstName": "ادمین",
|
||||
"LastName": "سایت"
|
||||
},
|
||||
"JwtSettings": {
|
||||
"SecretKey": "YA_KHOD_KHODA_fmGg7e7T5uXQMhbm99e2MF2VudmBn0KCdo+HAblHkhC9P0ldpii1Tj+0nOVep9zh_YA_EMAM_HOSSEIN_tfbiY7CdhODnAc01GG138g==_YA_JADE_SEYED_MAJID_tfbiY7CdhODnAc01GG138g==_YA_ALI_MADADI",
|
||||
"Issuer": "HamyanEdalat",
|
||||
"Audience": "HamyanEdalat",
|
||||
"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": "*"
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
"ConnectionStrings": {
|
||||
"PostgresServer": "User ID=postgres;Password=root;Host=localhost;Port=5432;Database=HamyanEdalatDB;",
|
||||
"Postgres": "Host=pg-0,pg-1;Username=igarsonAgent;Password=xHTpBf4wC+bBeNg2pL6Ga7VEWKFJx7VPEUpqxwPFfOc2YYTVwFQuHfsiqoVeT9+6;Database=HamyanEdalatDB;Load Balance Hosts=true;Target Session Attributes=primary;Application Name=iGLS",
|
||||
"MartenDB": "Host=pg-0,pg-1;Username=igarsonAgent;Password=xHTpBf4wC+bBeNg2pL6Ga7VEWKFJx7VPEUpqxwPFfOc2YYTVwFQuHfsiqoVeT9+6;Database=HamyanEdalatMartenDB;"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "None",
|
||||
"Microsoft.Hosting.Lifetime": "Information",
|
||||
"Microsoft.AspNetCore.SignalR": "Debug",
|
||||
"Microsoft.AspNetCore.Http.Connections": "Debug"
|
||||
}
|
||||
},
|
||||
"SiteSettings": {
|
||||
"BaseUrl": "http://localhost:32768",
|
||||
"AdminPanelBaseUrl": "https://admin.hamyanedalat.com",
|
||||
"KaveNegarApiKey": "3735494B4143727A794346457461576A2B4B6668414973424E333561505A694B",
|
||||
"StorageSetting": {
|
||||
"AccessKey": "",
|
||||
"SecretKey": ""
|
||||
},
|
||||
"RootUser": {
|
||||
"Username": "09214802813",
|
||||
"Email": "avvampier@gmail.com",
|
||||
"Password": "KqBGMExTe42yHUiz",
|
||||
"Phone": "09214802813",
|
||||
"RoleName": "RootAdmin",
|
||||
"FirstName": "همه کاره",
|
||||
"LastName": "سیستم"
|
||||
},
|
||||
"ManagerUser": {
|
||||
"Username": "hamyanedalat",
|
||||
"Email": "info@hivakil.io",
|
||||
"Password": "GPDBxpk28yKo0L3v",
|
||||
"Phone": "09211111111",
|
||||
"RoleName": "Manager",
|
||||
"FirstName": "ادمین",
|
||||
"LastName": "سایت"
|
||||
},
|
||||
"JwtSettings": {
|
||||
"SecretKey": "YA_KHOD_KHODA_fmGg7e7T5uXQMhbm99e2MF2VudmBn0KCdo+HAblHkhC9P0ldpii1Tj+0nOVep9zh_YA_EMAM_HOSSEIN_tfbiY7CdhODnAc01GG138g==_YA_JADE_SEYED_MAJID_tfbiY7CdhODnAc01GG138g==_YA_ALI_MADADI",
|
||||
"Issuer": "HamyanEdalat",
|
||||
"Audience": "HamyanEdalat",
|
||||
"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": "*"
|
||||
}
|
|
@ -4,5 +4,6 @@
|
|||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
namespace NetinaCMS.Api.Controllers;
|
||||
|
||||
|
||||
public class AuthController : ICarterModule
|
||||
{
|
||||
public virtual void AddRoutes(IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.NewVersionedApi("Auth")
|
||||
.MapGroup($"api/auth");
|
||||
|
||||
group.MapPost("login/password", LoginWithPassword)
|
||||
.WithDisplayName("LoginWithPassword")
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
group.MapPost("login/swagger", LoginSwagger)
|
||||
.WithDisplayName("LoginSwagger")
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
group.MapPost("login/code", LoginWithVerifyCode)
|
||||
.WithDisplayName("LoginWithVerifyCode")
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
group.MapGet("verifycode", GetVerifyCodeCode)
|
||||
.WithDisplayName("GetVerifyCodeCode")
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
group.MapPut("forget/password", ForgetPassword)
|
||||
.WithDisplayName("ForgetPassword")
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
group.MapPost("signup", SignUpComplex)
|
||||
.WithDisplayName("SignUp")
|
||||
.RequireAuthorization(builder => builder.AddAuthenticationSchemes("Bearer").RequireAuthenticatedUser())
|
||||
.HasApiVersion(1.0);
|
||||
}
|
||||
|
||||
public async Task<IResult> SignUpComplex([FromBody] SignUpRequestDto request, IAccountService accountService, CancellationToken cancellationToken) =>
|
||||
TypedResults.Ok(await accountService.CompleteSignUpAsync(request, cancellationToken));
|
||||
|
||||
public async Task<IResult> LoginWithPassword([FromBody] LoginRequestDto loginRequestDto, IAccountService accountService, CancellationToken cancellationToken) =>
|
||||
TypedResults.Ok(await accountService.LoginWithPasswordAsync(loginRequestDto.UserName, loginRequestDto.Password, cancellationToken));
|
||||
|
||||
|
||||
public async Task<IResult> LoginWithVerifyCode([FromBody] LoginRequestDto loginRequestDto, IAccountService accountService, CancellationToken cancellationToken) =>
|
||||
TypedResults.Ok(await accountService.LoginWithVerifyCodeAsync(loginRequestDto.UserName, loginRequestDto.VerifyCode, cancellationToken));
|
||||
|
||||
|
||||
public async Task<IResult> GetVerifyCodeCode([FromQuery] string phoneNumber, IAccountService accountService) =>
|
||||
TypedResults.Ok(await accountService.GetVerifyCodeAsync(phoneNumber));
|
||||
|
||||
|
||||
public async Task<IResult> ForgetPassword([FromQuery] string phoneNumber, IAccountService accountService) =>
|
||||
TypedResults.Ok(await accountService.ForgetPasswordAsync(phoneNumber));
|
||||
|
||||
|
||||
public async Task<IResult> LoginSwagger(HttpContext ctx, IAccountService accountService, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
var username = ctx.Request.Form["username"];
|
||||
var password = ctx.Request.Form["password"];
|
||||
return TypedResults.Json(await accountService.LoginWithPasswordAsync(username, password, cancellationToken));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using NetinaCMS.Api.Views.Home;
|
||||
|
||||
namespace NetinaCMS.Api.Controllers;
|
||||
|
||||
[Route("")]
|
||||
[AllowAnonymous]
|
||||
public class HomeController : Controller
|
||||
{
|
||||
[HttpGet]
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View("Index", new IndexModel());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<InvariantGlobalization>true</InvariantGlobalization>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
|
||||
<AssemblyVersion>0.0.0.1</AssemblyVersion>
|
||||
<FileVersion>0.0.0.1</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.1" />
|
||||
|
||||
<PackageReference Include="Asp.Versioning.Http" Version="8.0.0" />
|
||||
<PackageReference Include="Ben.BlockingDetector" Version="0.0.4" />
|
||||
<PackageReference Include="Carter" Version="8.0.0" />
|
||||
<PackageReference Include="FluentValidation" Version="11.9.0" />
|
||||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.0" />
|
||||
<PackageReference Include="MediatR.Extensions.Autofac.DependencyInjection" Version="12.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Autofac" Version="8.0.0" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" />
|
||||
<PackageReference Include="Elmah.Io.AspNetCore.Serilog" Version="5.0.17" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
|
||||
<PackageReference Include="Sentry.Serilog" Version="4.0.1" />
|
||||
<PackageReference Include="Serilog" Version="3.1.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.PostgreSQL" Version="2.3.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Seq" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.ElmahIo" Version="5.0.38" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="StackExchange.Redis.Extensions.AspNetCore" Version="10.2.0" />
|
||||
<PackageReference Include="StackExchange.Redis.Extensions.Core" Version="10.2.0" />
|
||||
<PackageReference Include="StackExchange.Redis.Extensions.Newtonsoft" Version="10.2.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.0" />
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.8.6" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NetinaCMS.Infrastructure\NetinaCMS.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Asp.Versioning" />
|
||||
<Using Include="AspNetCoreRateLimit" />
|
||||
<Using Include="AspNetCoreRateLimit.Redis" />
|
||||
<Using Include="Autofac" />
|
||||
<Using Include="Autofac.Extensions.DependencyInjection" />
|
||||
<Using Include="Carter" />
|
||||
<Using Include="FluentValidation" />
|
||||
<Using Include="NetinaCMS.Api.Views.Home" />
|
||||
<Using Include="NetinaCMS.Api.WebFramework.Configurations" />
|
||||
<Using Include="NetinaCMS.Api.WebFramework.MiddleWares" />
|
||||
<Using Include="NetinaCMS.Api.WebFramework.Swagger" />
|
||||
<Using Include="NetinaCMS.Common.Extensions" />
|
||||
<Using Include="NetinaCMS.Common.Models" />
|
||||
<Using Include="NetinaCMS.Common.Models.Api" />
|
||||
<Using Include="NetinaCMS.Common.Models.Entity" />
|
||||
<Using Include="NetinaCMS.Common.Models.Exception" />
|
||||
<Using Include="NetinaCMS.Common.Models.Mapper" />
|
||||
<Using Include="NetinaCMS.Core" />
|
||||
<Using Include="NetinaCMS.Core.CoreServices.Abstracts" />
|
||||
<Using Include="NetinaCMS.Core.Models.Api" />
|
||||
<Using Include="NetinaCMS.Domain" />
|
||||
<Using Include="NetinaCMS.Domain.Dtos.RequestDtos" />
|
||||
<Using Include="NetinaCMS.Domain.Entities.Users" />
|
||||
<Using Include="NetinaCMS.Domain.Models.Settings" />
|
||||
<Using Include="NetinaCMS.Infrastructure" />
|
||||
<Using Include="NetinaCMS.Infrastructure.Models" />
|
||||
<Using Include="NetinaCMS.Repository" />
|
||||
<Using Include="NetinaCMS.Repository.Abstracts" />
|
||||
<Using Include="NetinaCMS.Repository.Behaviors" />
|
||||
<Using Include="NetinaCMS.Repository.Extensions" />
|
||||
<Using Include="NetinaCMS.Repository.Models" />
|
||||
<Using Include="NetinaCMS.Repository.Repositories.Base.Contracts" />
|
||||
<Using Include="Mapster" />
|
||||
<Using Include="MediatR" />
|
||||
<Using Include="MediatR.Extensions.Autofac.DependencyInjection" />
|
||||
<Using Include="MediatR.Extensions.Autofac.DependencyInjection.Builder" />
|
||||
<Using Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
|
||||
<Using Include="Microsoft.AspNetCore.Authorization" />
|
||||
<Using Include="Microsoft.AspNetCore.Identity" />
|
||||
<Using Include="Microsoft.AspNetCore.Mvc" />
|
||||
<Using Include="Microsoft.AspNetCore.Mvc.Authorization" />
|
||||
<Using Include="Microsoft.AspNetCore.ResponseCompression" />
|
||||
<Using Include="Microsoft.EntityFrameworkCore" />
|
||||
<Using Include="Microsoft.IdentityModel.Tokens" />
|
||||
<Using Include="Newtonsoft.Json" />
|
||||
<Using Include="Newtonsoft.Json.Serialization" />
|
||||
<Using Include="Serilog" />
|
||||
<Using Include="StackExchange.Redis.Extensions.Core.Configuration" />
|
||||
<Using Include="StackExchange.Redis.Extensions.Newtonsoft" />
|
||||
<Using Include="System.IdentityModel.Tokens.Jwt" />
|
||||
<Using Include="System.IO.Compression" />
|
||||
<Using Include="System.Linq.Expressions" />
|
||||
<Using Include="System.Net" />
|
||||
<Using Include="System.Text" />
|
||||
<Using Include="Weasel.Core" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,116 @@
|
|||
using NetinaCMS.Api.WebFramework.Configurations;
|
||||
using NetinaCMS.Api.WebFramework.MiddleWares;
|
||||
using NetinaCMS.Api.WebFramework.Swagger;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
|
||||
builder.Host.UseSerilog();
|
||||
LoggerConfig.ConfigureSerilog();
|
||||
string env = builder.Environment.IsDevelopment() == true ? "Development" : "Production";
|
||||
builder.Host.UseContentRoot(Directory.GetCurrentDirectory());
|
||||
if (builder.Environment.IsDevelopment())
|
||||
builder.Configuration
|
||||
.AddJsonFile($"AppSettings/appsettings.json")
|
||||
.AddJsonFile($"AppSettings/appsettings.{env}.json");
|
||||
|
||||
if (builder.Environment.IsProduction())
|
||||
builder.Configuration.AddJsonFile($"AppSettings/Production/appsettings.{env}.json");
|
||||
var configuration = builder.Configuration;
|
||||
var siteSetting = configuration.GetSection(nameof(SiteSettings)).Get<SiteSettings>();
|
||||
builder.Services.Configure<SiteSettings>(configuration.GetSection(nameof(SiteSettings)));
|
||||
|
||||
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddCustomCores();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddCustomSwagger(siteSetting!.BaseUrl);
|
||||
builder.Services.AddCustomApiVersioning();
|
||||
builder.Services.AddCustomController();
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddCustomResponseCompression();
|
||||
builder.Services.AddValidatorsFromAssembly(typeof(RepositoryConfig).Assembly, includeInternalTypes: true);
|
||||
builder.Services.AddCustomMvc();
|
||||
builder.Services.AddCustomAuthorization();
|
||||
builder.Services.AddJwtCustomAuthentication(siteSetting.JwtSettings);
|
||||
builder.Services.AddMvcCore().AddRazorPages().AddRazorViewEngine().AddViews();
|
||||
builder.Services.AddCustomIdentity();
|
||||
builder.Services.AddCustomDbContext(configuration);
|
||||
builder.Services.AddMarten(configuration, builder.Environment);
|
||||
builder.Services.AddCarter();
|
||||
|
||||
|
||||
builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
|
||||
{
|
||||
|
||||
var assembly = typeof(CoreConfig).Assembly;
|
||||
builder
|
||||
.RegisterAssemblyTypes(assembly)
|
||||
.AssignableTo<IScopedDependency>()
|
||||
.AsImplementedInterfaces()
|
||||
.InstancePerLifetimeScope();
|
||||
|
||||
var assemblyB = typeof(InfrastructureConfig).Assembly;
|
||||
builder.RegisterAssemblyTypes(assemblyB)
|
||||
.AssignableTo<IScopedDependency>()
|
||||
.AsImplementedInterfaces()
|
||||
.InstancePerLifetimeScope();
|
||||
|
||||
var assemblyC = typeof(RepositoryConfig).Assembly;
|
||||
builder.RegisterAssemblyTypes(assemblyC)
|
||||
.AssignableTo<IScopedDependency>()
|
||||
.AsImplementedInterfaces()
|
||||
.InstancePerLifetimeScope();
|
||||
|
||||
|
||||
var assemblyD = typeof(Program).Assembly;
|
||||
builder.RegisterAssemblyTypes(assemblyD)
|
||||
.AssignableTo<IScopedDependency>()
|
||||
.AsImplementedInterfaces()
|
||||
.InstancePerLifetimeScope();
|
||||
|
||||
builder.RegisterMediatR(MediatRConfigurationBuilder
|
||||
.Create(typeof(RepositoryConfig).Assembly)
|
||||
.WithCustomPipelineBehavior(typeof(ValidationBehavior<,>))
|
||||
.WithAllOpenGenericHandlerTypesRegistered()
|
||||
.Build());
|
||||
|
||||
builder.RegisterMediatR(MediatRConfigurationBuilder
|
||||
.Create(typeof(CoreConfig).Assembly)
|
||||
.WithCustomPipelineBehavior(typeof(ValidationBehavior<,>))
|
||||
.WithAllOpenGenericHandlerTypesRegistered()
|
||||
.Build());
|
||||
|
||||
builder.RegisterMediatR(MediatRConfigurationBuilder
|
||||
.Create(typeof(DomainConfig).Assembly)
|
||||
.WithCustomPipelineBehavior(typeof(ValidationBehavior<,>))
|
||||
.WithAllOpenGenericHandlerTypesRegistered()
|
||||
.Build());
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
//app.UseSwagger();
|
||||
//app.UseSwaggerUI();
|
||||
}
|
||||
app.UseCors("CorsPolicy");
|
||||
|
||||
app.UseCustomSwagger(siteSetting.BaseUrl);
|
||||
|
||||
app.UseAuthorization();
|
||||
app.UseAuthentication();
|
||||
|
||||
app.UseExceptionHandlerMiddleware();
|
||||
|
||||
app.MapCarter();
|
||||
app.UseStaticFiles();
|
||||
await app.InitialDb();
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
|
@ -23,9 +23,10 @@
|
|||
"launchBrowser": true,
|
||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_HTTP_PORTS": "8080"
|
||||
"ASPNETCORE_URLS": "http://+:80"
|
||||
},
|
||||
"publishAllPorts": true
|
||||
"publishAllPorts": true,
|
||||
"DockerfileRunArguments": " --network=mother -p 32768:80"
|
||||
}
|
||||
},
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
|
@ -0,0 +1,50 @@
|
|||
using System.Security.Claims;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace NetinaCMS.Api.Services;
|
||||
|
||||
public class CurrentUserService : ICurrentUserService
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public CurrentUserService(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
public string? UserId => _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
public string? RoleName => _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.Role);
|
||||
public string? UserName => _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.Name);
|
||||
public string? DeviceId => GetDeviceId(_httpContextAccessor.HttpContext);
|
||||
public bool IsAuthorized => GetAuthorized();
|
||||
public List<string>? Permissions => _httpContextAccessor.HttpContext?.User?.FindAll("Permission")?.Select(c => c.Value)?.ToList();
|
||||
|
||||
private string? GetDeviceId(HttpContext? context)
|
||||
{
|
||||
if (context?.Request?.Headers == null)
|
||||
return null;
|
||||
string? userAgent = context.Request.Headers["User-Agent"];
|
||||
string? ipAddress = context.Connection.RemoteIpAddress?.ToString();
|
||||
string? origin = context.Request.Headers["Origin"];
|
||||
string input = userAgent + "_" + ipAddress;
|
||||
|
||||
using SHA256 sha256Hash = SHA256.Create();
|
||||
byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < bytes.Length; i++)
|
||||
{
|
||||
builder.Append(bytes[i].ToString("x2"));
|
||||
}
|
||||
var uniqueId = builder.ToString();
|
||||
return uniqueId;
|
||||
}
|
||||
|
||||
private bool GetAuthorized()
|
||||
{
|
||||
if (_httpContextAccessor.HttpContext?.User.Identity == null)
|
||||
return false;
|
||||
return _httpContextAccessor.HttpContext.User.Identity.IsAuthenticated;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,274 @@
|
|||
@model NetinaCMS.Api.Views.Home.IndexModel
|
||||
@{
|
||||
}
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||
|
||||
<title>HiVakil API</title>
|
||||
<meta content="" name="description">
|
||||
<meta content="" name="keywords">
|
||||
|
||||
<!-- Favicons -->
|
||||
<link href="assets/img/favicon.png" rel="icon">
|
||||
<link href="assets/img/apple-touch-icon.png" rel="apple-touch-icon">
|
||||
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i|Jost:300,300i,400,400i,500,500i,600,600i,700,700i|Poppins:300,300i,400,400i,500,500i,600,600i,700,700i" rel="stylesheet">
|
||||
<link rel="preconnect" href="//fdn.fontcdn.ir">
|
||||
<link rel="preconnect" href="//v1.fontapi.ir">
|
||||
<link href="https://v1.fontapi.ir/css/Shabnam" rel="stylesheet">
|
||||
<!-- Vendor CSS Files -->
|
||||
|
||||
<link href="assets/vendor/boxicons/css/boxicons.min.css" rel="stylesheet">
|
||||
<link href="assets/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Template Main CSS File -->
|
||||
<link href="assets/css/style.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- ======= Header ======= -->
|
||||
<header id="header" class="fixed-top ">
|
||||
<div class="container d-flex align-items-center">
|
||||
|
||||
<h1 class="logo me-auto"><a href="index.html">HiVakil API</a></h1>
|
||||
<!-- Uncomment below if you prefer to use an image logo -->
|
||||
<!-- <a href="index.html" class="logo me-auto"><img src="assets/img/logo.png" alt="" class="img-fluid"></a>-->
|
||||
|
||||
<nav id="navbar" class="navbar">
|
||||
<ul>
|
||||
<li><a class="nav-link scrollto active" href="#hero">Home</a></li>
|
||||
<li><a class="nav-link scrollto" href="#why-us">Features</a></li>
|
||||
<li><a class="nav-link scrollto" href="#skills">Framworks</a></li>
|
||||
<li><a class="getstarted scrollto" target="_blank" href="/swagger/index.html">Go To Swagger</a></li>
|
||||
</ul>
|
||||
<i class="bi bi-list mobile-nav-toggle"></i>
|
||||
</nav><!-- .navbar -->
|
||||
|
||||
</div>
|
||||
</header><!-- End Header -->
|
||||
<!-- ======= Hero Section ======= -->
|
||||
<section id="hero" dir="ltr" class="d-flex min-vh-100 flex-column justify-content-center">
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 d-flex flex-column justify-content-center pt-4 pt-lg-0 order-2 order-lg-1" data-aos="fade-up" data-aos-delay="200">
|
||||
<h1>New API for use </h1>
|
||||
<h2 style="font-family: SF Pro Display, sans-serif; text-align: justify ;">
|
||||
This API is organized around REST. Our API has predictable resource-oriented URLs, accepts form-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs.
|
||||
</h2>
|
||||
<h2 style="font-family: SF Pro Display, sans-serif; text-align: justify ;">
|
||||
Version : @Model?.Version
|
||||
</h2>
|
||||
<div class="d-flex justify-content-center justify-content-lg-start">
|
||||
<a href="/swagger/index.html" target="_blank" class="btn-get-started scrollto"><i class="bi bi-chevron-right" style="font-family: SF Pro Display, sans-serif; margin-right:10px ;"></i><span>Go to Swagger</span></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 order-1 order-lg-2 hero-img" data-aos="zoom-in" data-aos-delay="200">
|
||||
<img src="assets/img/hero-img.png" class="img-fluid animated" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section><!-- End Hero -->
|
||||
|
||||
<main id="main">
|
||||
<!-- ======= Why Us Section ======= -->
|
||||
<section id="why-us" class="why-us section-bg d-flex min-vh-100 flex-column justify-content-center">
|
||||
<div class="container-fluid" data-aos="fade-up">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-lg-7 d-flex flex-column justify-content-center align-items-stretch order-2 order-lg-1">
|
||||
|
||||
<div class="content">
|
||||
<h3>Features of using this API</h3>
|
||||
<p>
|
||||
We have brought you lots of different features in this API for you
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="accordion-list">
|
||||
<ul>
|
||||
<li>
|
||||
<a data-bs-toggle="collapse" class="collapse" data-bs-target="#accordion-list-1"><span>01</span> Use JSON <i class="bx bx-chevron-down icon-show"></i><i class="bx bx-chevron-up icon-close"></i></a>
|
||||
<div id="accordion-list-1" class="collapse show" data-bs-parent=".accordion-list">
|
||||
<p>
|
||||
All responses and data convert to JSON , and you get json compresed response in all request
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a data-bs-toggle="collapse" data-bs-target="#accordion-list-2" class="collapsed"><span>02</span> User Authentication <i class="bx bx-chevron-down icon-show"></i><i class="bx bx-chevron-up icon-close"></i></a>
|
||||
<div id="accordion-list-2" class="collapse" data-bs-parent=".accordion-list">
|
||||
<p>
|
||||
The authentication of API is based on USERNAME & PASSWORD , in this case you user your test user for testing API and methods
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a data-bs-toggle="collapse" data-bs-target="#accordion-list-3" class="collapsed"><span>03</span> Api Versionening <i class="bx bx-chevron-down icon-show"></i><i class="bx bx-chevron-up icon-close"></i></a>
|
||||
<div id="accordion-list-3" class="collapse" data-bs-parent=".accordion-list">
|
||||
<p>
|
||||
We create several version of APIs and update it , so you can use all version of APIs , old one and new one . you have to be care about use versions
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-lg-5 align-items-stretch order-1 order-lg-2 img" style='background-image: url("assets/img/features.svg");' data-aos="zoom-in" data-aos-delay="150"> </div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section><!-- End Why Us Section -->
|
||||
<!-- ======= Cta Section ======= -->
|
||||
<section id="cta" class="cta ">
|
||||
<div class="container" data-aos="zoom-in">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9 text-center text-lg-start">
|
||||
<h3>SWAGGER</h3>
|
||||
<p>Swagger is one of the best API document generator , You can use swagger to read API document and test API , for using swagger you need to login and use you username and password</p>
|
||||
</div>
|
||||
<div class="col-lg-3 cta-btn-container text-center">
|
||||
<a class="cta-btn align-middle" target="_blank" href="/swagger/index.html">Go to swagger</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section><!-- End Cta Section -->
|
||||
<!-- ======= Skills Section ======= -->
|
||||
<section id="skills" class="skills d-flex min-vh-100 flex-column justify-content-center">
|
||||
<div class="container" data-aos="fade-up">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6 d-flex align-items-center" data-aos="fade-right" data-aos-delay="100">
|
||||
<img src="assets/img/skills.png" class="img-fluid" alt="">
|
||||
</div>
|
||||
<div class="col-lg-6 pt-4 pt-lg-0 content" data-aos="fade-left" data-aos-delay="100">
|
||||
<h3>Framworks that we used in this project</h3>
|
||||
<p class="fst-italic">
|
||||
We use several frameworks to develope our projects , is this project we use hight teck and most popular frameworks and programming languages
|
||||
</p>
|
||||
|
||||
<div class="skills-content">
|
||||
|
||||
<div class="progress">
|
||||
<span class="skill">.NET <i class="val">100%</i></span>
|
||||
<div class="progress-bar-wrap">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="progress">
|
||||
<span class="skill">MicroServices <i class="val">100%</i></span>
|
||||
<div class="progress-bar-wrap">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="progress">
|
||||
<span class="skill">Netina-Architecture <i class="val">100%</i></span>
|
||||
<div class="progress-bar-wrap">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="progress">
|
||||
<span class="skill">DDD <i class="val">90%</i></span>
|
||||
<div class="progress-bar-wrap">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="progress">
|
||||
<span class="skill">MediatR <i class="val">90%</i></span>
|
||||
<div class="progress-bar-wrap">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="90" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="progress">
|
||||
<span class="skill">Redis <i class="val">90%</i></span>
|
||||
<div class="progress-bar-wrap">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="70" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section><!-- End Skills Section -->
|
||||
|
||||
|
||||
</main><!-- End #main -->
|
||||
<!-- ======= Footer ======= -->
|
||||
<footer id="footer">
|
||||
|
||||
<div class="footer-top">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-lg-9 content align-items-center justify-content-center">
|
||||
<h1 class="display-1">HiVakil API</h1>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-3 col-md-6 footer-links">
|
||||
<h4>Our Social Networks</h4>
|
||||
<p>Cras fermentum odio eu feugiat lide par naso tierra videa magna derita valies</p>
|
||||
<div class="social-links mt-3">
|
||||
<a href="#" class="twitter"><i class="bx bxl-twitter"></i></a>
|
||||
<a href="#" class="facebook"><i class="bx bxl-facebook"></i></a>
|
||||
<a href="#" class="instagram"><i class="bx bxl-instagram"></i></a>
|
||||
<a href="#" class="google-plus"><i class="bx bxl-skype"></i></a>
|
||||
<a href="#" class="linkedin"><i class="bx bxl-linkedin"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container footer-bottom clearfix">
|
||||
<div class="copyright">
|
||||
© Copyright <strong><span>HiVakil</span></strong>. All Rights Reserved
|
||||
</div>
|
||||
<div class="credits">
|
||||
<!-- All the links in the footer should remain intact. -->
|
||||
<!-- You can delete the links only if you purchased the pro version. -->
|
||||
<!-- Licensing information: https://bootstrapmade.com/license/ -->
|
||||
<!-- Purchase the pro version with working PHP/AJAX contact form: https://bootstrapmade.com/arsha-free-bootstrap-html-template-corporate/ -->
|
||||
Designed by <a href="https://mr-mohande3.ir/" style="color:white;">Amir Hossein Khademi (MR.MOHANDE3)</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer><!-- End Footer -->
|
||||
|
||||
<div id="preloader"></div>
|
||||
<a href="#" class="back-to-top d-flex align-items-center justify-content-center"><i class="bi bi-arrow-up-short"></i></a>
|
||||
|
||||
<!-- Vendor JS Files -->
|
||||
<script src="assets/vendor/aos/aos.js"></script>
|
||||
<script src="assets/vendor/glightbox/js/glightbox.min.js"></script>
|
||||
<script src="assets/vendor/waypoints/noframework.waypoints.js"></script>
|
||||
|
||||
<script src="assets/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- Template Main JS File -->
|
||||
<script src="assets/js/main.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,12 @@
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace NetinaCMS.Api.Views.Home
|
||||
{
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
public string Version = typeof(Program).Assembly.GetName().Version.ToString();
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
namespace NetinaCMS.Api.WebFramework.Bases;
|
||||
|
||||
public class ApiResultFactory
|
||||
{
|
||||
|
||||
}
|
||||
public class ApiResultFilterAttribute : ActionFilterAttribute
|
||||
{
|
||||
public override void OnResultExecuting(ResultExecutingContext context)
|
||||
{
|
||||
if (context.Result is OkObjectResult okObjectResult)
|
||||
{
|
||||
var apiResult = new ApiResult<object>(true, ApiResultStatusCode.Success, okObjectResult.Value);
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = okObjectResult.StatusCode };
|
||||
}
|
||||
else if (context.Result is OkResult okResult)
|
||||
{
|
||||
var apiResult = new ApiResult(true, ApiResultStatusCode.Success);
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = okResult.StatusCode };
|
||||
}
|
||||
else if (context.Result is BadRequestResult badRequestResult)
|
||||
{
|
||||
var apiResult = new ApiResult(false, ApiResultStatusCode.BadRequest);
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = badRequestResult.StatusCode };
|
||||
}
|
||||
else if (context.Result is BadRequestObjectResult badRequestObjectResult)
|
||||
{
|
||||
var message = badRequestObjectResult.Value.ToString();
|
||||
|
||||
if (badRequestObjectResult.Value is SerializableError errors)
|
||||
{
|
||||
var errorMessages = errors.SelectMany(p => (string[])p.Value).Distinct();
|
||||
message = string.Join(" | ", errorMessages);
|
||||
}
|
||||
|
||||
if (badRequestObjectResult.Value is ValidationProblemDetails problemDetails)
|
||||
{
|
||||
var errorMessages = problemDetails.Errors.Values.SelectMany(v => v);
|
||||
message = string.Join(" | ", errorMessages);
|
||||
}
|
||||
|
||||
var apiResult = new ApiResult(false, ApiResultStatusCode.BadRequest, message);
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = badRequestObjectResult.StatusCode };
|
||||
}
|
||||
else if (context.Result is ContentResult contentResult)
|
||||
{
|
||||
var apiResult = new ApiResult(true, ApiResultStatusCode.Success, contentResult.Content);
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = contentResult.StatusCode };
|
||||
}
|
||||
else if (context.Result is NotFoundResult notFoundResult)
|
||||
{
|
||||
var apiResult = new ApiResult(false, ApiResultStatusCode.NotFound);
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = notFoundResult.StatusCode };
|
||||
}
|
||||
else if (context.Result is NotFoundObjectResult notFoundObjectResult)
|
||||
{
|
||||
var apiResult = new ApiResult<object>(false, ApiResultStatusCode.NotFound, notFoundObjectResult.Value);
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = notFoundObjectResult.StatusCode };
|
||||
}
|
||||
else if (context.Result is ObjectResult objectResult && objectResult.StatusCode == null
|
||||
&& !(objectResult.Value is ApiResult))
|
||||
{
|
||||
var apiResult = new ApiResult<object>(true, ApiResultStatusCode.Success, objectResult.Value);
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = objectResult.StatusCode };
|
||||
}
|
||||
else if (context.Result is ObjectResult objectResultBad && objectResultBad.Value is ApiResult)
|
||||
{
|
||||
var apiResult = objectResultBad.Value as ApiResult;
|
||||
context.Result = new JsonResult(apiResult) { StatusCode = objectResultBad.StatusCode };
|
||||
}
|
||||
|
||||
base.OnResultExecuting(context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
namespace NetinaCMS.Api.WebFramework.Bases;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
|
||||
public class ClaimRequirement : AuthorizeAttribute, IAuthorizationFilter
|
||||
{
|
||||
private readonly string _claimsType;
|
||||
private readonly string _claimsValue;
|
||||
|
||||
public ClaimRequirement(string type,string value)
|
||||
{
|
||||
type = value;
|
||||
}
|
||||
|
||||
public void OnAuthorization(AuthorizationFilterContext context)
|
||||
{
|
||||
var user = context.HttpContext.User;
|
||||
var permissions = user.Claims?.Where(c => c.Type == _claimsType)?.ToList();
|
||||
if (permissions == null)
|
||||
{
|
||||
context.Result = new StatusCodeResult((int)HttpStatusCode.Forbidden);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool isAccepted = false;
|
||||
if (permissions.FirstOrDefault(p => p.Value == _claimsValue) != null)
|
||||
isAccepted = true;
|
||||
if (!isAccepted)
|
||||
context.Result = new StatusCodeResult((int)HttpStatusCode.Forbidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
namespace NetinaCMS.Api.WebFramework.Bases;
|
||||
|
||||
|
||||
public class CrudEndpoint<TEntity,TGetAllQuery,TGetOneQuery,TCreateCommand,TUpdateCommand,TDeleteCommand> where TEntity : ApiEntity, new()
|
||||
{
|
||||
private readonly string _endpointName;
|
||||
|
||||
public CrudEndpoint(string endpointName)
|
||||
{
|
||||
_endpointName = endpointName;
|
||||
}
|
||||
|
||||
public virtual void AddRoutes(IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.NewVersionedApi(_endpointName).MapGroup($"api/{_endpointName}");
|
||||
|
||||
group.MapGet("", GetAllAsync)
|
||||
.WithDisplayName("GetAll")
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
group.MapGet("{id}", GetAsync)
|
||||
.WithName("GetOne")
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
group.MapPost("", Post)
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
group.MapPut("", Put)
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
group.MapDelete("", Delete)
|
||||
.HasApiVersion(1.0);
|
||||
}
|
||||
|
||||
// GET:Get All Entity
|
||||
public virtual async Task<IResult> GetAllAsync(ISender sender , CancellationToken cancellationToken)
|
||||
{
|
||||
var res = sender.Send(Activator.CreateInstance<TGetAllQuery>(),cancellationToken);
|
||||
return TypedResults.Ok(res);
|
||||
}
|
||||
|
||||
// GET:Get An Entity By Id
|
||||
public async Task<IResult> GetAsync(Guid id, ISender sender, CancellationToken cancellationToken)
|
||||
=> TypedResults.Ok(sender.Send(Activator.CreateInstance<TGetOneQuery>()));
|
||||
|
||||
// POST:Create Entity
|
||||
public virtual async Task<IResult> Post([FromBody] TCreateCommand ent , ISender mediator , CancellationToken cancellationToken)
|
||||
{
|
||||
return TypedResults.Ok(await mediator.Send(ent, cancellationToken));
|
||||
}
|
||||
|
||||
// PUT:Update Entity
|
||||
public virtual async Task<IResult> Put([FromBody] TEntity ent , IRepositoryWrapper _repositoryWrapper, CancellationToken cancellationToken)
|
||||
{
|
||||
_repositoryWrapper.SetRepository<TEntity>().Update(ent);
|
||||
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
|
||||
return TypedResults.Ok();
|
||||
}
|
||||
|
||||
// DELETE:Delete Entity
|
||||
public virtual async Task<IResult> Delete(Guid id,IRepositoryWrapper _repositoryWrapper, CancellationToken cancellationToken)
|
||||
{
|
||||
var ent = await _repositoryWrapper.SetRepository<TEntity>().GetByIdAsync(cancellationToken, id);
|
||||
_repositoryWrapper.SetRepository<TEntity>().Delete(ent);
|
||||
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
|
||||
return TypedResults.Ok();
|
||||
}
|
||||
}
|
||||
|
||||
[ApiController]
|
||||
//[AllowAnonymous]
|
||||
[ApiResultFilter]
|
||||
[Route("api/v{version:apiVersion}/[controller]")] // api/v1/[controller]
|
||||
public class BaseController : ControllerBase
|
||||
{
|
||||
//public UserRepository UserRepository { get; set; } => property injection
|
||||
public bool UserIsAutheticated => HttpContext.User.Identity.IsAuthenticated;
|
||||
}
|
||||
|
||||
[Authorize(AuthenticationSchemes = "Bearer")]
|
||||
public class CrudController<TDto, TEntity> : BaseController
|
||||
where TDto : BaseDto<TDto, TEntity>, new()
|
||||
where TEntity : ApiEntity, new()
|
||||
{
|
||||
protected readonly IRepositoryWrapper _repositoryWrapper;
|
||||
|
||||
public CrudController(IRepositoryWrapper repositoryWrapper)
|
||||
{
|
||||
_repositoryWrapper = repositoryWrapper;
|
||||
}
|
||||
|
||||
// GET:Get All Entity
|
||||
[HttpGet]
|
||||
public virtual async Task<IActionResult> GetAllAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var projectTo = typeof(TDto).BaseType?.GetProperty("ProjectToDto")?.GetValue(null, null);
|
||||
if (projectTo != null)
|
||||
{
|
||||
var exprss = projectTo as Expression<Func<TEntity, TDto>>;
|
||||
var entites = await _repositoryWrapper
|
||||
.SetRepository<TEntity>()
|
||||
.TableNoTracking
|
||||
.Select(exprss)
|
||||
.ToListAsync(cancellationToken);
|
||||
return Ok(entites);
|
||||
}
|
||||
|
||||
throw new BaseApiException("ProjectTo Not Found");
|
||||
}
|
||||
|
||||
// GET:Get An Entity By Id
|
||||
[HttpGet("{id}")]
|
||||
public virtual async Task<IActionResult> GetAsync(string id, CancellationToken cancellationToken)
|
||||
{
|
||||
var ent = await _repositoryWrapper.SetRepository<TEntity>().GetByIdAsync(cancellationToken, id);
|
||||
var dto = ent.Adapt<TDto>();
|
||||
return Ok(dto);
|
||||
}
|
||||
|
||||
// POST:Add New Entity
|
||||
[HttpPost]
|
||||
public virtual async Task<IActionResult> PostOrginal([FromBody] TEntity ent, CancellationToken cancellationToken)
|
||||
{
|
||||
_repositoryWrapper.SetRepository<TEntity>().Add(ent);
|
||||
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
|
||||
return Ok(ent);
|
||||
}
|
||||
|
||||
// POST:Add New Entity By Dto
|
||||
[HttpPost("Dto")]
|
||||
public async Task<IActionResult> PostDto([FromBody] TDto dto, CancellationToken cancellationToken)
|
||||
{
|
||||
_repositoryWrapper
|
||||
.SetRepository<TEntity>()
|
||||
.Add(dto.ToEntity());
|
||||
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// PUT:Update Entity
|
||||
[HttpPut]
|
||||
public virtual async Task<IActionResult> Put([FromBody] TEntity ent, CancellationToken cancellationToken)
|
||||
{
|
||||
_repositoryWrapper
|
||||
.SetRepository<TEntity>()
|
||||
.Update(ent);
|
||||
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// DELETE:Delete Entity
|
||||
[HttpDelete]
|
||||
[Route("{id:int}")]
|
||||
public virtual async Task<IActionResult> Delete(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var ent = await _repositoryWrapper
|
||||
.SetRepository<TEntity>()
|
||||
.GetByIdAsync(cancellationToken, id);
|
||||
_repositoryWrapper.SetRepository<TEntity>().Delete(ent);
|
||||
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize(AuthenticationSchemes = "Bearer")]
|
||||
public class CrudController<TEntity> : BaseController
|
||||
where TEntity : ApiEntity, new()
|
||||
{
|
||||
protected readonly IRepositoryWrapper _repositoryWrapper;
|
||||
|
||||
public CrudController(IRepositoryWrapper repositoryWrapper)
|
||||
{
|
||||
_repositoryWrapper = repositoryWrapper;
|
||||
}
|
||||
|
||||
// GET:Get All Entity
|
||||
[HttpGet]
|
||||
public virtual async Task<IActionResult> GetAllAsync()
|
||||
{
|
||||
return Ok(await _repositoryWrapper.SetRepository<TEntity>().TableNoTracking.ToListAsync());
|
||||
}
|
||||
|
||||
// GET:Get An Entity By Id
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var ent = await _repositoryWrapper.SetRepository<TEntity>().GetByIdAsync(cancellationToken, id);
|
||||
return Ok(ent);
|
||||
}
|
||||
|
||||
// POST:Add New Entity
|
||||
[HttpPost]
|
||||
public virtual async Task<IActionResult> Post([FromBody] TEntity ent, CancellationToken cancellationToken)
|
||||
{
|
||||
_repositoryWrapper.SetRepository<TEntity>().Add(ent);
|
||||
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
|
||||
return Ok(ent);
|
||||
}
|
||||
|
||||
// PUT:Update Entity
|
||||
[HttpPut]
|
||||
public virtual async Task<IActionResult> Put([FromBody] TEntity ent, CancellationToken cancellationToken)
|
||||
{
|
||||
_repositoryWrapper.SetRepository<TEntity>().Update(ent);
|
||||
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// DELETE:Delete Entity
|
||||
[HttpDelete("{id}")]
|
||||
public virtual async Task<IActionResult> Delete(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var ent = await _repositoryWrapper.SetRepository<TEntity>().GetByIdAsync(cancellationToken, id);
|
||||
_repositoryWrapper.SetRepository<TEntity>().Delete(ent);
|
||||
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
|
||||
return Ok();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace NetinaCMS.Api.WebFramework.Configurations;
|
||||
|
||||
public class ConfigureJwtBearerOptions : IPostConfigureOptions<JwtBearerOptions>
|
||||
{
|
||||
public void PostConfigure(string name, JwtBearerOptions options)
|
||||
{
|
||||
var originalOnMessageReceived = options.Events.OnMessageReceived;
|
||||
options.Events.OnMessageReceived = async context =>
|
||||
{
|
||||
await originalOnMessageReceived(context);
|
||||
|
||||
if (string.IsNullOrEmpty(context.Token))
|
||||
{
|
||||
var accessToken = context.Request.Query["access_token"];
|
||||
var path = context.HttpContext.Request.Path;
|
||||
|
||||
if (!string.IsNullOrEmpty(accessToken)) context.Token = accessToken;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using Serilog.Events;
|
||||
using Serilog.Sinks.SystemConsole.Themes;
|
||||
|
||||
namespace NetinaCMS.Api.WebFramework.Configurations;
|
||||
|
||||
public static class LoggerConfig
|
||||
{
|
||||
public static void ConfigureSerilog()
|
||||
{
|
||||
var logName = $"{DirectoryAddress.Logs}/Log_Server_.log";
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.Enrich.FromLogContext()
|
||||
.WriteTo.Console(theme: AnsiConsoleTheme.Literate)
|
||||
.WriteTo.Sentry(o =>
|
||||
{
|
||||
o.MinimumEventLevel = LogEventLevel.Error;
|
||||
o.Dsn = "https://592b7fbb29464442a8e996247abe857f@watcher.igarson.app/7";
|
||||
})
|
||||
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", Serilog.Events.LogEventLevel.Error)
|
||||
.CreateLogger();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
namespace NetinaCMS.Api.WebFramework.Configurations;
|
||||
|
||||
public class PersianIdentityErrorDescriber : IdentityErrorDescriber
|
||||
{
|
||||
public override IdentityError DefaultError()
|
||||
{
|
||||
return new IdentityError { Code = nameof(DefaultError), Description = "ارور ناشناخته ای رخ داده است" };
|
||||
}
|
||||
|
||||
public override IdentityError ConcurrencyFailure()
|
||||
{
|
||||
return new IdentityError
|
||||
{ Code = nameof(ConcurrencyFailure), Description = "در درخواست شما تداخلی ایجاد شده است" };
|
||||
}
|
||||
|
||||
public override IdentityError PasswordMismatch()
|
||||
{
|
||||
return new IdentityError { Code = nameof(PasswordMismatch), Description = "رمز عبور اشتباه است" };
|
||||
}
|
||||
|
||||
public override IdentityError InvalidToken()
|
||||
{
|
||||
return new IdentityError { Code = nameof(InvalidToken), Description = "توکن ارسالی اشتباه است" };
|
||||
}
|
||||
|
||||
public override IdentityError LoginAlreadyAssociated()
|
||||
{
|
||||
return new IdentityError
|
||||
{ Code = nameof(LoginAlreadyAssociated), Description = "یوزری با این مشخصات در حال حاضر لاگین کرده است" };
|
||||
}
|
||||
|
||||
public override IdentityError InvalidUserName(string userName)
|
||||
{
|
||||
return new IdentityError
|
||||
{
|
||||
Code = nameof(InvalidUserName),
|
||||
Description = $"یوزر نیم '{userName}' صحیح نمی باشد فقط می توانید از حروف و اعداد استفاده کنید"
|
||||
};
|
||||
}
|
||||
|
||||
public override IdentityError InvalidEmail(string email)
|
||||
{
|
||||
return new IdentityError { Code = nameof(InvalidEmail), Description = $"ایمیل '{email}' صحیح نمی باشد" };
|
||||
}
|
||||
|
||||
public override IdentityError DuplicateUserName(string userName)
|
||||
{
|
||||
return new IdentityError
|
||||
{
|
||||
Code = nameof(DuplicateUserName),
|
||||
Description = $"یوزرنیم '{userName}' قبلا توسط اکانت دیگری استفاده شده است"
|
||||
};
|
||||
}
|
||||
|
||||
public override IdentityError DuplicateEmail(string email)
|
||||
{
|
||||
return new IdentityError
|
||||
{ Code = nameof(DuplicateEmail), Description = $"ایمیل '{email}' قبل استفاده شده است" };
|
||||
}
|
||||
|
||||
public override IdentityError InvalidRoleName(string role)
|
||||
{
|
||||
return new IdentityError { Code = nameof(InvalidRoleName), Description = $"نقش '{role}' موجود نمی باشد" };
|
||||
}
|
||||
|
||||
public override IdentityError DuplicateRoleName(string role)
|
||||
{
|
||||
return new IdentityError
|
||||
{ Code = nameof(DuplicateRoleName), Description = $"نقش '{role}' قبلا برای این کاربر استفاده شده است" };
|
||||
}
|
||||
|
||||
public override IdentityError UserAlreadyHasPassword()
|
||||
{
|
||||
return new IdentityError
|
||||
{ Code = nameof(UserAlreadyHasPassword), Description = "کاربر قبلا رمز عبوری را استفاده کرده است" };
|
||||
}
|
||||
|
||||
public override IdentityError UserLockoutNotEnabled()
|
||||
{
|
||||
return new IdentityError { Code = nameof(UserLockoutNotEnabled), Description = "کاربر مورد نظر قفل شده است" };
|
||||
}
|
||||
|
||||
public override IdentityError UserAlreadyInRole(string role)
|
||||
{
|
||||
return new IdentityError
|
||||
{ Code = nameof(UserAlreadyInRole), Description = "نشق مورد نظر برای این کاربر استفاده شده است" };
|
||||
}
|
||||
|
||||
public override IdentityError UserNotInRole(string role)
|
||||
{
|
||||
return new IdentityError { Code = nameof(UserNotInRole), Description = $"کاربر مورد نظر در نقش '{role}' نیست" };
|
||||
}
|
||||
|
||||
public override IdentityError PasswordTooShort(int length)
|
||||
{
|
||||
return new IdentityError
|
||||
{ Code = nameof(PasswordTooShort), Description = $"پسورد حداقل باید {length} کاراکتر باشد" };
|
||||
}
|
||||
|
||||
public override IdentityError PasswordRequiresNonAlphanumeric()
|
||||
{
|
||||
return new IdentityError
|
||||
{
|
||||
Code = nameof(PasswordRequiresNonAlphanumeric),
|
||||
Description = "رمز عبور باید حداقل یک کاراکتر غیر عددی داشته باشد"
|
||||
};
|
||||
}
|
||||
|
||||
public override IdentityError PasswordRequiresDigit()
|
||||
{
|
||||
return new IdentityError
|
||||
{
|
||||
Code = nameof(PasswordRequiresDigit), Description = "پسور مورد نظر باید حداقل یک عدد داشته باشد ('0'-'9')"
|
||||
};
|
||||
}
|
||||
|
||||
public override IdentityError PasswordRequiresLower()
|
||||
{
|
||||
return new IdentityError
|
||||
{
|
||||
Code = nameof(PasswordRequiresLower),
|
||||
Description = "پسورد مورد نظر باید حداقل یکی از حروف ('a'-'z') به صورت کوچک داشته باشد"
|
||||
};
|
||||
}
|
||||
|
||||
public override IdentityError PasswordRequiresUpper()
|
||||
{
|
||||
return new IdentityError
|
||||
{
|
||||
Code = nameof(PasswordRequiresUpper),
|
||||
Description = "پسورد مورد نظر باید حداقل یکی از حروف ('A'-'Z') به صورت بزرگ داشته باشد"
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
using Marten;
|
||||
|
||||
namespace NetinaCMS.Api.WebFramework.Configurations;
|
||||
|
||||
public static class ServiceExtensions
|
||||
{
|
||||
public static void AddIpRateLimit(this IServiceCollection services, IConfigurationRoot configuration)
|
||||
{
|
||||
|
||||
//load general configuration from appsettings.json
|
||||
services.Configure<IpRateLimitOptions>(configuration.GetSection("IpRateLimiting"));
|
||||
|
||||
//load ip rules from appsettings.json
|
||||
services.Configure<IpRateLimitPolicies>(configuration.GetSection("IpRateLimitPolicies"));
|
||||
|
||||
// inject counter and rules stores
|
||||
//services.AddInMemoryRateLimiting();
|
||||
services.AddSingleton<IIpPolicyStore, DistributedCacheIpPolicyStore>();
|
||||
services.AddSingleton<IRateLimitCounterStore, DistributedCacheRateLimitCounterStore>();
|
||||
services.AddDistributedRateLimiting<AsyncKeyLockProcessingStrategy>();
|
||||
services.AddDistributedRateLimiting<RedisProcessingStrategy>();
|
||||
services.AddRedisRateLimiting();
|
||||
|
||||
// configuration (resolvers, counter key builders)
|
||||
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
|
||||
}
|
||||
public static void AddCustomStackExchangeRedis(this IServiceCollection serviceCollection, SiteSettings siteSettings)
|
||||
{
|
||||
serviceCollection.AddStackExchangeRedisExtensions<NewtonsoftSerializer>(options =>
|
||||
{
|
||||
return new List<RedisConfiguration>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Hosts = new[]
|
||||
{
|
||||
new RedisHost
|
||||
{
|
||||
Port = siteSettings.MasterRedisConfiguration.Port,
|
||||
Host = siteSettings.MasterRedisConfiguration.Host
|
||||
}
|
||||
},
|
||||
Password = siteSettings.MasterRedisConfiguration.Password,
|
||||
Ssl = false
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public static void AddCustomDbContext(this IServiceCollection serviceCollection, IConfigurationRoot Configuration)
|
||||
{
|
||||
serviceCollection.AddDbContextFactory<ApplicationContext>(options =>
|
||||
{
|
||||
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
|
||||
options.UseNpgsql(Configuration.GetConnectionString("Postgres"), b => b.MigrationsAssembly("HamyanEdalat.Repository"))
|
||||
.UseProjectAssembly(typeof(ApplicationUser).Assembly);
|
||||
|
||||
//options.EnableServiceProviderCaching(true);
|
||||
}).BuildServiceProvider();
|
||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||
}
|
||||
|
||||
public static void AddMarten(this IServiceCollection serviceCollection, IConfigurationRoot configuration , IWebHostEnvironment environment)
|
||||
{
|
||||
serviceCollection.AddMarten(options =>
|
||||
{
|
||||
// Establish the connection string to your Marten database
|
||||
options.Connection(configuration.GetConnectionString("MartenDB")!);
|
||||
|
||||
// If we're running in development mode, let Marten just take care
|
||||
// of all necessary schema building and patching behind the scenes
|
||||
if (environment.IsDevelopment())
|
||||
{
|
||||
options.AutoCreateSchemaObjects = AutoCreate.All;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void AddCustomResponseCompression(this IServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection.Configure<GzipCompressionProviderOptions>(options =>
|
||||
options.Level = CompressionLevel.Fastest);
|
||||
serviceCollection.AddResponseCompression(options =>
|
||||
{
|
||||
options.Providers.Add<GzipCompressionProvider>();
|
||||
options.EnableForHttps = true;
|
||||
});
|
||||
}
|
||||
|
||||
public static void AddCustomCores(this IServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection.AddCors(options => options.AddPolicy("CorsPolicy",
|
||||
builder =>
|
||||
{
|
||||
builder.AllowAnyMethod()
|
||||
.SetPreflightMaxAge(TimeSpan.FromHours(24))
|
||||
.WithExposedHeaders("Access-control-allow-origins")
|
||||
.AllowAnyHeader()
|
||||
.SetIsOriginAllowed(_ => true)
|
||||
.AllowCredentials();
|
||||
}));
|
||||
}
|
||||
|
||||
public static void AddCustomController(this IServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection.AddControllers(options => { options.Filters.Add(new AuthorizeFilter()); })
|
||||
.AddControllersAsServices()
|
||||
.AddNewtonsoftJson(options =>
|
||||
{
|
||||
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||
//options.SerializerSettings.ContractResolver = new DefaultContractResolver();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static void AddCustomMvc(this IServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection
|
||||
.AddMvc()
|
||||
.AddNewtonsoftJson(options =>
|
||||
{
|
||||
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||
});
|
||||
}
|
||||
|
||||
public static void AddCustomAuthorization(this IServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection.AddAuthorization();
|
||||
}
|
||||
|
||||
public static void AddJwtCustomAuthentication(this IServiceCollection serviceCollection, JwtSettings jwtSettings)
|
||||
{
|
||||
serviceCollection.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
var secretKey = Encoding.UTF8.GetBytes(jwtSettings.SecretKey);
|
||||
var validateParammetrs = new TokenValidationParameters
|
||||
{
|
||||
ClockSkew = TimeSpan.Zero,
|
||||
RequireSignedTokens = true,
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(secretKey),
|
||||
RequireExpirationTime = true,
|
||||
ValidateLifetime = true,
|
||||
ValidateAudience = true,
|
||||
ValidAudience = jwtSettings.Audience,
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = jwtSettings.Issuer
|
||||
};
|
||||
options.RequireHttpsMetadata = true;
|
||||
options.SaveToken = true;
|
||||
options.TokenValidationParameters = validateParammetrs;
|
||||
options.Events = new JwtBearerEvents
|
||||
{
|
||||
OnMessageReceived = context =>
|
||||
{
|
||||
var accessToken = context.Request.Query["access_token"];
|
||||
|
||||
// If the request is for our hub...
|
||||
var path = context.HttpContext.Request.Path;
|
||||
if (!string.IsNullOrEmpty(accessToken))
|
||||
// Read the token out of the query string
|
||||
context.Token = accessToken.ToString();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public static void AddCustomIdentity(this IServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection.AddIdentity<ApplicationUser, ApplicationRole>(options =>
|
||||
{
|
||||
options.Password.RequireLowercase = false;
|
||||
options.Password.RequireUppercase = false;
|
||||
options.Password.RequireDigit = false;
|
||||
options.Password.RequireNonAlphanumeric = false;
|
||||
options.User.RequireUniqueEmail = false;
|
||||
}).AddEntityFrameworkStores<ApplicationContext>()
|
||||
.AddDefaultTokenProviders()
|
||||
.AddErrorDescriber<PersianIdentityErrorDescriber>();
|
||||
|
||||
}
|
||||
|
||||
public static void AddCustomApiVersioning(this IServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection.AddApiVersioning(options =>
|
||||
{
|
||||
options.AssumeDefaultVersionWhenUnspecified = true;
|
||||
options.DefaultApiVersion = new ApiVersion(1, 0);
|
||||
options.ApiVersionReader = new HeaderApiVersionReader("api-version");
|
||||
options.ReportApiVersions = true;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
using Refit;
|
||||
|
||||
namespace NetinaCMS.Api.WebFramework.MiddleWares;
|
||||
|
||||
public static class ExceptionHandlerMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseExceptionHandlerMiddleware(this IApplicationBuilder applicationBuilder)
|
||||
{
|
||||
return applicationBuilder.UseMiddleware<ExceptionHandlerMiddleware>();
|
||||
}
|
||||
}
|
||||
|
||||
public class ExceptionHandlerMiddleware
|
||||
{
|
||||
private readonly IWebHostEnvironment _env;
|
||||
private readonly ILogger<ExceptionHandlerMiddleware> _logger;
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public ExceptionHandlerMiddleware(
|
||||
RequestDelegate next,
|
||||
IWebHostEnvironment env,
|
||||
ILogger<ExceptionHandlerMiddleware> logger)
|
||||
{
|
||||
_next = next;
|
||||
_env = env;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
string message = null;
|
||||
var httpStatusCode = HttpStatusCode.InternalServerError;
|
||||
var apiStatusCode = ApiResultStatusCode.ServerError;
|
||||
|
||||
try
|
||||
{
|
||||
await _next(context);
|
||||
}
|
||||
catch (BaseApiException exception)
|
||||
{
|
||||
_logger.LogError(exception, exception.Message);
|
||||
httpStatusCode = exception.HttpStatusCode;
|
||||
apiStatusCode = exception.ApiStatusCode;
|
||||
|
||||
if (_env.IsDevelopment())
|
||||
{
|
||||
var dic = new Dictionary<string, string>
|
||||
{
|
||||
["Exception"] = exception.Message,
|
||||
["StackTrace"] = exception.StackTrace
|
||||
};
|
||||
if (exception.InnerException != null)
|
||||
{
|
||||
dic.Add("InnerException.Exception", exception.InnerException.Message);
|
||||
dic.Add("InnerException.StackTrace", exception.InnerException.StackTrace);
|
||||
}
|
||||
|
||||
if (exception.AdditionalData != null)
|
||||
dic.Add("AdditionalData", JsonConvert.SerializeObject(exception.AdditionalData));
|
||||
var contractResolver = new DefaultContractResolver
|
||||
{
|
||||
NamingStrategy = new CamelCaseNamingStrategy()
|
||||
};
|
||||
|
||||
message = JsonConvert.SerializeObject(dic, new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = contractResolver,
|
||||
Formatting = Formatting.Indented
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
message = exception.Message;
|
||||
}
|
||||
|
||||
if (exception.AdditionalData == null)
|
||||
await WriteToResponseAsync();
|
||||
else
|
||||
await WriteToResponseWithObjectAsync(exception.AdditionalData);
|
||||
}
|
||||
catch (SecurityTokenExpiredException exception)
|
||||
{
|
||||
_logger.LogError(exception, exception.Message);
|
||||
SetUnAuthorizeResponse(exception);
|
||||
await WriteToResponseAsync();
|
||||
}
|
||||
catch (UnauthorizedAccessException exception)
|
||||
{
|
||||
_logger.LogError(exception, exception.Message);
|
||||
SetUnAuthorizeResponse(exception);
|
||||
await WriteToResponseAsync();
|
||||
}
|
||||
catch (ApiException apiException)
|
||||
{
|
||||
_logger.LogError(apiException, apiException.Message);
|
||||
|
||||
httpStatusCode = HttpStatusCode.InternalServerError;
|
||||
apiStatusCode = ApiResultStatusCode.RefitError;
|
||||
|
||||
message = apiException.Message;
|
||||
|
||||
await WriteToResponseAsync();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, exception.Message);
|
||||
|
||||
if (_env.IsDevelopment())
|
||||
{
|
||||
if (exception?.InnerException?.Message != null)
|
||||
{
|
||||
var dic = new Dictionary<string, string>
|
||||
{
|
||||
["Exception"] = exception.Message,
|
||||
["InnerException"] = exception?.InnerException?.Message,
|
||||
["StackTrace"] = exception?.StackTrace
|
||||
};
|
||||
message = JsonConvert.SerializeObject(dic);
|
||||
}
|
||||
}
|
||||
|
||||
message = exception.Message;
|
||||
await WriteToResponseAsync();
|
||||
}
|
||||
|
||||
async Task WriteToResponseAsync()
|
||||
{
|
||||
if (context.Response.HasStarted)
|
||||
throw new InvalidOperationException("The response has already started, the http status code middleware will not be executed.");
|
||||
|
||||
var result = new ApiResult(false, apiStatusCode, message);
|
||||
|
||||
var contractResolver = new DefaultContractResolver
|
||||
{
|
||||
NamingStrategy = new CamelCaseNamingStrategy()
|
||||
};
|
||||
|
||||
var json = JsonConvert.SerializeObject(result, new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = contractResolver,
|
||||
Formatting = Formatting.Indented
|
||||
});
|
||||
|
||||
context.Response.StatusCode = (int)httpStatusCode;
|
||||
context.Response.ContentType = "application/json";
|
||||
await context.Response.WriteAsync(json);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async Task WriteToResponseWithObjectAsync(object additionalData)
|
||||
{
|
||||
if (context.Response.HasStarted)
|
||||
throw new InvalidOperationException(
|
||||
"The response has already started, the http status code middleware will not be executed.");
|
||||
|
||||
var result = new ApiResult<object>(false, apiStatusCode, additionalData, message);
|
||||
|
||||
var contractResolver = new DefaultContractResolver
|
||||
{
|
||||
NamingStrategy = new CamelCaseNamingStrategy()
|
||||
};
|
||||
|
||||
var json = JsonConvert.SerializeObject(result, new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = contractResolver,
|
||||
Formatting = Formatting.Indented
|
||||
});
|
||||
|
||||
context.Response.StatusCode = (int)httpStatusCode;
|
||||
context.Response.ContentType = "application/json";
|
||||
await context.Response.WriteAsync(json);
|
||||
}
|
||||
|
||||
void SetUnAuthorizeResponse(Exception exception)
|
||||
{
|
||||
httpStatusCode = HttpStatusCode.Unauthorized;
|
||||
apiStatusCode = ApiResultStatusCode.UnAuthorized;
|
||||
|
||||
if (_env.IsDevelopment())
|
||||
{
|
||||
var dic = new Dictionary<string, string>
|
||||
{
|
||||
["Exception"] = exception.Message,
|
||||
["StackTrace"] = exception.StackTrace
|
||||
};
|
||||
if (exception is SecurityTokenExpiredException tokenException)
|
||||
dic.Add("Expires", tokenException.Expires.ToString());
|
||||
|
||||
message = JsonConvert.SerializeObject(dic);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
JwtSecurityToken ReadJwtToken(bool fromHeader = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (fromHeader)
|
||||
{
|
||||
var stream = context.Request.Headers.Values.First(v => v.FirstOrDefault().Contains("Bearer"))
|
||||
.FirstOrDefault();
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var jsonToken = handler.ReadToken(stream.Split(" ").Last());
|
||||
return jsonToken as JwtSecurityToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
string stream = context.Request.Query["access_token"];
|
||||
;
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var jsonToken = handler.ReadToken(stream.Split(" ").Last());
|
||||
return jsonToken as JwtSecurityToken;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new BaseApiException(ApiResultStatusCode.UnAuthorized, e.Message + " Jwt is wrong",
|
||||
HttpStatusCode.Unauthorized);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
using System.Diagnostics;
|
||||
|
||||
namespace NetinaCMS.Api.WebFramework.MiddleWares;
|
||||
|
||||
public static class PerformanceMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UsePerformanceMiddlewar(this IApplicationBuilder applicationBuilder)
|
||||
{
|
||||
return applicationBuilder.UseMiddleware<PerformanceMiddleware>();
|
||||
}
|
||||
}
|
||||
|
||||
public class PerformanceMiddleware
|
||||
{
|
||||
private readonly ILogger<ExceptionHandlerMiddleware> _logger;
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly Stopwatch _timer;
|
||||
|
||||
public PerformanceMiddleware(
|
||||
RequestDelegate next,
|
||||
ILogger<ExceptionHandlerMiddleware> logger)
|
||||
{
|
||||
_next = next;
|
||||
_logger = logger;
|
||||
_timer = new Stopwatch();
|
||||
}
|
||||
|
||||
public async System.Threading.Tasks.Task Invoke(HttpContext context)
|
||||
{
|
||||
_timer.Start();
|
||||
await _next(context);
|
||||
_timer.Stop();
|
||||
|
||||
var elapsedMilliseconds = _timer.ElapsedMilliseconds;
|
||||
_logger.LogWarning($"REQUEST TIMER : {elapsedMilliseconds}");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,283 @@
|
|||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Pluralize.NET;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Swashbuckle.AspNetCore.SwaggerUI;
|
||||
|
||||
namespace NetinaCMS.Api.WebFramework.Swagger;
|
||||
|
||||
public static class SwaggerConfiguration
|
||||
{
|
||||
public static void AddCustomSwagger(this IServiceCollection services,string baseUrl)
|
||||
{
|
||||
services.AddSwaggerGen(options =>
|
||||
{
|
||||
//var xmlDuc = Path.Combine(AppContext.BaseDirectory, "swaggerApi.xml");
|
||||
//options.IncludeXmlComments(xmlDuc,true);
|
||||
options.SwaggerDoc("v1",
|
||||
new OpenApiInfo
|
||||
{
|
||||
Version = "v1",
|
||||
Title = "iGarson Api Dacument",
|
||||
Description = "iGarson api for clients that wana use",
|
||||
License = new OpenApiLicense { Name = "Vira Safir Fanavar " },
|
||||
Contact = new OpenApiContact
|
||||
{
|
||||
Name = "Amir Hossein Khademi",
|
||||
Email = "avvampier@gmail.com",
|
||||
Url = new Uri("http://amir-khademi.ir/")
|
||||
}
|
||||
});
|
||||
options.EnableAnnotations();
|
||||
options.DescribeAllParametersInCamelCase();
|
||||
options.IgnoreObsoleteActions();
|
||||
|
||||
//#region Versioning
|
||||
|
||||
//// Remove version parameter from all Operations
|
||||
//options.OperationFilter<RemoveVersionParameters>();
|
||||
|
||||
////set version "api/v{version}/[controller]" from current swagger doc verion
|
||||
//options.DocumentFilter<SetVersionInPaths>();
|
||||
|
||||
////Seperate and categorize end-points by doc version
|
||||
//options.DocInclusionPredicate((version, desc) =>
|
||||
//{
|
||||
// if (!desc.TryGetMethodInfo(out var methodInfo)) return false;
|
||||
// var versions = methodInfo.DeclaringType
|
||||
// .GetCustomAttributes(true)
|
||||
// .OfType<ApiVersionAttribute>()
|
||||
// .SelectMany(attr => attr.Versions)
|
||||
// .ToList();
|
||||
|
||||
// return versions.Any(v => $"v{v.ToString()}" == version);
|
||||
//});
|
||||
|
||||
//#endregion
|
||||
|
||||
#region Security
|
||||
|
||||
var url = $"{baseUrl}/api/auth/login/swagger";
|
||||
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
||||
{
|
||||
Type = SecuritySchemeType.OAuth2,
|
||||
Scheme = "Bearer",
|
||||
Name = "Bearer",
|
||||
Flows = new OpenApiOAuthFlows
|
||||
{
|
||||
Password = new OpenApiOAuthFlow
|
||||
{
|
||||
TokenUrl = new Uri(url)
|
||||
}
|
||||
}
|
||||
});
|
||||
options.OperationFilter<UnauthorizedResponsesOperationFilter>(true, "Bearer");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Customize
|
||||
|
||||
options.OperationFilter<ApplySummariesOperationFilter>();
|
||||
|
||||
#endregion
|
||||
});
|
||||
}
|
||||
|
||||
public static void UseCustomSwagger(this IApplicationBuilder app,string baseUrl)
|
||||
{
|
||||
app.UseSwagger(options =>
|
||||
{
|
||||
options.SerializeAsV2 = true;
|
||||
});
|
||||
|
||||
app.UseSwaggerUI(options =>
|
||||
{
|
||||
options.InjectStylesheet("/assets/swagger-ui/x3/theme-flattop.css");
|
||||
options.DocExpansion(DocExpansion.None);
|
||||
// Display
|
||||
options.DefaultModelExpandDepth(2);
|
||||
options.DefaultModelRendering(ModelRendering.Model);
|
||||
options.DefaultModelsExpandDepth(-1);
|
||||
options.DisplayOperationId();
|
||||
options.DisplayRequestDuration();
|
||||
options.EnableDeepLinking();
|
||||
options.EnableFilter();
|
||||
options.ShowExtensions();
|
||||
|
||||
options.OAuthUseBasicAuthenticationWithAccessCodeGrant();
|
||||
options.SwaggerEndpoint($"{baseUrl}/swagger/v1/swagger.json", "V1 Docs");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class RemoveVersionParameters : IOperationFilter
|
||||
{
|
||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||
{
|
||||
// Remove version parameter from all Operations
|
||||
var versionParameter = operation.Parameters.SingleOrDefault(p => p.Name == "version");
|
||||
if (versionParameter != null)
|
||||
operation.Parameters.Remove(versionParameter);
|
||||
}
|
||||
}
|
||||
|
||||
public class SetVersionInPaths : IDocumentFilter
|
||||
{
|
||||
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
|
||||
{
|
||||
if (swaggerDoc == null)
|
||||
throw new ArgumentNullException(nameof(swaggerDoc));
|
||||
|
||||
var replacements = new OpenApiPaths();
|
||||
|
||||
foreach (var (key, value) in swaggerDoc.Paths)
|
||||
replacements.Add(key.Replace("v{version}", swaggerDoc.Info.Version, StringComparison.InvariantCulture),
|
||||
value);
|
||||
|
||||
swaggerDoc.Paths = replacements;
|
||||
}
|
||||
}
|
||||
|
||||
public class UnauthorizedResponsesOperationFilter : IOperationFilter
|
||||
{
|
||||
private readonly bool includeUnauthorizedAndForbiddenResponses;
|
||||
private readonly string schemeName;
|
||||
|
||||
public UnauthorizedResponsesOperationFilter(bool includeUnauthorizedAndForbiddenResponses,
|
||||
string schemeName = "Bearer")
|
||||
{
|
||||
this.includeUnauthorizedAndForbiddenResponses = includeUnauthorizedAndForbiddenResponses;
|
||||
this.schemeName = schemeName;
|
||||
}
|
||||
|
||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||
{
|
||||
var filters = context.ApiDescription.ActionDescriptor.FilterDescriptors;
|
||||
|
||||
var hasAnynomousEndPoint = context.ApiDescription.ActionDescriptor.EndpointMetadata.Any(e =>
|
||||
e.GetType() == typeof(AllowAnonymousAttribute));
|
||||
|
||||
//var hasAnonymous = filters.Any(p => p.Filter is AllowAnonymousFilter);
|
||||
if (hasAnynomousEndPoint)
|
||||
return;
|
||||
|
||||
/*var hasAuthorize = filters.Any(p => p.Filter is AuthorizeFilter);
|
||||
if (!hasAuthorize)
|
||||
return;*/
|
||||
|
||||
if (includeUnauthorizedAndForbiddenResponses)
|
||||
{
|
||||
operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" });
|
||||
operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" });
|
||||
}
|
||||
|
||||
operation.Security.Add(new OpenApiSecurityRequirement
|
||||
{
|
||||
{
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
Type = ReferenceType.SecurityScheme,
|
||||
Id = "Bearer"
|
||||
}
|
||||
},
|
||||
new string[] { }
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class ApplySummariesOperationFilter : IOperationFilter
|
||||
{
|
||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||
{
|
||||
var controllerActionDescriptor = context.ApiDescription.ActionDescriptor as ControllerActionDescriptor;
|
||||
if (controllerActionDescriptor == null)
|
||||
{
|
||||
operation.Summary = context.ApiDescription.ActionDescriptor.DisplayName;
|
||||
return;
|
||||
}
|
||||
|
||||
var pluralizer = new Pluralizer();
|
||||
|
||||
var actionName = controllerActionDescriptor.ActionName;
|
||||
var singularizeName = pluralizer.Singularize(controllerActionDescriptor.ControllerName);
|
||||
var pluralizeName = pluralizer.Pluralize(controllerActionDescriptor.DisplayName);
|
||||
|
||||
var parameterCount = operation.Parameters.Where(p => p.Name != "version" && p.Name != "api-version").Count();
|
||||
|
||||
if (IsGetAllAction())
|
||||
{
|
||||
if (!operation.Summary.HasValue())
|
||||
operation.Summary = $"Returns all {pluralizeName}";
|
||||
}
|
||||
else if (IsActionName("Post", "Create"))
|
||||
{
|
||||
if (!operation.Summary.HasValue())
|
||||
operation.Summary = $"Creates a {singularizeName}";
|
||||
|
||||
if (operation.Parameters.Count > 0 && !operation.Parameters[0].Description.HasValue())
|
||||
operation.Parameters[0].Description = $"A {singularizeName} representation";
|
||||
}
|
||||
else if (IsActionName("Read", "Get"))
|
||||
{
|
||||
if (!operation.Summary.HasValue())
|
||||
operation.Summary = $"Retrieves a {singularizeName} by unique id";
|
||||
|
||||
if (operation.Parameters.Count > 0 && !operation.Parameters[0].Description.HasValue())
|
||||
operation.Parameters[0].Description = $"a unique id for the {singularizeName}";
|
||||
}
|
||||
else if (IsActionName("Put", "Edit", "Update"))
|
||||
{
|
||||
if (!operation.Summary.HasValue())
|
||||
operation.Summary = $"Updates a {singularizeName} by unique id";
|
||||
|
||||
//if (!operation.Parameters[0].OrderDescription.HasValue())
|
||||
// operation.Parameters[0].OrderDescription = $"A unique id for the {singularizeName}";
|
||||
|
||||
if (operation.Parameters.Count > 0 && !operation.Parameters[0].Description.HasValue())
|
||||
operation.Parameters[0].Description = $"A {singularizeName} representation";
|
||||
}
|
||||
else if (IsActionName("Delete", "Remove"))
|
||||
{
|
||||
if (!operation.Summary.HasValue())
|
||||
operation.Summary = $"Deletes a {singularizeName} by unique id";
|
||||
|
||||
if (operation.Parameters.Count > 0 && !operation.Parameters[0].Description.HasValue())
|
||||
operation.Parameters[0].Description = $"A unique id for the {singularizeName}";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!operation.Summary.HasValue())
|
||||
operation.Summary = $"{actionName} {pluralizeName}";
|
||||
}
|
||||
|
||||
#region Local Functions
|
||||
|
||||
bool IsGetAllAction()
|
||||
{
|
||||
foreach (var name in new[] { "Get", "Read", "Select" })
|
||||
if (actionName.Equals(name, StringComparison.OrdinalIgnoreCase) && parameterCount == 0 ||
|
||||
actionName.Equals($"{name}All", StringComparison.OrdinalIgnoreCase) ||
|
||||
actionName.Equals($"{name}{pluralizeName}", StringComparison.OrdinalIgnoreCase) ||
|
||||
actionName.Equals($"{name}All{singularizeName}", StringComparison.OrdinalIgnoreCase) ||
|
||||
actionName.Equals($"{name}All{pluralizeName}", StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsActionName(params string[] names)
|
||||
{
|
||||
foreach (var name in names)
|
||||
if (actionName.Contains(name, StringComparison.OrdinalIgnoreCase) ||
|
||||
actionName.Contains($"{name}ById", StringComparison.OrdinalIgnoreCase) ||
|
||||
actionName.Contains($"{name}{singularizeName}", StringComparison.OrdinalIgnoreCase) ||
|
||||
actionName.Contains($"{name}{singularizeName}ById", StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 1002 KiB |
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 106 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 85 KiB |
|
@ -0,0 +1,257 @@
|
|||
/**
|
||||
* Template Name: Arsha - v4.9.1
|
||||
* Template URL: https://bootstrapmade.com/arsha-free-bootstrap-html-template-corporate/
|
||||
* Author: BootstrapMade.com
|
||||
* License: https://bootstrapmade.com/license/
|
||||
*/
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Easy selector helper function
|
||||
*/
|
||||
const select = (el, all = false) => {
|
||||
el = el.trim()
|
||||
if (all) {
|
||||
return [...document.querySelectorAll(el)]
|
||||
} else {
|
||||
return document.querySelector(el)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Easy event listener function
|
||||
*/
|
||||
const on = (type, el, listener, all = false) => {
|
||||
let selectEl = select(el, all)
|
||||
if (selectEl) {
|
||||
if (all) {
|
||||
selectEl.forEach(e => e.addEventListener(type, listener))
|
||||
} else {
|
||||
selectEl.addEventListener(type, listener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Easy on scroll event listener
|
||||
*/
|
||||
const onscroll = (el, listener) => {
|
||||
el.addEventListener('scroll', listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* Navbar links active state on scroll
|
||||
*/
|
||||
let navbarlinks = select('#navbar .scrollto', true)
|
||||
const navbarlinksActive = () => {
|
||||
let position = window.scrollY + 200
|
||||
navbarlinks.forEach(navbarlink => {
|
||||
if (!navbarlink.hash) return
|
||||
let section = select(navbarlink.hash)
|
||||
if (!section) return
|
||||
if (position >= section.offsetTop && position <= (section.offsetTop + section.offsetHeight)) {
|
||||
navbarlink.classList.add('active')
|
||||
} else {
|
||||
navbarlink.classList.remove('active')
|
||||
}
|
||||
})
|
||||
}
|
||||
window.addEventListener('load', navbarlinksActive)
|
||||
onscroll(document, navbarlinksActive)
|
||||
|
||||
/**
|
||||
* Scrolls to an element with header offset
|
||||
*/
|
||||
const scrollto = (el) => {
|
||||
let header = select('#header')
|
||||
let offset = header.offsetHeight
|
||||
|
||||
let elementPos = select(el).offsetTop
|
||||
window.scrollTo({
|
||||
top: elementPos - offset,
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle .header-scrolled class to #header when page is scrolled
|
||||
*/
|
||||
let selectHeader = select('#header')
|
||||
if (selectHeader) {
|
||||
const headerScrolled = () => {
|
||||
if (window.scrollY > 100) {
|
||||
selectHeader.classList.add('header-scrolled')
|
||||
} else {
|
||||
selectHeader.classList.remove('header-scrolled')
|
||||
}
|
||||
}
|
||||
window.addEventListener('load', headerScrolled)
|
||||
onscroll(document, headerScrolled)
|
||||
}
|
||||
|
||||
/**
|
||||
* Back to top button
|
||||
*/
|
||||
let backtotop = select('.back-to-top')
|
||||
if (backtotop) {
|
||||
const toggleBacktotop = () => {
|
||||
if (window.scrollY > 100) {
|
||||
backtotop.classList.add('active')
|
||||
} else {
|
||||
backtotop.classList.remove('active')
|
||||
}
|
||||
}
|
||||
window.addEventListener('load', toggleBacktotop)
|
||||
onscroll(document, toggleBacktotop)
|
||||
}
|
||||
|
||||
/**
|
||||
* Mobile nav toggle
|
||||
*/
|
||||
on('click', '.mobile-nav-toggle', function(e) {
|
||||
select('#navbar').classList.toggle('navbar-mobile')
|
||||
this.classList.toggle('bi-list')
|
||||
this.classList.toggle('bi-x')
|
||||
})
|
||||
|
||||
/**
|
||||
* Mobile nav dropdowns activate
|
||||
*/
|
||||
on('click', '.navbar .dropdown > a', function(e) {
|
||||
if (select('#navbar').classList.contains('navbar-mobile')) {
|
||||
e.preventDefault()
|
||||
this.nextElementSibling.classList.toggle('dropdown-active')
|
||||
}
|
||||
}, true)
|
||||
|
||||
/**
|
||||
* Scrool with ofset on links with a class name .scrollto
|
||||
*/
|
||||
on('click', '.scrollto', function(e) {
|
||||
if (select(this.hash)) {
|
||||
e.preventDefault()
|
||||
|
||||
let navbar = select('#navbar')
|
||||
if (navbar.classList.contains('navbar-mobile')) {
|
||||
navbar.classList.remove('navbar-mobile')
|
||||
let navbarToggle = select('.mobile-nav-toggle')
|
||||
navbarToggle.classList.toggle('bi-list')
|
||||
navbarToggle.classList.toggle('bi-x')
|
||||
}
|
||||
scrollto(this.hash)
|
||||
}
|
||||
}, true)
|
||||
|
||||
/**
|
||||
* Scroll with ofset on page load with hash links in the url
|
||||
*/
|
||||
window.addEventListener('load', () => {
|
||||
if (window.location.hash) {
|
||||
if (select(window.location.hash)) {
|
||||
scrollto(window.location.hash)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Preloader
|
||||
*/
|
||||
let preloader = select('#preloader');
|
||||
if (preloader) {
|
||||
window.addEventListener('load', () => {
|
||||
preloader.remove()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate glightbox
|
||||
*/
|
||||
const glightbox = GLightbox({
|
||||
selector: '.glightbox'
|
||||
});
|
||||
|
||||
/**
|
||||
* Skills animation
|
||||
*/
|
||||
let skilsContent = select('.skills-content');
|
||||
if (skilsContent) {
|
||||
new Waypoint({
|
||||
element: skilsContent,
|
||||
offset: '80%',
|
||||
handler: function(direction) {
|
||||
let progress = select('.progress .progress-bar', true);
|
||||
progress.forEach((el) => {
|
||||
el.style.width = el.getAttribute('aria-valuenow') + '%'
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Porfolio isotope and filter
|
||||
*/
|
||||
window.addEventListener('load', () => {
|
||||
let portfolioContainer = select('.portfolio-container');
|
||||
if (portfolioContainer) {
|
||||
let portfolioIsotope = new Isotope(portfolioContainer, {
|
||||
itemSelector: '.portfolio-item'
|
||||
});
|
||||
|
||||
let portfolioFilters = select('#portfolio-flters li', true);
|
||||
|
||||
on('click', '#portfolio-flters li', function(e) {
|
||||
e.preventDefault();
|
||||
portfolioFilters.forEach(function(el) {
|
||||
el.classList.remove('filter-active');
|
||||
});
|
||||
this.classList.add('filter-active');
|
||||
|
||||
portfolioIsotope.arrange({
|
||||
filter: this.getAttribute('data-filter')
|
||||
});
|
||||
portfolioIsotope.on('arrangeComplete', function() {
|
||||
AOS.refresh()
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Initiate portfolio lightbox
|
||||
*/
|
||||
const portfolioLightbox = GLightbox({
|
||||
selector: '.portfolio-lightbox'
|
||||
});
|
||||
|
||||
/**
|
||||
* Portfolio details slider
|
||||
*/
|
||||
new Swiper('.portfolio-details-slider', {
|
||||
speed: 400,
|
||||
loop: true,
|
||||
autoplay: {
|
||||
delay: 5000,
|
||||
disableOnInteraction: false
|
||||
},
|
||||
pagination: {
|
||||
el: '.swiper-pagination',
|
||||
type: 'bullets',
|
||||
clickable: true
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Animation on scroll
|
||||
*/
|
||||
window.addEventListener('load', () => {
|
||||
AOS.init({
|
||||
duration: 1000,
|
||||
easing: "ease-in-out",
|
||||
once: true,
|
||||
mirror: false
|
||||
});
|
||||
});
|
||||
|
||||
})()
|
|
@ -0,0 +1 @@
|
|||
The .scss (Sass) files are only avilable in the pro version. You can buy it from: https://bootstrapmade.com/arsha-free-bootstrap-html-template-corporate/
|
|
@ -0,0 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<div class="sidebar">
|
||||
<a class="active" href="#home">Home</a>
|
||||
<a href="#news">News</a>
|
||||
<a href="#contact">Contact</a>
|
||||
<a href="#about">About</a>
|
||||
</div>
|
After Width: | Height: | Size: 1.2 MiB |
|
@ -0,0 +1,758 @@
|
|||
/*!
|
||||
Waypoints - 4.0.1
|
||||
Copyright © 2011-2016 Caleb Troughton
|
||||
Licensed under the MIT license.
|
||||
https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
|
||||
*/
|
||||
(function() {
|
||||
'use strict'
|
||||
|
||||
var keyCounter = 0
|
||||
var allWaypoints = {}
|
||||
|
||||
/* http://imakewebthings.com/waypoints/api/waypoint */
|
||||
function Waypoint(options) {
|
||||
if (!options) {
|
||||
throw new Error('No options passed to Waypoint constructor')
|
||||
}
|
||||
if (!options.element) {
|
||||
throw new Error('No element option passed to Waypoint constructor')
|
||||
}
|
||||
if (!options.handler) {
|
||||
throw new Error('No handler option passed to Waypoint constructor')
|
||||
}
|
||||
|
||||
this.key = 'waypoint-' + keyCounter
|
||||
this.options = Waypoint.Adapter.extend({}, Waypoint.defaults, options)
|
||||
this.element = this.options.element
|
||||
this.adapter = new Waypoint.Adapter(this.element)
|
||||
this.callback = options.handler
|
||||
this.axis = this.options.horizontal ? 'horizontal' : 'vertical'
|
||||
this.enabled = this.options.enabled
|
||||
this.triggerPoint = null
|
||||
this.group = Waypoint.Group.findOrCreate({
|
||||
name: this.options.group,
|
||||
axis: this.axis
|
||||
})
|
||||
this.context = Waypoint.Context.findOrCreateByElement(this.options.context)
|
||||
|
||||
if (Waypoint.offsetAliases[this.options.offset]) {
|
||||
this.options.offset = Waypoint.offsetAliases[this.options.offset]
|
||||
}
|
||||
this.group.add(this)
|
||||
this.context.add(this)
|
||||
allWaypoints[this.key] = this
|
||||
keyCounter += 1
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Waypoint.prototype.queueTrigger = function(direction) {
|
||||
this.group.queueTrigger(this, direction)
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Waypoint.prototype.trigger = function(args) {
|
||||
if (!this.enabled) {
|
||||
return
|
||||
}
|
||||
if (this.callback) {
|
||||
this.callback.apply(this, args)
|
||||
}
|
||||
}
|
||||
|
||||
/* Public */
|
||||
/* http://imakewebthings.com/waypoints/api/destroy */
|
||||
Waypoint.prototype.destroy = function() {
|
||||
this.context.remove(this)
|
||||
this.group.remove(this)
|
||||
delete allWaypoints[this.key]
|
||||
}
|
||||
|
||||
/* Public */
|
||||
/* http://imakewebthings.com/waypoints/api/disable */
|
||||
Waypoint.prototype.disable = function() {
|
||||
this.enabled = false
|
||||
return this
|
||||
}
|
||||
|
||||
/* Public */
|
||||
/* http://imakewebthings.com/waypoints/api/enable */
|
||||
Waypoint.prototype.enable = function() {
|
||||
this.context.refresh()
|
||||
this.enabled = true
|
||||
return this
|
||||
}
|
||||
|
||||
/* Public */
|
||||
/* http://imakewebthings.com/waypoints/api/next */
|
||||
Waypoint.prototype.next = function() {
|
||||
return this.group.next(this)
|
||||
}
|
||||
|
||||
/* Public */
|
||||
/* http://imakewebthings.com/waypoints/api/previous */
|
||||
Waypoint.prototype.previous = function() {
|
||||
return this.group.previous(this)
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Waypoint.invokeAll = function(method) {
|
||||
var allWaypointsArray = []
|
||||
for (var waypointKey in allWaypoints) {
|
||||
allWaypointsArray.push(allWaypoints[waypointKey])
|
||||
}
|
||||
for (var i = 0, end = allWaypointsArray.length; i < end; i++) {
|
||||
allWaypointsArray[i][method]()
|
||||
}
|
||||
}
|
||||
|
||||
/* Public */
|
||||
/* http://imakewebthings.com/waypoints/api/destroy-all */
|
||||
Waypoint.destroyAll = function() {
|
||||
Waypoint.invokeAll('destroy')
|
||||
}
|
||||
|
||||
/* Public */
|
||||
/* http://imakewebthings.com/waypoints/api/disable-all */
|
||||
Waypoint.disableAll = function() {
|
||||
Waypoint.invokeAll('disable')
|
||||
}
|
||||
|
||||
/* Public */
|
||||
/* http://imakewebthings.com/waypoints/api/enable-all */
|
||||
Waypoint.enableAll = function() {
|
||||
Waypoint.Context.refreshAll()
|
||||
for (var waypointKey in allWaypoints) {
|
||||
allWaypoints[waypointKey].enabled = true
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/* Public */
|
||||
/* http://imakewebthings.com/waypoints/api/refresh-all */
|
||||
Waypoint.refreshAll = function() {
|
||||
Waypoint.Context.refreshAll()
|
||||
}
|
||||
|
||||
/* Public */
|
||||
/* http://imakewebthings.com/waypoints/api/viewport-height */
|
||||
Waypoint.viewportHeight = function() {
|
||||
return window.innerHeight || document.documentElement.clientHeight
|
||||
}
|
||||
|
||||
/* Public */
|
||||
/* http://imakewebthings.com/waypoints/api/viewport-width */
|
||||
Waypoint.viewportWidth = function() {
|
||||
return document.documentElement.clientWidth
|
||||
}
|
||||
|
||||
Waypoint.adapters = []
|
||||
|
||||
Waypoint.defaults = {
|
||||
context: window,
|
||||
continuous: true,
|
||||
enabled: true,
|
||||
group: 'default',
|
||||
horizontal: false,
|
||||
offset: 0
|
||||
}
|
||||
|
||||
Waypoint.offsetAliases = {
|
||||
'bottom-in-view': function() {
|
||||
return this.context.innerHeight() - this.adapter.outerHeight()
|
||||
},
|
||||
'right-in-view': function() {
|
||||
return this.context.innerWidth() - this.adapter.outerWidth()
|
||||
}
|
||||
}
|
||||
|
||||
window.Waypoint = Waypoint
|
||||
}())
|
||||
;(function() {
|
||||
'use strict'
|
||||
|
||||
function requestAnimationFrameShim(callback) {
|
||||
window.setTimeout(callback, 1000 / 60)
|
||||
}
|
||||
|
||||
var keyCounter = 0
|
||||
var contexts = {}
|
||||
var Waypoint = window.Waypoint
|
||||
var oldWindowLoad = window.onload
|
||||
|
||||
/* http://imakewebthings.com/waypoints/api/context */
|
||||
function Context(element) {
|
||||
this.element = element
|
||||
this.Adapter = Waypoint.Adapter
|
||||
this.adapter = new this.Adapter(element)
|
||||
this.key = 'waypoint-context-' + keyCounter
|
||||
this.didScroll = false
|
||||
this.didResize = false
|
||||
this.oldScroll = {
|
||||
x: this.adapter.scrollLeft(),
|
||||
y: this.adapter.scrollTop()
|
||||
}
|
||||
this.waypoints = {
|
||||
vertical: {},
|
||||
horizontal: {}
|
||||
}
|
||||
|
||||
element.waypointContextKey = this.key
|
||||
contexts[element.waypointContextKey] = this
|
||||
keyCounter += 1
|
||||
if (!Waypoint.windowContext) {
|
||||
Waypoint.windowContext = true
|
||||
Waypoint.windowContext = new Context(window)
|
||||
}
|
||||
|
||||
this.createThrottledScrollHandler()
|
||||
this.createThrottledResizeHandler()
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Context.prototype.add = function(waypoint) {
|
||||
var axis = waypoint.options.horizontal ? 'horizontal' : 'vertical'
|
||||
this.waypoints[axis][waypoint.key] = waypoint
|
||||
this.refresh()
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Context.prototype.checkEmpty = function() {
|
||||
var horizontalEmpty = this.Adapter.isEmptyObject(this.waypoints.horizontal)
|
||||
var verticalEmpty = this.Adapter.isEmptyObject(this.waypoints.vertical)
|
||||
var isWindow = this.element == this.element.window
|
||||
if (horizontalEmpty && verticalEmpty && !isWindow) {
|
||||
this.adapter.off('.waypoints')
|
||||
delete contexts[this.key]
|
||||
}
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Context.prototype.createThrottledResizeHandler = function() {
|
||||
var self = this
|
||||
|
||||
function resizeHandler() {
|
||||
self.handleResize()
|
||||
self.didResize = false
|
||||
}
|
||||
|
||||
this.adapter.on('resize.waypoints', function() {
|
||||
if (!self.didResize) {
|
||||
self.didResize = true
|
||||
Waypoint.requestAnimationFrame(resizeHandler)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Context.prototype.createThrottledScrollHandler = function() {
|
||||
var self = this
|
||||
function scrollHandler() {
|
||||
self.handleScroll()
|
||||
self.didScroll = false
|
||||
}
|
||||
|
||||
this.adapter.on('scroll.waypoints', function() {
|
||||
if (!self.didScroll || Waypoint.isTouch) {
|
||||
self.didScroll = true
|
||||
Waypoint.requestAnimationFrame(scrollHandler)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Context.prototype.handleResize = function() {
|
||||
Waypoint.Context.refreshAll()
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Context.prototype.handleScroll = function() {
|
||||
var triggeredGroups = {}
|
||||
var axes = {
|
||||
horizontal: {
|
||||
newScroll: this.adapter.scrollLeft(),
|
||||
oldScroll: this.oldScroll.x,
|
||||
forward: 'right',
|
||||
backward: 'left'
|
||||
},
|
||||
vertical: {
|
||||
newScroll: this.adapter.scrollTop(),
|
||||
oldScroll: this.oldScroll.y,
|
||||
forward: 'down',
|
||||
backward: 'up'
|
||||
}
|
||||
}
|
||||
|
||||
for (var axisKey in axes) {
|
||||
var axis = axes[axisKey]
|
||||
var isForward = axis.newScroll > axis.oldScroll
|
||||
var direction = isForward ? axis.forward : axis.backward
|
||||
|
||||
for (var waypointKey in this.waypoints[axisKey]) {
|
||||
var waypoint = this.waypoints[axisKey][waypointKey]
|
||||
if (waypoint.triggerPoint === null) {
|
||||
continue
|
||||
}
|
||||
var wasBeforeTriggerPoint = axis.oldScroll < waypoint.triggerPoint
|
||||
var nowAfterTriggerPoint = axis.newScroll >= waypoint.triggerPoint
|
||||
var crossedForward = wasBeforeTriggerPoint && nowAfterTriggerPoint
|
||||
var crossedBackward = !wasBeforeTriggerPoint && !nowAfterTriggerPoint
|
||||
if (crossedForward || crossedBackward) {
|
||||
waypoint.queueTrigger(direction)
|
||||
triggeredGroups[waypoint.group.id] = waypoint.group
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var groupKey in triggeredGroups) {
|
||||
triggeredGroups[groupKey].flushTriggers()
|
||||
}
|
||||
|
||||
this.oldScroll = {
|
||||
x: axes.horizontal.newScroll,
|
||||
y: axes.vertical.newScroll
|
||||
}
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Context.prototype.innerHeight = function() {
|
||||
/*eslint-disable eqeqeq */
|
||||
if (this.element == this.element.window) {
|
||||
return Waypoint.viewportHeight()
|
||||
}
|
||||
/*eslint-enable eqeqeq */
|
||||
return this.adapter.innerHeight()
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Context.prototype.remove = function(waypoint) {
|
||||
delete this.waypoints[waypoint.axis][waypoint.key]
|
||||
this.checkEmpty()
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Context.prototype.innerWidth = function() {
|
||||
/*eslint-disable eqeqeq */
|
||||
if (this.element == this.element.window) {
|
||||
return Waypoint.viewportWidth()
|
||||
}
|
||||
/*eslint-enable eqeqeq */
|
||||
return this.adapter.innerWidth()
|
||||
}
|
||||
|
||||
/* Public */
|
||||
/* http://imakewebthings.com/waypoints/api/context-destroy */
|
||||
Context.prototype.destroy = function() {
|
||||
var allWaypoints = []
|
||||
for (var axis in this.waypoints) {
|
||||
for (var waypointKey in this.waypoints[axis]) {
|
||||
allWaypoints.push(this.waypoints[axis][waypointKey])
|
||||
}
|
||||
}
|
||||
for (var i = 0, end = allWaypoints.length; i < end; i++) {
|
||||
allWaypoints[i].destroy()
|
||||
}
|
||||
}
|
||||
|
||||
/* Public */
|
||||
/* http://imakewebthings.com/waypoints/api/context-refresh */
|
||||
Context.prototype.refresh = function() {
|
||||
/*eslint-disable eqeqeq */
|
||||
var isWindow = this.element == this.element.window
|
||||
/*eslint-enable eqeqeq */
|
||||
var contextOffset = isWindow ? undefined : this.adapter.offset()
|
||||
var triggeredGroups = {}
|
||||
var axes
|
||||
|
||||
this.handleScroll()
|
||||
axes = {
|
||||
horizontal: {
|
||||
contextOffset: isWindow ? 0 : contextOffset.left,
|
||||
contextScroll: isWindow ? 0 : this.oldScroll.x,
|
||||
contextDimension: this.innerWidth(),
|
||||
oldScroll: this.oldScroll.x,
|
||||
forward: 'right',
|
||||
backward: 'left',
|
||||
offsetProp: 'left'
|
||||
},
|
||||
vertical: {
|
||||
contextOffset: isWindow ? 0 : contextOffset.top,
|
||||
contextScroll: isWindow ? 0 : this.oldScroll.y,
|
||||
contextDimension: this.innerHeight(),
|
||||
oldScroll: this.oldScroll.y,
|
||||
forward: 'down',
|
||||
backward: 'up',
|
||||
offsetProp: 'top'
|
||||
}
|
||||
}
|
||||
|
||||
for (var axisKey in axes) {
|
||||
var axis = axes[axisKey]
|
||||
for (var waypointKey in this.waypoints[axisKey]) {
|
||||
var waypoint = this.waypoints[axisKey][waypointKey]
|
||||
var adjustment = waypoint.options.offset
|
||||
var oldTriggerPoint = waypoint.triggerPoint
|
||||
var elementOffset = 0
|
||||
var freshWaypoint = oldTriggerPoint == null
|
||||
var contextModifier, wasBeforeScroll, nowAfterScroll
|
||||
var triggeredBackward, triggeredForward
|
||||
|
||||
if (waypoint.element !== waypoint.element.window) {
|
||||
elementOffset = waypoint.adapter.offset()[axis.offsetProp]
|
||||
}
|
||||
|
||||
if (typeof adjustment === 'function') {
|
||||
adjustment = adjustment.apply(waypoint)
|
||||
}
|
||||
else if (typeof adjustment === 'string') {
|
||||
adjustment = parseFloat(adjustment)
|
||||
if (waypoint.options.offset.indexOf('%') > - 1) {
|
||||
adjustment = Math.ceil(axis.contextDimension * adjustment / 100)
|
||||
}
|
||||
}
|
||||
|
||||
contextModifier = axis.contextScroll - axis.contextOffset
|
||||
waypoint.triggerPoint = Math.floor(elementOffset + contextModifier - adjustment)
|
||||
wasBeforeScroll = oldTriggerPoint < axis.oldScroll
|
||||
nowAfterScroll = waypoint.triggerPoint >= axis.oldScroll
|
||||
triggeredBackward = wasBeforeScroll && nowAfterScroll
|
||||
triggeredForward = !wasBeforeScroll && !nowAfterScroll
|
||||
|
||||
if (!freshWaypoint && triggeredBackward) {
|
||||
waypoint.queueTrigger(axis.backward)
|
||||
triggeredGroups[waypoint.group.id] = waypoint.group
|
||||
}
|
||||
else if (!freshWaypoint && triggeredForward) {
|
||||
waypoint.queueTrigger(axis.forward)
|
||||
triggeredGroups[waypoint.group.id] = waypoint.group
|
||||
}
|
||||
else if (freshWaypoint && axis.oldScroll >= waypoint.triggerPoint) {
|
||||
waypoint.queueTrigger(axis.forward)
|
||||
triggeredGroups[waypoint.group.id] = waypoint.group
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Waypoint.requestAnimationFrame(function() {
|
||||
for (var groupKey in triggeredGroups) {
|
||||
triggeredGroups[groupKey].flushTriggers()
|
||||
}
|
||||
})
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Context.findOrCreateByElement = function(element) {
|
||||
return Context.findByElement(element) || new Context(element)
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Context.refreshAll = function() {
|
||||
for (var contextId in contexts) {
|
||||
contexts[contextId].refresh()
|
||||
}
|
||||
}
|
||||
|
||||
/* Public */
|
||||
/* http://imakewebthings.com/waypoints/api/context-find-by-element */
|
||||
Context.findByElement = function(element) {
|
||||
return contexts[element.waypointContextKey]
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
if (oldWindowLoad) {
|
||||
oldWindowLoad()
|
||||
}
|
||||
Context.refreshAll()
|
||||
}
|
||||
|
||||
|
||||
Waypoint.requestAnimationFrame = function(callback) {
|
||||
var requestFn = window.requestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
requestAnimationFrameShim
|
||||
requestFn.call(window, callback)
|
||||
}
|
||||
Waypoint.Context = Context
|
||||
}())
|
||||
;(function() {
|
||||
'use strict'
|
||||
|
||||
function byTriggerPoint(a, b) {
|
||||
return a.triggerPoint - b.triggerPoint
|
||||
}
|
||||
|
||||
function byReverseTriggerPoint(a, b) {
|
||||
return b.triggerPoint - a.triggerPoint
|
||||
}
|
||||
|
||||
var groups = {
|
||||
vertical: {},
|
||||
horizontal: {}
|
||||
}
|
||||
var Waypoint = window.Waypoint
|
||||
|
||||
/* http://imakewebthings.com/waypoints/api/group */
|
||||
function Group(options) {
|
||||
this.name = options.name
|
||||
this.axis = options.axis
|
||||
this.id = this.name + '-' + this.axis
|
||||
this.waypoints = []
|
||||
this.clearTriggerQueues()
|
||||
groups[this.axis][this.name] = this
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Group.prototype.add = function(waypoint) {
|
||||
this.waypoints.push(waypoint)
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Group.prototype.clearTriggerQueues = function() {
|
||||
this.triggerQueues = {
|
||||
up: [],
|
||||
down: [],
|
||||
left: [],
|
||||
right: []
|
||||
}
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Group.prototype.flushTriggers = function() {
|
||||
for (var direction in this.triggerQueues) {
|
||||
var waypoints = this.triggerQueues[direction]
|
||||
var reverse = direction === 'up' || direction === 'left'
|
||||
waypoints.sort(reverse ? byReverseTriggerPoint : byTriggerPoint)
|
||||
for (var i = 0, end = waypoints.length; i < end; i += 1) {
|
||||
var waypoint = waypoints[i]
|
||||
if (waypoint.options.continuous || i === waypoints.length - 1) {
|
||||
waypoint.trigger([direction])
|
||||
}
|
||||
}
|
||||
}
|
||||
this.clearTriggerQueues()
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Group.prototype.next = function(waypoint) {
|
||||
this.waypoints.sort(byTriggerPoint)
|
||||
var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
|
||||
var isLast = index === this.waypoints.length - 1
|
||||
return isLast ? null : this.waypoints[index + 1]
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Group.prototype.previous = function(waypoint) {
|
||||
this.waypoints.sort(byTriggerPoint)
|
||||
var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
|
||||
return index ? this.waypoints[index - 1] : null
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Group.prototype.queueTrigger = function(waypoint, direction) {
|
||||
this.triggerQueues[direction].push(waypoint)
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Group.prototype.remove = function(waypoint) {
|
||||
var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
|
||||
if (index > -1) {
|
||||
this.waypoints.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/* Public */
|
||||
/* http://imakewebthings.com/waypoints/api/first */
|
||||
Group.prototype.first = function() {
|
||||
return this.waypoints[0]
|
||||
}
|
||||
|
||||
/* Public */
|
||||
/* http://imakewebthings.com/waypoints/api/last */
|
||||
Group.prototype.last = function() {
|
||||
return this.waypoints[this.waypoints.length - 1]
|
||||
}
|
||||
|
||||
/* Private */
|
||||
Group.findOrCreate = function(options) {
|
||||
return groups[options.axis][options.name] || new Group(options)
|
||||
}
|
||||
|
||||
Waypoint.Group = Group
|
||||
}())
|
||||
;(function() {
|
||||
'use strict'
|
||||
|
||||
var Waypoint = window.Waypoint
|
||||
|
||||
function isWindow(element) {
|
||||
return element === element.window
|
||||
}
|
||||
|
||||
function getWindow(element) {
|
||||
if (isWindow(element)) {
|
||||
return element
|
||||
}
|
||||
return element.defaultView
|
||||
}
|
||||
|
||||
function NoFrameworkAdapter(element) {
|
||||
this.element = element
|
||||
this.handlers = {}
|
||||
}
|
||||
|
||||
NoFrameworkAdapter.prototype.innerHeight = function() {
|
||||
var isWin = isWindow(this.element)
|
||||
return isWin ? this.element.innerHeight : this.element.clientHeight
|
||||
}
|
||||
|
||||
NoFrameworkAdapter.prototype.innerWidth = function() {
|
||||
var isWin = isWindow(this.element)
|
||||
return isWin ? this.element.innerWidth : this.element.clientWidth
|
||||
}
|
||||
|
||||
NoFrameworkAdapter.prototype.off = function(event, handler) {
|
||||
function removeListeners(element, listeners, handler) {
|
||||
for (var i = 0, end = listeners.length - 1; i < end; i++) {
|
||||
var listener = listeners[i]
|
||||
if (!handler || handler === listener) {
|
||||
element.removeEventListener(listener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var eventParts = event.split('.')
|
||||
var eventType = eventParts[0]
|
||||
var namespace = eventParts[1]
|
||||
var element = this.element
|
||||
|
||||
if (namespace && this.handlers[namespace] && eventType) {
|
||||
removeListeners(element, this.handlers[namespace][eventType], handler)
|
||||
this.handlers[namespace][eventType] = []
|
||||
}
|
||||
else if (eventType) {
|
||||
for (var ns in this.handlers) {
|
||||
removeListeners(element, this.handlers[ns][eventType] || [], handler)
|
||||
this.handlers[ns][eventType] = []
|
||||
}
|
||||
}
|
||||
else if (namespace && this.handlers[namespace]) {
|
||||
for (var type in this.handlers[namespace]) {
|
||||
removeListeners(element, this.handlers[namespace][type], handler)
|
||||
}
|
||||
this.handlers[namespace] = {}
|
||||
}
|
||||
}
|
||||
|
||||
/* Adapted from jQuery 1.x offset() */
|
||||
NoFrameworkAdapter.prototype.offset = function() {
|
||||
if (!this.element.ownerDocument) {
|
||||
return null
|
||||
}
|
||||
|
||||
var documentElement = this.element.ownerDocument.documentElement
|
||||
var win = getWindow(this.element.ownerDocument)
|
||||
var rect = {
|
||||
top: 0,
|
||||
left: 0
|
||||
}
|
||||
|
||||
if (this.element.getBoundingClientRect) {
|
||||
rect = this.element.getBoundingClientRect()
|
||||
}
|
||||
|
||||
return {
|
||||
top: rect.top + win.pageYOffset - documentElement.clientTop,
|
||||
left: rect.left + win.pageXOffset - documentElement.clientLeft
|
||||
}
|
||||
}
|
||||
|
||||
NoFrameworkAdapter.prototype.on = function(event, handler) {
|
||||
var eventParts = event.split('.')
|
||||
var eventType = eventParts[0]
|
||||
var namespace = eventParts[1] || '__default'
|
||||
var nsHandlers = this.handlers[namespace] = this.handlers[namespace] || {}
|
||||
var nsTypeList = nsHandlers[eventType] = nsHandlers[eventType] || []
|
||||
|
||||
nsTypeList.push(handler)
|
||||
this.element.addEventListener(eventType, handler)
|
||||
}
|
||||
|
||||
NoFrameworkAdapter.prototype.outerHeight = function(includeMargin) {
|
||||
var height = this.innerHeight()
|
||||
var computedStyle
|
||||
|
||||
if (includeMargin && !isWindow(this.element)) {
|
||||
computedStyle = window.getComputedStyle(this.element)
|
||||
height += parseInt(computedStyle.marginTop, 10)
|
||||
height += parseInt(computedStyle.marginBottom, 10)
|
||||
}
|
||||
|
||||
return height
|
||||
}
|
||||
|
||||
NoFrameworkAdapter.prototype.outerWidth = function(includeMargin) {
|
||||
var width = this.innerWidth()
|
||||
var computedStyle
|
||||
|
||||
if (includeMargin && !isWindow(this.element)) {
|
||||
computedStyle = window.getComputedStyle(this.element)
|
||||
width += parseInt(computedStyle.marginLeft, 10)
|
||||
width += parseInt(computedStyle.marginRight, 10)
|
||||
}
|
||||
|
||||
return width
|
||||
}
|
||||
|
||||
NoFrameworkAdapter.prototype.scrollLeft = function() {
|
||||
var win = getWindow(this.element)
|
||||
return win ? win.pageXOffset : this.element.scrollLeft
|
||||
}
|
||||
|
||||
NoFrameworkAdapter.prototype.scrollTop = function() {
|
||||
var win = getWindow(this.element)
|
||||
return win ? win.pageYOffset : this.element.scrollTop
|
||||
}
|
||||
|
||||
NoFrameworkAdapter.extend = function() {
|
||||
var args = Array.prototype.slice.call(arguments)
|
||||
|
||||
function merge(target, obj) {
|
||||
if (typeof target === 'object' && typeof obj === 'object') {
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
target[key] = obj[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target
|
||||
}
|
||||
|
||||
for (var i = 1, end = args.length; i < end; i++) {
|
||||
merge(args[0], args[i])
|
||||
}
|
||||
return args[0]
|
||||
}
|
||||
|
||||
NoFrameworkAdapter.inArray = function(element, array, i) {
|
||||
return array == null ? -1 : array.indexOf(element, i)
|
||||
}
|
||||
|
||||
NoFrameworkAdapter.isEmptyObject = function(obj) {
|
||||
/* eslint no-unused-vars: 0 */
|
||||
for (var name in obj) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
Waypoint.adapters.push({
|
||||
name: 'noframework',
|
||||
Adapter: NoFrameworkAdapter
|
||||
})
|
||||
Waypoint.Adapter = NoFrameworkAdapter
|
||||
}())
|
||||
;
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common
|
||||
namespace NetinaCMS.Common
|
||||
{
|
||||
public class CommonConfig
|
||||
{
|
|
@ -1,6 +1,6 @@
|
|||
using System.Collections;
|
||||
|
||||
namespace HamyanEdalat.Common.Extensions
|
||||
namespace NetinaCMS.Common.Extensions
|
||||
{
|
||||
public static class AssertExtensions
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Extensions;
|
||||
namespace NetinaCMS.Common.Extensions;
|
||||
|
||||
public static class BoolExtensions
|
||||
{
|
|
@ -1,6 +1,6 @@
|
|||
using HamyanEdalat.Common.Models.Entity;
|
||||
using NetinaCMS.Common.Models.Entity;
|
||||
|
||||
namespace HamyanEdalat.Common.Extensions
|
||||
namespace NetinaCMS.Common.Extensions
|
||||
{
|
||||
public static class ClassDisplayExtensions
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Extensions
|
||||
namespace NetinaCMS.Common.Extensions
|
||||
{
|
||||
public static class DateTimeExtensions
|
||||
{
|
|
@ -1,6 +1,6 @@
|
|||
using System.Reflection;
|
||||
|
||||
namespace HamyanEdalat.Common.Extensions
|
||||
namespace NetinaCMS.Common.Extensions
|
||||
{
|
||||
public enum DisplayProperty
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Extensions
|
||||
namespace NetinaCMS.Common.Extensions
|
||||
{
|
||||
public static class NewtonJsonExtensions
|
||||
{
|
|
@ -1,6 +1,6 @@
|
|||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace HamyanEdalat.Common.Extensions;
|
||||
namespace NetinaCMS.Common.Extensions;
|
||||
public static class PhoneNumberExtensions
|
||||
{
|
||||
public static bool CheckPhoneNumber(string phoneNumber)
|
|
@ -1,6 +1,6 @@
|
|||
using System.Reflection;
|
||||
|
||||
namespace HamyanEdalat.Common.Extensions
|
||||
namespace NetinaCMS.Common.Extensions
|
||||
{
|
||||
public static class PropertyExtensions
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Extensions
|
||||
namespace NetinaCMS.Common.Extensions
|
||||
{
|
||||
public static class RandomExtensions
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Extensions
|
||||
namespace NetinaCMS.Common.Extensions
|
||||
{
|
||||
public static class StringExtensions
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Extensions
|
||||
namespace NetinaCMS.Common.Extensions
|
||||
{
|
||||
public static class ValidationExtensions
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Models.Api
|
||||
namespace NetinaCMS.Common.Models.Api
|
||||
{
|
||||
public class AccessToken
|
||||
{
|
|
@ -1,6 +1,6 @@
|
|||
using System.Net;
|
||||
|
||||
namespace HamyanEdalat.Common.Models.Api
|
||||
namespace NetinaCMS.Common.Models.Api
|
||||
{
|
||||
public enum ApiResultStatusCode
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Models.Api
|
||||
namespace NetinaCMS.Common.Models.Api
|
||||
{
|
||||
public class AppSettings
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Models.Api
|
||||
namespace NetinaCMS.Common.Models.Api
|
||||
{
|
||||
public enum FileUploadType
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Models.Api;
|
||||
namespace NetinaCMS.Common.Models.Api;
|
||||
|
||||
public class FileUploadResponse
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Models.Api
|
||||
namespace NetinaCMS.Common.Models.Api
|
||||
{
|
||||
public class HealthCheck
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Models.Api
|
||||
namespace NetinaCMS.Common.Models.Api
|
||||
{
|
||||
public class ResponseFile
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Models.Api
|
||||
namespace NetinaCMS.Common.Models.Api
|
||||
{
|
||||
public class TokenRequest
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Models.Entity;
|
||||
namespace NetinaCMS.Common.Models.Entity;
|
||||
public abstract class ApiEntity : IApiEntity , IEquatable<ApiEntity>
|
||||
{
|
||||
[Key]
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Models.Entity
|
||||
namespace NetinaCMS.Common.Models.Entity
|
||||
{
|
||||
public interface IApiEntity
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Models.Entity
|
||||
namespace NetinaCMS.Common.Models.Entity
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class PageClassDisplay : Attribute
|
|
@ -1,7 +1,7 @@
|
|||
using System.Runtime.Serialization;
|
||||
using HamyanEdalat.Common.Models.Api;
|
||||
using NetinaCMS.Common.Models.Api;
|
||||
|
||||
namespace HamyanEdalat.Common.Models.Exception
|
||||
namespace NetinaCMS.Common.Models.Exception
|
||||
{
|
||||
[Serializable()]
|
||||
public class AppException : System.Exception
|
|
@ -1,8 +1,8 @@
|
|||
using System.Net;
|
||||
using System.Runtime.Serialization;
|
||||
using HamyanEdalat.Common.Models.Api;
|
||||
using NetinaCMS.Common.Models.Api;
|
||||
|
||||
namespace HamyanEdalat.Common.Models.Exception
|
||||
namespace NetinaCMS.Common.Models.Exception
|
||||
{
|
||||
[Serializable()]
|
||||
public class BaseApiException : System.Exception
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Models.Exception;
|
||||
namespace NetinaCMS.Common.Models.Exception;
|
||||
|
||||
public class ValidationException : System.Exception
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace HamyanEdalat.Common.Models
|
||||
namespace NetinaCMS.Common.Models
|
||||
{
|
||||
public interface IScopedDependency
|
||||
{
|
|
@ -1,9 +1,9 @@
|
|||
using System.ComponentModel;
|
||||
using System.Linq.Expressions;
|
||||
using System.Runtime.CompilerServices;
|
||||
using HamyanEdalat.Common.Models.Exception;
|
||||
using NetinaCMS.Common.Models.Exception;
|
||||
|
||||
namespace HamyanEdalat.Common.Models.Mapper
|
||||
namespace NetinaCMS.Common.Models.Mapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Base Dto Class initial map config between entity and dto
|
|
@ -1,6 +1,6 @@
|
|||
using System.Linq.Expressions;
|
||||
|
||||
namespace HamyanEdalat.Common.Models.Mapper
|
||||
namespace NetinaCMS.Common.Models.Mapper
|
||||
{
|
||||
public interface IBaseDto<TDto,TEntity>
|
||||
{
|