From f863f49af7f10e7c5dd8ac3a3a9c04bfda3ef2b2 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Mon, 23 Mar 2026 19:13:06 +0330 Subject: [PATCH] Initial ... --- .gitignore | 13 +++ Base/XIBaseV1Controller.cs | 33 ++++++ Controllers/StartupController.cs | 45 +++++++ Controllers/TestIdentity.cs | 193 ++++++++++++++++++++++++++++++ Controllers/V1/.gitkeep | 0 DI/XPushDIExtensions.cs | 32 +++++ Extensions/XStartupExtensions.cs | 86 ++++++++++++++ Program.cs | 20 ++++ Properties/launchSettings.json | 23 ++++ Startup.cs | 194 +++++++++++++++++++++++++++++++ appsettings.Development.json | 28 +++++ appsettings.json | 69 +++++++++++ nuget.config | 8 ++ xAiApi.csproj | 51 ++++++++ 14 files changed, 795 insertions(+) create mode 100644 .gitignore create mode 100644 Base/XIBaseV1Controller.cs create mode 100644 Controllers/StartupController.cs create mode 100644 Controllers/TestIdentity.cs create mode 100644 Controllers/V1/.gitkeep create mode 100644 DI/XPushDIExtensions.cs create mode 100644 Extensions/XStartupExtensions.cs create mode 100644 Program.cs create mode 100644 Properties/launchSettings.json create mode 100644 Startup.cs create mode 100644 appsettings.Development.json create mode 100644 appsettings.json create mode 100644 nuget.config create mode 100644 xAiApi.csproj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e99217 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# +bin +obj + +# +Db/* +!Db/.gitkeep + +# +Migrations/* + +# +wwwroot/* \ No newline at end of file diff --git a/Base/XIBaseV1Controller.cs b/Base/XIBaseV1Controller.cs new file mode 100644 index 0000000..cf02bd4 --- /dev/null +++ b/Base/XIBaseV1Controller.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Attributes; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Controllers; +using xIdentityService.Interfaces; + +namespace xAiApi.Base +{ + [ApiController] + [ApiVersion("1.0")] + [RequireXPowered(true)] + [Route("api/v{version:apiVersion}/[controller]")] + public abstract class XIBaseV1Controller : XIBaseController + { + // + #region Constructor ... + protected XIBaseV1Controller( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider + ) + { } + #endregion + } +} \ No newline at end of file diff --git a/Controllers/StartupController.cs b/Controllers/StartupController.cs new file mode 100644 index 0000000..3717c43 --- /dev/null +++ b/Controllers/StartupController.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Controllers; +using xCommons.Providers; + +namespace xAiApi.Controllers +{ + /// + /// Runing when application start ... + /// + [Route("")] + [AllowAnonymous] + public class StartupController : XBaseController + { + + public StartupController( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + validationProvider + ) + { } + + /// + /// Show Configured Welcome Message + /// + /// + [HttpGet("")] + [AllowAnonymous] + public virtual ActionResult Index() + { + // + var controllerName = GetControllerName(); + var message = $"{AppConfiguration.WelcomeMessage}"; + + // + return Ok(message); + } + } +} \ No newline at end of file diff --git a/Controllers/TestIdentity.cs b/Controllers/TestIdentity.cs new file mode 100644 index 0000000..fde04f3 --- /dev/null +++ b/Controllers/TestIdentity.cs @@ -0,0 +1,193 @@ +using System.Linq; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Attributes; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityHelper; +using xIdentityService.Controllers; +using xIdentityService.Interfaces; + +namespace xAiApi.Controllers +{ + /// + /// Test all Authentication Policies ... + /// + [RequireXPowered(false)] + public class TestIdentity : XIBaseController + { + public TestIdentity( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider + ) + { } + + // + #region Test Actions ... + /// + /// API Read Scope + /// + /// string message + [HttpGet("PassReadAccess")] + [Authorize(Policy = XPolicies.ReadAccess)] + public ActionResult PassReadAccess() + { + return Ok("Read Access Passed ..."); + } + + /// + /// API Write Scope + /// + /// string message + [HttpGet("PassWriteAccess")] + [Authorize(Policy = XPolicies.WriteAccess)] + public ActionResult PassWriteAccess() + { + return Ok("Write Access Passed ..."); + } + + /// + /// API Admin Scope + /// + /// string message + [HttpGet("PassAdminAccess")] + [Authorize(Policy = XPolicies.AdminAccess)] + public ActionResult PassAdminAccess() + { + return Ok("Admin Access Passed ..."); + } + + /// + /// API Manage Scop + /// + /// string message + [HttpGet("PassManageAccess")] + [Authorize(Policy = XPolicies.ManageAccess)] + public ActionResult PassManageAccess() + { + return Ok("Manage Access Passed ..."); + } + + /// + /// a simple Action which returns a List of Authenticated User Claims + /// + /// string message which represent current user's claims + [HttpGet("HiClaims")] + [Authorize(Policy = XPolicies.User)] + public ActionResult HiClaims() + { + // + var result = new + { + name = User.Identity.Name, + claims = User.Claims.Select(c => new + { + c.Type, + c.Value + }) + }; + + // + return Ok(result); + } + + /// + /// a simple Hello User for Checking Authentication and Policy + /// + /// string message which contains authenticated user name + [HttpGet("HiUser")] + [Authorize(Policy = XPolicies.User)] + public ActionResult HiUser() + { + // + var result = $"Hi User: {User.Identity.Name} ..."; + + // + return Ok(result); + } + + /// + /// a simple Hello User for Checking Authentication and Policy + /// + /// string message which contains authenticated user name + [HttpGet("HiEnabledUser")] + [Authorize(Policy = XPolicies.EnabledUser)] + public ActionResult HiEnabledUser() + { + // + var result = $"Hi User: {User.Identity.Name} is Enabled ..."; + + // + return Ok(result); + } + + /// + /// a simple Hello User for Checking Authentication and Policy + /// + /// string message which contains authenticated user name + [HttpGet("HiAgent")] + [Authorize(Policy = XPolicies.Agent)] + public ActionResult HiAgent() + { + // + var result = $"Hi Admin: {User.Identity.Name} ..."; + + // + return Ok(result); + } + + /// + /// a simple Hello User for Checking Authentication and Policy + /// + /// string message which contains authenticated user name + [HttpGet("HiEnabledAgent")] + [Authorize(Policy = XPolicies.EnabledAgent)] + public ActionResult HiEnabledAgent() + { + // + var result = $"Hi Admin: {User.Identity.Name} is Enabled ..."; + + // + return Ok(result); + } + + /// + /// a simple Hello User for Checking Authentication and Policy + /// + /// string message which contains authenticated user name + [HttpGet("HiAdmin")] + [Authorize(Policy = XPolicies.Admin)] + public ActionResult HiAdmin() + { + // + var result = $"Hi Admin: {User.Identity.Name} ..."; + + // + return Ok(result); + } + + /// + /// a simple Hello User for Checking Authentication and Policy + /// + /// string message which contains authenticated user name + [HttpGet("HiEnabledAdmin")] + [Authorize(Policy = XPolicies.EnabledAdmin)] + public ActionResult HiEnabledAdmin() + { + // + var result = $"Hi Admin: {User.Identity.Name} is Enabled ..."; + + // + return Ok(result); + } + #endregion + } +} \ No newline at end of file diff --git a/Controllers/V1/.gitkeep b/Controllers/V1/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/DI/XPushDIExtensions.cs b/DI/XPushDIExtensions.cs new file mode 100644 index 0000000..38c1175 --- /dev/null +++ b/DI/XPushDIExtensions.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; + +namespace xAiApi.DI +{ + public static class XPushDIExtensions + { + /// + /// Register XPushProvider Services + /// + /// + public static void AddXPushHubProvider( + this IServiceCollection services, + ServiceLifetime lifeTime = ServiceLifetime.Transient + ) + { + AddPushHubProvider(services, lifeTime); + } + + // + #region Private ... + private static void AddPushHubProvider( + this IServiceCollection services, + ServiceLifetime lifeTime = ServiceLifetime.Transient + ) + { } + #endregion + } +} \ No newline at end of file diff --git a/Extensions/XStartupExtensions.cs b/Extensions/XStartupExtensions.cs new file mode 100644 index 0000000..b2ef96c --- /dev/null +++ b/Extensions/XStartupExtensions.cs @@ -0,0 +1,86 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Authorization; +using xCommons.Extensions; +using xIdentityHelper; + +namespace xAiApi.Extensions +{ + public static class XStartupExtensions + { + /// + /// Register Authorization Policies + /// + /// + /// + public static void AddXAuthorization( + this IServiceCollection services, + IDictionary policies = null + ) + { + // + services.AddAuthorization(options => + { + // + if (!policies.IsNull()) + { + // + // Fill Policies ... + using (var policiesEnumerator = policies.GetEnumerator()) + { + while (policiesEnumerator.MoveNext()) + { + var policyDescriptor = policiesEnumerator.Current; + options.AddPolicy(policyDescriptor.Key, policyDescriptor.Value); + } + } + } + + // + var xPolicies = XAuthorizationHelper.GetXAuthorizationPolicies(); + if (!xPolicies.IsNull()) + { + // + // Fill Policies ... + using (var policiesEnumerator = xPolicies.GetEnumerator()) + { + while (policiesEnumerator.MoveNext()) + { + var policyDescriptor = policiesEnumerator.Current; + options.AddPolicy(policyDescriptor.Key, policyDescriptor.Value); + } + } + } + }); + + // + services.AddSingleton(); + } + + /// + /// Use Forward Headers Options for resolving behind a proxy issues ... + /// + /// + /// + public static void UseXForwardOptions(this IApplicationBuilder source) + { + // + var forwardOptions = new ForwardedHeadersOptions + { + ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto, + RequireHeaderSymmetry = false + }; + + // + forwardOptions.KnownNetworks.Clear(); + forwardOptions.KnownProxies.Clear(); + + // + // ref: https://github.com/aspnet/Docs/issues/2384 + source.UseForwardedHeaders(forwardOptions); + } + } +} \ No newline at end of file diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..9690251 --- /dev/null +++ b/Program.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace xAiApi +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} \ No newline at end of file diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json new file mode 100644 index 0000000..38a206e --- /dev/null +++ b/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5129", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:6001;http://localhost:6000;https://0.0.0.0:6001;", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Startup.cs b/Startup.cs new file mode 100644 index 0000000..a345b17 --- /dev/null +++ b/Startup.cs @@ -0,0 +1,194 @@ +using System; +using System.IO; +using System.Net.Http; +using System.Reflection; +using IdentityModel.AspNetCore.OAuth2Introspection; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using xCommons.Configurations; +using xCommons.Extensions; +using xHttpService.DI; +using xIdentityService.DI; +using xPushService.DI; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Server.Kestrel.Core; +using Microsoft.Extensions.Hosting; +using xDataService.Configuration; +using xDataService.Constants; +using xDataService.DI; +using xDataService.Interfaces; +using xPushService.Helpers; +using xAiApi.Extensions; +using xAiApi.DI; +// using xApi.Extensions; +// using xDataHelper; +// using xDataHelper.DbSeeder; +// using xDataHelper.Helpers; +// using xFileService.Hubs; +// using xPushHelper.DI; +// using xServices.DI; +// using xServices.TermsConditions.Push; +// using xStringService.Hubs; +// using xTagService.Hubs; + +namespace xAiApi +{ + public class Startup + { + public IConfiguration Configuration { get; } + + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public void ConfigureServices(IServiceCollection services) + { + // + // Register Validation Provider and all XCommons Module Services ... + services.AddXCommons(); + + // + // Register App Configuration ... + services.AddXAppConfiguration(Configuration); + var appConfiguration = services.GetRegisteredService(); + + // + // Register Allowed Origins ... + services.AddXCors(appConfiguration.AllowedOrigins); + + // + // Register Swagger ... + var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); + services.AddXSwagger(Configuration, xmlFilePath: xmlPath); + + // + // Register HttpService ... + services.AddXHttpService(Configuration); + + // + // Register Api Versioning ... + services.AddApiVersioning(opt => + { + // + // Set Default Api Version ... + opt.DefaultApiVersion = new ApiVersion(1, 0); + + // + // Set Routing to Default API Version, if Version unspecified ... + opt.AssumeDefaultVersionWhenUnspecified = true; + + // + // Report All Available Api Versions on Response ... + opt.ReportApiVersions = true; + }); + + // + var lifeTime = ServiceLifetime.Scoped; + + // + // OAuthIntrospectin Http Client Handler ... + // this is for handling SSLErrors ... + services.AddHttpClient(OAuth2IntrospectionDefaults.BackChannelHttpClientName) + .ConfigurePrimaryHttpMessageHandler(() => + { + return new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true + }; + }); + + // + // Register Authorizations ... + services.AddXAuthorization(); + + // + services.AddControllers() + .AddNewtonsoftJson(x => + { + x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + x.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + }); + + // + // Register XPushService ... + services.AddXPushService(Configuration); + services.AddXPushHubProvider(lifeTime); + + // + // Register XIdentityService ... + services.AddXIdentityService(Configuration, lifeTime); + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + // + var withPlayground = false; + if (env.IsDevelopment()) + { + // + withPlayground = true; + app.UseDeveloperExceptionPage(); + } + + // + // Use Swagger Middleware ... + app.UseXSwagger(); + + // + app.UseHttpsRedirection(); + + // + // Using Cors ... + app.UseXCors(); + + // + app.UseRouting(); + + // + // Use xIdentityService ... + app.UseXIdentityService(); + + // + app.UseAuthorization(); + + // + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + + // + #region XPushService ... + // + var helper = new XPushServiceHelper(); + + // + // helper.AddHub("termsHub"); + // helper.AddHub("tagEntityHub"); + // helper.AddHub("fileEntityHub"); + // helper.AddHub("stringEntityHub"); + + // + app.UseXPushService(helper); + #endregion + + // + // Use xDataService Middleware ... + app.UseXDataService(); + + // // + // // Using Services ... + // app.UseXServices(); + + // + // Use xGraphQL Middleware ... + app.UseXGraphQL(withPlayground: withPlayground); + } + } +} \ No newline at end of file diff --git a/appsettings.Development.json b/appsettings.Development.json new file mode 100644 index 0000000..9af7537 --- /dev/null +++ b/appsettings.Development.json @@ -0,0 +1,28 @@ +{ + "AllowedOrigins": [ + "http://localhost:5000", + "https://localhost:5001", + "http://api.saherelm.ir", + "https://api.saherelm.ir", + "http://api.saherelmhub.ir", + "https://api.saherelmhub.ir" + ], + "IdentityServiceConfiguration": { + "Authority": "https://localhost:4001", + "ApiName": "xSaherElmAPI", + "ApiSecret": "s@H@1694056", + "ClientId": "xSaherElmAPIClient", + "ClientSecret": "s@H@1694056", + "XPoweredValue": "SaherElmITCenter", + "XRevisionSecretKey": "SaherElmITCenter@1694056", + "DefaultClientTimeout": -1 + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} \ No newline at end of file diff --git a/appsettings.json b/appsettings.json new file mode 100644 index 0000000..fbb55a3 --- /dev/null +++ b/appsettings.json @@ -0,0 +1,69 @@ +{ + "Version": "0.1", + "Name": "xAiApi", + "DefaultLanguage": "fa-IR", + "XPoweredValue": "SaherElmITCenter", + "WelcomeMessage": "Welcome to xSaherElm Project's AI API", + "AllowedOrigins": [ + "http://api.saherelm.ir", + "https://api.saherelm.ir", + "http://api.saherelmhub.ir", + "https://api.saherelmhub.ir" + ], + "SwaggerConfiguration": { + "Version": "v1.0", + "Title": "xSaherElm AI API", + "Description": "Complete API Documentation", + "Contact": { + "Name": "Hadi Khazaee Asl", + "Email": "hadi_khazaee_asl@yahoo.com", + "Url": "https://www.saherelm.ir" + } + }, + "XDbProvider": "SQLITE", + "ConnectionStrings": { + "DataConnection": "Filename=./Db/XAiApi.db" + }, + "DataServiceConfiguration": { + "EnableTracking": true, + "EnableSoftDelete": false, + "EnableDetailedErrors": false, + "EnableSensitiveDataLogging": true, + "PagingConfiguration": { + "DefaultPageSize": 20, + "MaxAvailablePageSize": 400, + "MinAvailablePageSize": 5 + }, + "GraphQLBasePath": "/graphs" + }, + "DbSeeder": { + "UpdateExists": false, + "Strings": [] + }, + "HttpServiceConfiguration": { + "DisableSSLCheck": true, + "DefaultClientTimeout": -1 + }, + "IdentityServiceConfiguration": { + "Authority": "https://172.18.0.151", + "ApiName": "xSaherElmAPI", + "ApiSecret": "s@H@1694056", + "ClientId": "xSaherElmAPIClient", + "ClientSecret": "s@H@1694056", + "XPoweredValue": "SaherElmITCenter", + "XRevisionSecretKey": "SaherElmITCenter@1694056", + "DefaultClientTimeout": -1 + }, + "PushServiceConfiguration": { + "BaseRoute": "hubs", + "AddSupportMessageProtocol": true + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} \ No newline at end of file diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000..461a288 --- /dev/null +++ b/nuget.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/xAiApi.csproj b/xAiApi.csproj new file mode 100644 index 0000000..6e01616 --- /dev/null +++ b/xAiApi.csproj @@ -0,0 +1,51 @@ + + + + netcoreapp3.1 + xSaherElm.xAiApi + 1.0.0 + Hadi Khazaee Asl + SaherElm IT Center + + a WebAPI Project which contains all Business Logic of xSaherElm Project's AI. + + + + false + false + + + + + + + + + + + + + + + + + + + + + + + + true + true + $(NoWarn);1591 + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + +