Compare commits

..
24 Commits
Author SHA1 Message Date
saherelm a1cfa566f6 adding support for xDashboard Context Generator and regenerate Contexes and BKP olds ... 2026-10-10 11:12:54 +03:30
saherelm 95d30936d0 last ... 2026-10-09 23:18:03 +03:30
saherelm 7b79286e34 last ... 2026-10-09 14:21:37 +03:30
saherelm becc3d62bd last ... 2026-10-08 21:19:12 +03:30
saherelm 5c13df00f1 last ... 2026-10-07 22:42:09 +03:30
saherelm 70dc4a9b47 last ... 2026-10-07 19:39:03 +03:30
saherelm 5d13d0968d last ... 2026-10-06 21:32:27 +03:30
saherelm 2b0350d4be last ... 2026-10-06 17:48:05 +03:30
saherelm d108b2cf65 last ... 2026-10-05 22:30:56 +03:30
saherelm 17b00453ab last ... 2026-10-05 17:37:36 +03:30
saherelm a3ae8da269 last ... 2026-10-04 22:02:57 +03:30
saherelm e673d11a68 last ... 2026-10-04 18:11:59 +03:30
saherelm d7054eeb73 last ... 2026-10-04 11:42:43 +03:30
saherelm c81bc0e923 last ... 2026-10-03 22:26:41 +03:30
saherelm 81b900b887 Last ... 2026-10-03 18:04:36 +03:30
saherelm 92988ea155 last ... 2026-10-02 22:13:01 +03:30
saherelm 52579cd506 last ... 2026-10-02 00:29:18 +03:30
saherelm e9deaaa4b1 last ... 2026-10-01 01:16:52 +03:30
saherelm 2eb1daf923 last ... 2026-09-30 23:16:09 +03:30
saherelm 9fe8e86f73 last ... 2026-09-30 15:27:49 +03:30
saherelm c18c7b8c41 last ... 2026-09-29 17:42:00 +03:30
saherelm 0d448f1fab last ... 2026-09-28 22:36:48 +03:30
saherelm 959988ee29 last ... 2026-09-28 18:00:38 +03:30
saherelm 0f2876ad55 last ... 2026-09-28 18:00:10 +03:30
173 changed files with 857139 additions and 135 deletions
+14
View File
@@ -25,3 +25,17 @@ sc delete XLLMEmbdService
https://docs.mozilla.ai/llamafile/using-llamafile/api
https://github.com/mozilla-ai/llamafile
-------------------
Uncesored Qwen 3.5 Coder GGUF:
also download mmproj files for providing visioning:
https://huggingface.co/DavidAU/Qwen3.5-9B-The-Defiant-Fable-Uncensored-Heretic-NEO-IMATRIX-MAX-MTP-GGUF
-------------------
Download these files and put them inside xAiApi/Models:
https://huggingface.co/ggerganov/whisper.cpp/blob/main/ggml-base.bin
https://huggingface.co/ggerganov/whisper.cpp/blob/main/ggml-small.bin
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,173 @@
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xAiService\Constants\ConfigurationNodeNames.cs
namespace xAiService.Constants
{
public partial struct ConfigurationNodeNames
{
public const string AI_SERVICE_NODE = "AIServiceConfiguration";
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xAiService\Constants\XAiServiceConstants.cs
namespace xAiService.Constants
{
public struct XAiServiceConstants
{
//
// Service Extensions Log Tag ...
public const string XAiServiceDILogTag = "XAiService";
//
// File Provider Identifiers ...
public const string XAiProjectFilesIdentifier = "AiPrjF";
public const string XAiMessageFilesIdentifier = "AiMsgF";
public const string XMetaDataFileIdsIdentifier = "FileIds";
public const string XAiConversationFilesIdentifier = "AiCnvrF";
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xAiService\DI\XDIHelperExtension.cs
namespace xAiService.DI
{
public static partial class XDIHelperExtension
{ }
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xAiService\Extensions\XAiServiceExtensions.cs
using xCommons.Extensions;
using xExceptions.Constants;
using xIdentityModels.Models;
namespace xAiService.Extensions
{
public static class XAiServiceExtensions
{
/// <summary>
/// Validate User Info ...
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public static bool IsValid(this XUserClaimsInfoDto source)
{
//
var result =
!source.IsNullOrDefault() &&
!source.UserId.IsNullOrEmpty() &&
!source.AccessToken.IsNullOrEmpty();
//
return result;
}
/// <summary>
/// Validate User Info ...
/// </summary>
/// <param name="source"></param>
/// <param name="invalidException"></param>
public static void Validate(
this XUserClaimsInfoDto source,
XException invalidException = XException.InvalidArgs
)
{
//
var isValid = source.IsValid();
if (!isValid)
{
invalidException.Throw();
}
}
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xAiService\Helpers\VectorHelper.cs
using System;
using xCommons.Extensions;
namespace xAiService.Helpers
{
public static class VectorHelper
{
/// <summary>
/// Normalize a Vector ...
/// </summary>
/// <param name="vector"></param>
public static void Normalize(float[] vector)
{
//
// Validate ...
if (vector.IsNull() || !vector.HasChild())
{
return;
}
//
float sum = 0f;
foreach (var value in vector)
{
sum += value * value;
}
//
float norm = MathF.Sqrt(sum);
if (norm <= 1e-9f)
{
return;
}
//
for (int i = 0; i < vector.Length; i++)
{
vector[i] /= norm;
}
}
/// <summary>
/// Similarity Check ...
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static float CosineSimilarity(float[] a, float[] b)
{
//
if (a.IsNull() || b.IsNull())
{
return 0f;
}
//
if (a.Length != b.Length)
{
return 0f;
}
//
float dot = 0f;
float normA = 0f;
float normB = 0f;
for (int i = 0; i < a.Length; i++)
{
//
dot += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
//
float denominator = MathF.Sqrt(normA) * MathF.Sqrt(normB);
if (denominator <= 1e-9f)
{
return 0f;
}
//
var result = dot / denominator;
return result;
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,253 @@
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xExceptions\Attributes\StringValueAttribute.cs
using System;
namespace xExceptions.Attributes
{
/// <summary>
/// an attribute which attached to enums to carry propper string
/// </summary>
public partial class StringValueAttribute : Attribute {
public string StringValue { get; set; }
public StringValueAttribute (string value) {
this.StringValue = value;
}
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xExceptions\Constants\XException.cs
using xExceptions.Attributes;
namespace xExceptions.Constants
{
public enum XException {
//
#region Invalid Exceptions 1xx ...
[StringValue ("Invalid Data ...")]
InvalidData = 100,
//
[StringValue ("Invalid Date ...")]
InvalidDate = 101,
//
[StringValue ("Bad Request ...")]
BadRequest = 102,
//
[StringValue ("Invalid Args ...")]
InvalidArgs = 103,
//
[StringValue ("Invalid Message ...")]
InvalidMessage = 104,
//
[StringValue ("Invalid Token ...")]
InvalidToken = 105,
//
[StringValue ("Invalid Verification Code ...")]
InvalidVerificationCode = 106,
//
[StringValue ("Invalid Email Address ...")]
InvalidEmailAddress = 107,
//
[StringValue ("Invalid Mobile Number ...")]
InvalidMobileNumber = 108,
//
[StringValue ("Invalid Password ...")]
InvalidPassword = 109,
//
[StringValue ("Inavlid Url ...")]
InavlidUrl = 110,
//
[StringValue ("Invalid Device ...")]
InvalidDevice = 111,
//
[StringValue ("Invalid Profile Image ...")]
InvalidProfileImage = 112,
//
[StringValue ("Invalid File ...")]
InavlidFile = 113,
//
[StringValue ("Invalid Username ...")]
InvalidUserName = 114,
#endregion
//
#region Not (...) Exceptions 2xx ...
//
[StringValue ("Not Found ...")]
NotFound = 200,
//
[StringValue ("Not Allowed ...")]
NotAllowed = 201,
//
[StringValue ("Not Authorized ...")]
NotAuthorized = 202,
//
[StringValue ("Not Visible ...")]
NotVisible = 203,
#endregion
//
#region User/Registration/Account/Bann Exceptions 3xx ...
//
[StringValue ("Account Locked out Until:{0}")]
AccountLockedOut = 300,
//
[StringValue ("Login Failed ...")]
LoginFailed = 301,
//
[StringValue ("User Registered Before ...")]
UserRegisteredBefore = 302,
//
[StringValue ("User Disabled ...")]
UserDisabled = 303,
//
[StringValue ("Device Banned Until:{0}")]
DeviceBanned = 304,
//
[StringValue ("You can't Request Verification code Until:{0}")]
VerificationCodeResendNotAllowed = 305,
//
[StringValue ("Passwords Same ...")]
PasswordsSame = 306,
//
[StringValue ("Mobiles Same ...")]
MobilesSame = 307,
//
[StringValue ("Emails Same ...")]
EmailsSame = 308,
//
[StringValue ("Mobile In Used ...")]
MobileInUsed = 309,
//
[StringValue ("Email In Used ...")]
EmailInUsed = 310,
//
[StringValue ("Mobile Not Confirmed ...")]
MobileNotConfirmed = 311,
//
[StringValue ("Email Not Confirmed ...")]
EmailNotConfirmed = 312,
//
[StringValue ("Post Banned ...")]
PostBanned = 313,
//
[StringValue ("User Banned ...")]
UserBanned = 314,
#endregion
//
#region General Exceptions 4xx ...
//
[StringValue ("Unknown Error ...")]
Unknown = 400,
//
[StringValue ("Duplicate ...")]
Duplicate = 401,
//
[StringValue ("Action Failed ...")]
ActionFailed = 402,
//
[StringValue ("Expired Date ...")]
ExpiredDate = 403,
//
[StringValue ("Unavailable ...")]
Unavailable = 404,
//
[StringValue ("Max Allowed Tag Exceeded ...")]
MaxAllowedTagExceeded = 405,
//
[StringValue ("Timed out ...")]
Timeout = 406,
#endregion
//
#region File Exceptions 5xx ...
//
[StringValue ("Empty File ...")]
EmptyFile = 500,
//
[StringValue ("max File size exceeded ...")]
MaxFileSizeExceeded = 501,
//
[StringValue ("Unsupported File Type ...")]
UnsupportedFileType = 502,
//
[StringValue ("Max Allowed Media Exceeded ...")]
MaxAllowedMediaExceeded = 503,
#endregion
//
#region Configuration Exceptions 6xx ...
//
[StringValue ("Invalid Configuration ...")]
InvalidConfiguration = 600,
//
[StringValue ("Storage Service Initiallization Failed ...")]
StorageServiceInitialFailed = 601,
//
[StringValue ("Message Service Initialization Failed ...")]
MessageServiceInitialFailed = 602,
#endregion
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xExceptions\Models\XError.cs
namespace xExceptions.Models {
public partial class XError {
/// <summary>
/// th ID of Error
/// </summary>
/// <value></value>
public int Id { get; set; }
/// <summary>
/// the String Value of Error
/// </summary>
/// <value></value>
public string Message { get; set; }
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,607 @@
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xHttpService\Configurations\XHttpServiceConfiguration.cs
namespace xHttpService.Configurations {
public partial class XHttpServiceConfiguration {
/// <summary>
/// Determines Http Handler Check SSL or not ...
/// </summary>
/// <value></value>
public bool DisableSSLCheck { get; set; }
public int DefaultClientTimeout { get; set; } = 360;
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xHttpService\Constants\ConfigurationNodeNames.cs
namespace xHttpService.Constants {
public partial struct ConfigurationNodeNames {
public const string HTTP_SERVICE_NODE_NAME = "HttpServiceConfiguration";
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xHttpService\Constants\XHttpMethod.cs
using xExceptions.Attributes;
namespace xHttpService.Constants {
public enum XHttpMethod {
[StringValue ("GET")]
GET,
[StringValue ("POST")]
POST,
[StringValue ("PUT")]
PUT,
[StringValue ("DELETE")]
DELETE
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xHttpService\DI\XDIHelperExtension.cs
using System;
using xCommons.Extensions;
using xHttpService.Constants;
using xHttpService.Providers;
using xHttpService.Interfaces;
using xHttpService.Configurations;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace xHttpService.DI {
public static partial class XDIHelperExtension {
/// <summary>
/// Retrieve XHttpService Configuration from AppSettings
/// </summary>
/// <param name="configuration"></param>
/// <returns></returns>
public static XHttpServiceConfiguration GetXHttpServiceConfiguration (this IConfiguration configuration) {
//
var xHttpSection = configuration.GetSection (ConfigurationNodeNames.HTTP_SERVICE_NODE_NAME);
return xHttpSection.Get<XHttpServiceConfiguration> ();
}
/// <summary>
/// Register XHttpService Configuration
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
public static void AddXHttpServiceConfiguration (
this IServiceCollection services,
IConfiguration configuration
) {
//
var httpServiceConfiguration = configuration.GetXHttpServiceConfiguration ();
services.AddXHttpServiceConfiguration(httpServiceConfiguration);
}
/// <summary>
/// Register XHttpService Configuration
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
public static void AddXHttpServiceConfiguration (
this IServiceCollection services,
XHttpServiceConfiguration configuration
) {
//
if (configuration.IsNull ()) {
Console.WriteLine($"xHttpService: there is no provided configurations, using default ...");
configuration = new XHttpServiceConfiguration ();
}
//
services.AddSingleton<XHttpServiceConfiguration> (configuration);
}
/// <summary>
/// Register XHttpService on DI ...
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
public static void AddXHttpService (
this IServiceCollection services,
IConfiguration configuration
) {
//
// Register Service Configuration ...
if (services.GetRegisteredService<XHttpServiceConfiguration> ().IsNull ()) {
services.AddXHttpServiceConfiguration (configuration);
}
//
// Register Service ...
services.AddSingleton<IXHttpProvider, XHttpProvider> ();
}
/// <summary>
/// Register XHttpService on DI ...
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
public static void AddXHttpService (
this IServiceCollection services,
XHttpServiceConfiguration configuration
) {
//
// Register Service Configuration ...
if (services.GetRegisteredService<XHttpServiceConfiguration> ().IsNull ()) {
services.AddXHttpServiceConfiguration (configuration);
}
//
// Register Service ...
services.AddSingleton<IXHttpProvider, XHttpProvider> ();
}
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xHttpService\Extensions\RestSharpExtensions.cs
using RestSharp;
using xModels.Dtos;
using xCommons.Constants;
using xCommons.Extensions;
namespace xHttpService.Extensions
{
public static partial class RestSharpExtensions
{
/// <summary>
/// Attach an instance of XQuery to a RestRequest object
/// </summary>
/// <param name="source"></param>
/// <param name="query"></param>
/// <returns></returns>
public static RestRequest AttachXQuery(
this RestRequest source,
XQuery query
)
{
//
if (query.IsNull())
{
return source;
}
//
// ContainsDetail ...
//
var containsDetailKey = System.Uri.EscapeDataString(nameof(query.ContainsDetail));
var containsDetailValue = query.ContainsDetail.AsHttpParamString();
source.AddQueryParameter(containsDetailKey, containsDetailValue);
//
// IsAscending ...
var isAscendingKey = System.Uri.EscapeDataString(nameof(query.IsAscending));
var isAscendingValue = query.IsAscending.AsHttpParamString();
source.AddQueryParameter(isAscendingKey, isAscendingValue);
//
// Filter ...
if (!query.Filter.IsNullOrEmpty())
{
//
var key = System.Uri.EscapeDataString(nameof(query.Filter));
var value = query.Filter.AsHttpParamString();
source.AddQueryParameter(key, value);
}
//
// SortBy ...
if (!query.SortBy.IsNullOrEmpty())
{
//
var key = System.Uri.EscapeDataString(nameof(query.SortBy));
var value = query.SortBy.AsHttpParamString();
source.AddQueryParameter(key, value);
}
//
// Page ...
if (query.Page > 0)
{
//
var key = System.Uri.EscapeDataString(nameof(query.Page));
var value = query.Page.AsHttpParamString();
source.AddQueryParameter(key, value);
}
//
// PageSize ...
if (query.PageSize > 0)
{
//
var key = System.Uri.EscapeDataString(nameof(query.PageSize));
var value = query.PageSize.AsHttpParamString();
source.AddQueryParameter(key, value);
}
//
return source;
}
/// <summary>
/// Add Dest User to Query Strings ...
/// </summary>
/// <param name="source"></param>
/// <param name="destUser"></param>
/// <returns></returns>
public static RestRequest AttachDestUser(
this RestRequest source,
string destUser
)
{
//
if (destUser.IsNullOrEmpty())
{
return source;
}
//
var destUserKey = System.Uri.EscapeDataString(nameof(destUser));
var destUserValue = destUser.AsHttpParamString();
source.AddQueryParameter(destUserKey, destUserValue);
//
return source;
}
/// <summary>
/// Add XPoweredBy Header to specific RestRequest Headers ...
/// </summary>
/// <param name="source"></param>
/// <param name="poweredByValue"></param>
/// <returns></returns>
public static RestRequest AddXPoweredBy(
this RestRequest source,
string poweredByValue
)
{
//
source.AddHeader(
XAuthorization.XPoweredBy,
poweredByValue
);
//
return source;
}
/// <summary>
/// Add XAccessToken Header to specific RestRequest Headers ...
/// </summary>
/// <param name="source"></param>
/// <param name="poweredByValue"></param>
/// <returns></returns>
public static RestRequest AddXAccessToken(
this RestRequest source,
string token
)
{
//
token = $"{XAuthentication.BEARER} {token}";
//
source.AddHeader(
XAuthorization.Header,
token
);
//
return source;
}
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xHttpService\Interfaces\IXHttpProvider.cs
using RestSharp;
using System.Net.Http;
using System.Threading.Tasks;
using xHttpService.Constants;
using xIdentityModels.Models;
using System.Collections.Generic;
namespace xHttpService.Interfaces {
public partial interface IXHttpProvider {
HttpClient GetHttpClient ();
RestClient GetRestClient (
string baseUrl = null,
string poweredByValue = null,
XTokenResponse tokens = null
);
RestRequest GetRestRequet (
string endpoint,
IDictionary<string, string> @params = null,
IDictionary<string, string> headers = null,
IDictionary<string, string> queryStrings = null
);
Task<T> RunRestRequest<T> (
RestClient client,
RestRequest request,
XHttpMethod method
);
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xHttpService\Providers\XHttpProvider.cs
using System;
using RestSharp;
using xModels.Base;
using System.Net.Http;
using xCommons.Constants;
using xCommons.Extensions;
using xExceptions.Constants;
using System.Threading.Tasks;
using xHttpService.Constants;
using xIdentityModels.Models;
using xHttpService.Interfaces;
using RestSharp.Authenticators;
using System.Collections.Generic;
using xHttpService.Configurations;
using Microsoft.Extensions.Logging;
namespace xHttpService.Providers {
public partial class XHttpProvider : XBaseClass, IXHttpProvider {
private readonly XHttpServiceConfiguration configuration;
public XHttpProvider (
ILoggerFactory loggerFactory,
XHttpServiceConfiguration configuration
) : base (loggerFactory) {
this.configuration = configuration;
}
//
#region Actions ...
/// <summary>
/// Get an Instance of Http Client
/// </summary>
/// <returns></returns>
public HttpClient GetHttpClient () {
//
HttpClient httpClient = null;
httpClient = new HttpClient ();
//
if (configuration.DisableSSLCheck) {
//
var httpHandler = new HttpClientHandler ();
httpHandler.ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
//
httpClient = new HttpClient (httpHandler);
}
//
// Set Timeout ...
httpClient.Timeout = TimeSpan
.FromSeconds (configuration.DefaultClientTimeout);
//
return httpClient;
}
/// <summary>
/// Get an Instance of RestClient
/// </summary>
/// <param name="baseUrl"></param>
/// <param name="poweredByValue"></param>
/// <param name="tokens"></param>
/// <returns></returns>
public RestClient GetRestClient (
string baseUrl = null,
string poweredByValue = null,
XTokenResponse tokens = null
) {
//
var client = new RestClient (baseUrl);
//
// Add XPowered By to Default Headers ...
if (!poweredByValue.IsNullOrEmpty ()) {
client.AddDefaultHeader (XAuthorization.XPoweredBy, poweredByValue);
}
//
// Set Authenticator ...
if (!tokens.IsNull () &&
!tokens.AccessToken.IsNullOrEmpty ()) {
client.Authenticator = new JwtAuthenticator (tokens.AccessToken);
}
//
// Refresh Token ...
if (!tokens.IsNull () &&
!tokens.RefreshToken.IsNullOrEmpty ()) {
client.AddDefaultHeader (XAuthorization.RefreshToken, tokens.RefreshToken);
}
//
// ExpiresAt ...
if (!tokens.IsNull () &&
tokens.ExpiresAt > 0) {
client.AddDefaultHeader (XAuthorization.ExpiresAt, tokens.ExpiresAt.AsHttpParamString ());
}
//
// Set Default Timeout ...
client.Timeout = configuration.DefaultClientTimeout;
//
// SSL Validation Handler ...
if (configuration.DisableSSLCheck) {
client.RemoteCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => true;
}
//
return client;
}
/// <summary>
/// Get an Instance of RestRequest
/// </summary>
/// <param name="endpoint"></param>
/// <param name="params"></param>
/// <param name="headers"></param>
/// <param name="queryStrings"></param>
/// <returns></returns>
public RestRequest GetRestRequet (
string endpoint,
IDictionary<string, string> @params = null,
IDictionary<string, string> headers = null,
IDictionary<string, string> queryStrings = null
) {
//
var url = endpoint.ToString ();
//
// Add Route Payloads ...
if (!@params.IsNull () && @params.Count > 0) {
//
var enumerator = @params.GetEnumerator ();
while (enumerator.MoveNext ()) {
//
var item = enumerator.Current;
//
url = url.Replace (item.Key, item.Value.ToUrlEncoded ());
}
}
//
// Add Query Strings ...
if (!queryStrings.IsNull () && queryStrings.Count > 0) {
//
url = $"{url}?";
var enumerator = queryStrings.GetEnumerator ();
while (enumerator.MoveNext ()) {
//
var item = enumerator.Current;
//
url += $"{item.Key}={item.Value.ToUrlEncoded ()}&";
}
//
// remove lates & ...
if (url.EndsWith ("&")) {
url = url.Substring (0, url.Length - 1);
}
}
//
// Create request ...
var request = new RestRequest (url, DataFormat.Json);
//
// Add Headers to request ...
if (!headers.IsNull () && headers.Count > 0) {
//
var enumerator = headers.GetEnumerator ();
while (enumerator.MoveNext ()) {
//
var item = enumerator.Current;
request.AddHeader (item.Key, item.Value);
}
}
//
return request;
}
/// <summary>
/// Handle Call Specific Request and retrieve Response
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="method"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public async Task<T> RunRestRequest<T> (
RestClient client,
RestRequest request,
XHttpMethod method
) {
//
var response = await Task.Run (() => {
//
IRestResponse resp = null;
//
// Call Endpoint Service using Specified Method ...
switch (method) {
//
// Get ...
case XHttpMethod.GET:
resp = client.Get (request);
break;
//
// Put ...
case XHttpMethod.PUT:
resp = client.Put (request);
break;
//
// Post ...
case XHttpMethod.POST:
resp = client.Post (request);
break;
//
// Delete ...
case XHttpMethod.DELETE:
resp = client.Delete (request);
break;
}
//
if (!resp.IsSuccessful) {
//
// UnAuthorized ...
if (resp.StatusCode == System.Net.HttpStatusCode.Unauthorized) {
XException.NotAuthorized.Throw ();
}
//
// NotFound ...
if (resp.StatusCode == System.Net.HttpStatusCode.NotFound) {
XException.NotFound.Throw ();
}
//
// Timeout ...
if (!resp.ErrorMessage.IsNullOrEmpty () &&
resp.ErrorMessage.Contains ("timed out")) {
XException.Timeout.Throw ();
}
//
// ErrorMessage ...
if (!resp.ErrorMessage.IsNullOrEmpty ()) {
throw new Exception (resp.ErrorMessage);
}
//
// Default Exception ...
XException.BadRequest.Throw ();
}
//
var result = resp.Content.FromJSON<T> ();
return result;
});
//
return response;
}
#endregion
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,727 @@
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xMessageService\Constants\ConfigurationNodeNames.cs
namespace xMessageService.Constants {
public partial struct ConfigurationNodeNames {
public const string MESSAGE_NODE_NAME = "MessageConfiguration";
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xMessageService\Constants\xMessageServiceConstants.cs
namespace xMessageService.Constants {
public struct xMessageServiceConstants {
public const string DEFAULT_EMPTY_PROVIDER = "EMPTY";
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xMessageService\DI\XDIHelperExtension.cs
using System;
using System.Linq;
using xCommons.Extensions;
using xMessageService.Models;
using xMessageService.Constants;
using xMessageService.Providers;
using System.Collections.Generic;
using xMessageService.Interfaces;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace xMessageService.DI {
public static partial class XDIHelperExtension {
/// <summary>
/// Retrieve XMessage Configuration from App Settings
/// </summary>
/// <param name="config"></param>
/// <returns></returns>
public static XMessageConfiguration GetXMessageConfiguration (this IConfiguration config) {
//
var xMessageConfigSection = config.GetSection (ConfigurationNodeNames.MESSAGE_NODE_NAME);
var xMessageConfiguration = xMessageConfigSection.Get<XMessageConfiguration> ();
//
var defaultMailConfiguration = new Dictionary<string, XMailConfiguration> {
["EMPTY"] =
new XMailConfiguration {
Title = xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER,
Host = "",
Port = 25,
EnableSsl = false,
Username = "",
Password = ""
}
};
//
var defaultSmsConfiguration = new Dictionary<string, XSmsConfiguration> {
["EMPTY"] =
new XSmsConfiguration {
ServiceUrl = "",
Username = "",
Password = "",
LineNumber = "",
UserApiKey = "",
SecretKey = "",
}
};
//
if (xMessageConfiguration.IsNull ()) {
//
xMessageConfiguration = new XMessageConfiguration {
DefaultSMSProvider = xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER,
DefaultMailProvider = xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER,
MailConfigurations = defaultMailConfiguration,
SmsConfigurations = defaultSmsConfiguration
};
//
// Warn Message is Default Configuration ...
Console.WriteLine ($"IMPORTANT: xMessageService Configuration is Default, so the Service can't works properly ...");
}
//
// Handle Default Mail Provider ...
if (xMessageConfiguration.DefaultMailProvider.IsNullOrEmpty ()) {
//
if (xMessageConfiguration.IsNull () ||
//
xMessageConfiguration.MailConfigurations.IsNull () ||
xMessageConfiguration.MailConfigurations.Count == 0) {
//
Console.WriteLine ("There is no defined Mail Provider Configurations ...");
xMessageConfiguration.DefaultMailProvider = xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER;
}
//
if (xMessageConfiguration.DefaultMailProvider == xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER) {
xMessageConfiguration.MailConfigurations = defaultMailConfiguration;
} else {
xMessageConfiguration.DefaultMailProvider = xMessageConfiguration.MailConfigurations.Keys.First ();
}
}
//
// Handle Default SMS Provider ...
if (xMessageConfiguration.DefaultSMSProvider.IsNullOrEmpty ()) {
//
if (xMessageConfiguration.IsNull () ||
xMessageConfiguration.SmsConfigurations.IsNull () ||
xMessageConfiguration.SmsConfigurations.Count == 0) {
//
Console.WriteLine ("There is no defined SMS Provider Configurations ...");
xMessageConfiguration.DefaultSMSProvider = xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER;
}
//
if (xMessageConfiguration.DefaultSMSProvider == xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER) {
xMessageConfiguration.SmsConfigurations = defaultSmsConfiguration;
} else {
xMessageConfiguration.DefaultSMSProvider = xMessageConfiguration.SmsConfigurations.Keys.First ();
}
}
//
return xMessageConfiguration;
}
/// <summary>
/// Register XMessage Service
/// </summary>
/// <param name="services"></param>
/// <param name="config"></param>
public static void AddXMessageService (this IServiceCollection services, IConfiguration config) {
//
var xMessageConfiguration = config.GetXMessageConfiguration ();
services.AddSingleton<XMessageConfiguration> (xMessageConfiguration);
//
// Register SMS Service ...
var xSmsService = new XSmsService (
xMessageConfiguration.DefaultSMSProvider,
xMessageConfiguration.SmsConfigurations
);
services.AddSingleton<IXSmsService> (xSmsService);
//
// Register Mail Service ...
var xMailService = new XMailService (
xMessageConfiguration.DefaultMailProvider,
xMessageConfiguration.MailConfigurations
);
services.AddSingleton<IXMailService> (xMailService);
//
// Register MEssage Provider ...
services.AddSingleton<IXMessageProvider, XMessageProvider> ();
}
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xMessageService\Interfaces\IXMailService.cs
using System.Threading.Tasks;
using xMessageService.Models;
namespace xMessageService.Interfaces {
public partial interface IXMailService {
Task SendMailAsync (
XMessage message,
string provider = null
);
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xMessageService\Interfaces\IXMessageProvider.cs
using System;
using System.Threading.Tasks;
using xMessageService.Models;
namespace xMessageService.Interfaces {
public partial interface IXMessageProvider {
bool IsReady (
bool? checkMailService = false,
bool? checkSmsService = false
);
Task SendMailAsync (
XMessage message,
string provider = null,
bool throwException = true,
Exception exception = null
);
Task SendSmsAsync (
XMessage message,
string provider = null,
bool throwException = true,
Exception exception = null
);
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xMessageService\Interfaces\IXSmsService.cs
using System.Threading.Tasks;
using xMessageService.Models;
namespace xMessageService.Interfaces {
public partial interface IXSmsService {
Task SendSmsAsync (
XMessage message,
string provider = null
);
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xMessageService\Models\XMailConfiguration.cs
namespace xMessageService.Models {
public partial class XMailConfiguration {
public string Title { get; set; }
public string Host { get; set; }
public int Port { get; set; }
public bool EnableSsl { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xMessageService\Models\XMessage.cs
using xModels.Base;
using System.Collections.Generic;
namespace xMessageService.Models {
public partial class XMessage : XBaseDto {
public HashSet<string> Recievers { get; set; } = new HashSet<string> ();
public string Message { get; set; }
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xMessageService\Models\XMessageConfiguration.cs
using System.Collections.Generic;
namespace xMessageService.Models {
public partial class XMessageConfiguration {
public string DefaultSMSProvider { get; set; }
public string DefaultMailProvider { get; set; }
public IDictionary<string, XMailConfiguration> MailConfigurations { get; set; }
public IDictionary<string, XSmsConfiguration> SmsConfigurations { get; set; }
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xMessageService\Models\XSmsConfiguration.cs
namespace xMessageService.Models {
public partial class XSmsConfiguration {
public string ServiceUrl { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string LineNumber { get; set; }
public string UserApiKey { get; set; }
public string SecretKey { get; set; }
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xMessageService\Providers\XMailService.cs
using System;
using System.Net;
using System.Net.Mail;
using xCommons.Extensions;
using xExceptions.Constants;
using System.Threading.Tasks;
using xMessageService.Models;
using System.Collections.Generic;
using xMessageService.Interfaces;
namespace xMessageService.Providers {
public partial class XMailService : IXMailService {
private SmtpClient smtpClient;
private readonly string defaultProvider;
private readonly IDictionary<string, XMailConfiguration> configurations;
public XMailService (
string defaultProvider,
IDictionary<string, XMailConfiguration> configurations
) {
this.defaultProvider = defaultProvider;
this.configurations = configurations;
//
// Get instance of SMTP Client ...
PrepareSmtpClient ();
}
public Task SendMailAsync (
XMessage message,
string provider = null
) {
//
// Run Send Mail Asynchronous ...
return Task.Run (() => {
SendMail (message, provider);
});
}
#region Private ...
/// <summary>
/// Validate and Retrieve Provider Configuration for Service
/// </summary>
/// <param name="provider"></param>
/// <returns></returns>
private XMailConfiguration GetConfiguration (string provider = null) {
//
if (provider.IsNullOrEmpty ()) {
provider = defaultProvider;
}
//
if (!configurations.Keys.Contains (provider)) {
XException.MessageServiceInitialFailed.Throw ();
}
//
return configurations[provider];
}
/// <summary>
/// Prepare SMTP Client by Using Specified/Default Provider
/// </summary>
/// <param name="provider"></param>
private void PrepareSmtpClient (string provider = null) {
//
var config = GetConfiguration (provider);
if (config.IsNull()) {
return;
}
//
smtpClient = new SmtpClient () {
Host = config.Host,
Port = config.Port,
EnableSsl = config.EnableSsl,
UseDefaultCredentials = false,
Credentials = new NetworkCredential (
config.Username,
config.Password)
};
}
/// <summary>
/// Prepare Mail Message With Specified/Default Provider
/// </summary>
/// <param name="message"></param>
/// <param name="provider"></param>
/// <returns></returns>
private MailMessage PrepareMailMessage (
XMessage message,
string provider = null
) {
//
if (message == null ||
message.Message.IsNullOrEmpty () ||
message.Recievers.Count == 0) {
XException.InvalidMessage.Throw ();
}
//
var config = GetConfiguration (provider);
var mailMessage = new MailMessage () {
From = new MailAddress (config.Username),
Subject = config.Title,
Body = message.Message
};
//
foreach (var reciever in message.Recievers) {
mailMessage.To.Add (reciever);
}
//
return mailMessage;
}
/// <summary>
/// Send Mail Using Specified/Default Provider
/// </summary>
/// <param name="message"></param>
/// <param name="provider"></param>
private void SendMail (
XMessage message,
string provider = null
) {
//
if (smtpClient.IsNull()) {
//
Console.WriteLine ($"SMTP Error: MessageServiceInitialFailed ...");
XException.MessageServiceInitialFailed.Throw ();
}
//
var mailMessage = PrepareMailMessage (message, provider);
//
try {
smtpClient.Send (mailMessage);
} catch (Exception ex) {
//
Console.WriteLine ($"SMTP Error: {ex.Message}");
XException.MessageServiceInitialFailed.Throw ();
}
}
#endregion
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xMessageService\Providers\XMessageProvider.cs
using System;
using xCommons.Extensions;
using xExceptions.Constants;
using System.Threading.Tasks;
using xMessageService.Models;
using xMessageService.Interfaces;
using Microsoft.Extensions.Logging;
namespace xMessageService.Providers {
public partial class XMessageProvider : IXMessageProvider {
private readonly XMessageConfiguration configuration;
private readonly ILogger<XMessageProvider> logger;
private readonly IXSmsService smsService;
private readonly IXMailService mailService;
public XMessageProvider (
XMessageConfiguration configuration,
ILogger<XMessageProvider> logger,
IXSmsService smsService,
IXMailService mailService
) {
this.configuration = configuration;
this.logger = logger;
this.smsService = smsService;
this.mailService = mailService;
}
/// <summary>
/// Send a Mail Message
/// </summary>
/// <param name="message"></param>
/// <param name="provider"></param>
/// <param name="throwException"></param>
/// <param name="exception"></param>
/// <returns></returns>
public async Task SendMailAsync (
XMessage message,
string provider = null,
bool throwException = true,
Exception exception = null
) {
//
if (exception.IsNull ()) {
exception = XException.ActionFailed.ToException ();
}
//
try {
await mailService.SendMailAsync (message, provider);
} catch (Exception ex) {
//
logger.LogError ($"Exeption: {ex.Message}");
//
if (throwException) {
throw exception;
}
}
}
/// <summary>
/// Send SMS Message
/// </summary>
/// <param name="message"></param>
/// <param name="provider"></param>
/// <param name="throwException"></param>
/// <param name="exception"></param>
/// <returns></returns>
public async Task SendSmsAsync (
XMessage message,
string provider = null,
bool throwException = true,
Exception exception = null
) {
//
if (exception.IsNull ()) {
exception = XException.ActionFailed.ToException ();
}
//
try {
await smsService.SendSmsAsync (message, provider);
} catch {
if (throwException) {
throw exception;
}
}
}
/// <summary>
/// Check Message Provider is Ready or not
/// </summary>
/// <param name="checkMailService"></param>
/// <param name="checkSmsService"></param>
/// <returns></returns>
public bool IsReady (
bool? checkMailService = false,
bool? checkSmsService = false
) {
//
var result = true;
//
// Check Mail Service ...
if (checkMailService.HasValue &&
checkMailService.GetValueOrDefault ()) {
result = result && mailService != null;
}
//
// Check Sms Service ...
if (checkSmsService.HasValue &&
checkSmsService.GetValueOrDefault ()) {
result = result && smsService != null;
}
//
return result;
}
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xMessageService\Providers\XSmsService.cs
using System;
using RestSharp;
using PhoneNumbers;
using xCommons.Extensions;
using xExceptions.Constants;
using System.Threading.Tasks;
using xMessageService.Models;
using System.Collections.Generic;
using xMessageService.Interfaces;
namespace xMessageService.Providers {
public partial class XSmsService : IXSmsService {
private string serviceUrl;
private readonly string defaultProvider;
private readonly IDictionary<string, XSmsConfiguration> configurations;
public XSmsService (
string defaultProvider,
IDictionary<string, XSmsConfiguration> configurations
) {
this.defaultProvider = defaultProvider;
this.configurations = configurations;
//
PrepareServiceUrl ();
}
public Task SendSmsAsync (
XMessage message,
string provider = null
) {
//
// Run Send SMS Asynchronous ...
return Task.Run (() => {
SendSMS (message, provider);
});
}
#region Private ...
/// <summary>
/// Prepare Service Url for Specified/Default Provider
/// </summary>
/// <param name="provider"></param>
private void PrepareServiceUrl (string provider = null) {
//
if (provider.IsNullOrEmpty ()) {
provider = defaultProvider;
}
//
var config = GetConfiguration (provider);
if (config.IsNull ()) {
return;
}
//
serviceUrl = config.ServiceUrl;
serviceUrl += "&username=" + config.Username + "&password=" + config.Password + "&";
}
/// <summary>
/// Prepare Service url for Integrating XMessage by
/// using Specified/Default Provider Configurations
/// </summary>
/// <param name="message"></param>
/// <param name="provider"></param>
private void PrepareSmsMessage (
XMessage message,
string provider = null
) {
//
if (message == null ||
message.Message.IsNullOrEmpty () ||
message.Recievers.Count == 0) {
XException.InvalidMessage.Throw ();
}
//
// Validating Mobile Numbers ...
var phoneNumberUtil = PhoneNumbers.PhoneNumberUtil.GetInstance ();
var recieversList = "";
//
PrepareServiceUrl (provider);
//
foreach (var reciever in message.Recievers) {
try {
//
var mPhoneNumber = phoneNumberUtil.Parse (reciever, null);
if (phoneNumberUtil.IsValidNumber (mPhoneNumber)) {
//
// Format Phone Numbers to the '+989120000000' pattern ...
var recieverPhone = phoneNumberUtil.Format (mPhoneNumber, PhoneNumberFormat.E164);
if (recieversList.Length > 0) {
recieversList += ",";
}
recieversList += reciever;
}
} catch { }
}
//
var config = GetConfiguration ();
//
// Preparing All Content and Configurations for Sending Messages ...
serviceUrl += "to=" +
recieversList +
"&message=" +
message.Message +
"&from=" +
config.LineNumber +
"&flash=" +
false;
}
/// <summary>
/// Send a Message as SMS by Specified/Default Provider
/// </summary>
/// <param name="message"></param>
/// <param name="provider"></param>
private void SendSMS (
XMessage message,
string provider = null
) {
//
if (serviceUrl.IsNullOrEmpty ()) {
//
Console.WriteLine ($"SMS Error: MessageServiceInitialFailed ...");
XException.MessageServiceInitialFailed.Throw ();
}
//
try {
//
// preparing Service Url based on message data ...
PrepareSmsMessage (message, provider);
//
// Implementing Service Calling Using RestSharp Library ...
// We Select RestSharp because it supports Sync/Async Calls ...
var client = new RestClient (serviceUrl);
var request = new RestRequest (Method.POST);
//
// Send Request ...
var response = client.Execute (request);
} catch (Exception ex) {
Console.WriteLine ($"SMS Error: {ex.Message}");
XException.MessageServiceInitialFailed.Throw ();
}
}
/// <summary>
/// Validate and Retrieve Provider Configuration for Service
/// </summary>
/// <param name="provider"></param>
/// <returns></returns>
private XSmsConfiguration GetConfiguration (string provider = null) {
//
if (
provider.IsNullOrEmpty () ||
provider.ToNormalString () == "empty"
) {
//
provider = defaultProvider;
return null;
}
//
if (!configurations.Keys.Contains (provider)) {
XException.MessageServiceInitialFailed.Throw ();
}
//
return configurations[provider];
}
#endregion
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,574 @@
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\Configurations\XTermsAndConditionsServiceConfiguration.cs
using xDataService.Constants;
using System.Collections.Generic;
using xStringService.Models.Dtos;
namespace xTermsAndConditionsService.Configurations
{
/// <summary>
/// Terms and Conditions Configuration ...
/// </summary>
public class XTermsAndConditionsServiceConfiguration
{
/// <summary>
/// Terms And Conditions Resource Title in XString Table ...
/// </summary>
public string ResourceTitle { get; set; } = "terms_and_conditions";
/// <summary>
/// Seeding Mode for Terms and Conditions ...
/// </summary>
public XDbSeedingMode SeedingMode { get; set; } = XDbSeedingMode.AddIfNotExists;
/// <summary>
/// Default Terms and Conditions for Add to Databse on App Startup ...
/// </summary>
public List<XStringDto> TermsAndConditions { get; set; } = null;
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\Constants\ConfigurationNodeNames.cs
namespace xTermsAndConditionsService.Constants
{
public partial struct ConfigurationNodeNames
{
public const string TERMS_AND_CONDITIONS_SERVICE_NODE = "TermsAndConditionsService";
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\Constants\XTermsAndConditionsServiceConstants.cs
namespace xTermsAndConditionsService.Constants
{
public struct XTermsAndConditionsServiceConstants
{
//
// Service Extensions Log Tag ...
public const string XTermsAndConditionsServiceDILogTag = "XTermsAndConditionsService";
//
// Table Mapping Identifiers ...
//
// GraphQL Collection Mappings ...
//
// Hubs ...
//
// Custome Constants ...
//
// EF Repositories Helper Extensions ...
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\Controllers\XTermsAndConditionsControllerBase.cs
using xCommons.Providers;
using xCommons.Configurations;
using xStringService.Controllers;
using xIdentityService.Interfaces;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Authorization;
using xTermsAndConditionsService.Interfaces;
namespace xTermsAndConditionsService.Controllers
{
/// <summary>
/// a Base Controller Implementation for Providing Base Actions of
/// Terms and Conditions Provided Actions ...
/// </summary>
[AllowAnonymous]
public abstract class XTermsAndConditionsControllerBase : XBaseResourceProviderControllerBase<IXTermsAndConditionsProvider>, IXTermsAndConditionsController
{
protected XTermsAndConditionsControllerBase(
ILogger<XTermsAndConditionsControllerBase> logger,
XAppConfiguration appConfiguration,
IXIdentityProvider identityProvider,
XValidationProvider validationProvider,
IXTermsAndConditionsProvider provider
) : base(
logger,
appConfiguration,
identityProvider,
validationProvider,
provider
)
{ }
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\DI\XDIHelperExtension.cs
using System;
using System.Linq;
using xPushService.DI;
using xCommons.Extensions;
using xPushService.Helpers;
using xExceptions.Constants;
using xCommons.Configurations;
using Microsoft.AspNetCore.Builder;
using xStringService.Interfaces.Dtos;
using xTermsAndConditionsService.Hubs;
using Microsoft.Extensions.Configuration;
using xTermsAndConditionsService.Constants;
using xTermsAndConditionsService.Providers;
using xTermsAndConditionsService.Interfaces;
using Microsoft.Extensions.DependencyInjection;
using xTermsAndConditionsService.Configurations;
namespace xTermsAndConditionsService.DI
{
public static class XDIHelperExtension
{
/// <summary>
/// Retrieve xTermsAndConditionsService Configuration from AppSettings ...
/// </summary>
/// <param name="configuration"></param>
/// <returns></returns>
public static XTermsAndConditionsServiceConfiguration GetXTermsAndConditionsServiceConfiguration(
this IConfiguration configuration
)
{
//
var section = configuration.GetSection(ConfigurationNodeNames.TERMS_AND_CONDITIONS_SERVICE_NODE);
var result = section.Get<XTermsAndConditionsServiceConfiguration>();
//
return result;
}
/// <summary>
/// Extract xTermsAndConditionsService Configurations from AppSettings
/// and Register it in DI ...
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
public static void AddXTermsAndConditionsConfiguration(
this IServiceCollection services,
IConfiguration configuration
)
{
//
var serviceConfiguration = configuration.GetXTermsAndConditionsServiceConfiguration();
services.AddXTermsAndConditionsConfiguration(serviceConfiguration);
}
/// <summary>
/// Register xTermsAndConditionsService Configurations in DI ...
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
public static void AddXTermsAndConditionsConfiguration(
this IServiceCollection services,
XTermsAndConditionsServiceConfiguration configuration
)
{
//
if (configuration.IsNull())
{
Log($"there is no provided configurations, using default ...");
configuration = new XTermsAndConditionsServiceConfiguration();
}
//
services.AddSingleton(configuration);
}
/// <summary>
/// Register TermsConditions Provider ...
/// </summary>
/// <param name="services"></param>
/// <param name="configuration">provided AppSettings Configuration</param>
/// <param name="lifeTime"></param>
public static void AddXTermsConditionsService(
this IServiceCollection services,
IConfiguration configuration,
ServiceLifetime lifeTime = ServiceLifetime.Transient
)
{
//
AddTermsAndConditionsService(
services: services,
lifeTime: lifeTime,
configuration: configuration
);
}
/// <summary>
/// Register TermsConditions Provider ...
/// </summary>
/// <param name="services"></param>
/// <param name="configuration">provided xTermsAndConditionsService Configuration</param>
/// <param name="lifeTime"></param>
public static void AddXTermsConditionsService(
this IServiceCollection services,
XTermsAndConditionsServiceConfiguration configuration,
ServiceLifetime lifeTime = ServiceLifetime.Transient
)
{
//
AddTermsAndConditionsService(
services: services,
lifeTime: lifeTime,
serviceConfiguration: configuration
);
}
public static void UseXTermsAndConditionsService(
this IApplicationBuilder app
)
{
//
// Create an Scope for Services Acess ...
using (var scope = app.ApplicationServices.CreateScope())
{
//
// Inject Requirements ...
var stringProvider = scope.ServiceProvider.GetService<IXStringServiceProvider>();
var serviceConfiguration = scope.ServiceProvider.GetService<XTermsAndConditionsServiceConfiguration>();
//
// Using XTerms Hub ...
var pushHelper = new XPushServiceHelper();
pushHelper.AddHub<XTermsHub>("terms");
app.UseXPushService(pushHelper);
//
// Validate Requirements Exists ...
var isValid =
!stringProvider.IsNull() &&
!serviceConfiguration.IsNull() &&
!serviceConfiguration.TermsAndConditions.IsNull() &&
serviceConfiguration.TermsAndConditions.HasChild() &&
serviceConfiguration.SeedingMode != xDataService.Constants.XDbSeedingMode.None;
if (isValid)
{
//
// Prepare Default Terms ...
serviceConfiguration
.TermsAndConditions
.ToList()
.ForEach(t => t.ResourceTitle = serviceConfiguration.ResourceTitle);
//
// Loop through Terms and Conditions for Seeding ...
foreach (var item in serviceConfiguration.TermsAndConditions)
{
//
// Detect Exists Item ...
var exists = stringProvider.FindOneAsync(
predicate: x =>
x.Language == item.Language &&
x.ResourceTitle == item.ResourceTitle
)
.GetAwaiter()
.GetResult();
//
var isExists = !exists.IsNullOrDefault();
//
switch (serviceConfiguration.SeedingMode)
{
//
case xDataService.Constants.XDbSeedingMode.AddOrUpdate:
//
// Update Exists by new Provided ...
exists.TranslatedValue = item.TranslatedValue;
exists = stringProvider.UpdateAsync(
id: exists.Id,
item: exists,
saveChanges: true
)
.GetAwaiter()
.GetResult();
//
Log($"Refresh Terms and Conditions of {exists.Language}");
break;
//
case xDataService.Constants.XDbSeedingMode.AddIfNotExists:
//
if (!isExists)
{
//
exists = stringProvider.AddAsync(
item: item,
saveChanges: true
)
.GetAwaiter()
.GetResult();
//
Log($"Add Terms and Conditions for {exists.Language}");
}
break;
}
}
}
}
}
//
#region Private ...
/// <summary>
/// a LogTag for Service ...
/// </summary>
private static string XLogTag = XTermsAndConditionsServiceConstants.XTermsAndConditionsServiceDILogTag;
/// <summary>
/// print a log in Console ...
/// </summary>
/// <param name="message"></param>
private static void Log(string message)
{
Console.WriteLine($"{XLogTag} => {message}");
}
/// <summary>
/// Add Terms and Conditions Provider Service ...
/// </summary>
/// <param name="services"></param>
/// <param name="lifeTime"></param>
private static void AddTermsAndConditionsService(
IServiceCollection services,
IConfiguration configuration = null,
XTermsAndConditionsServiceConfiguration serviceConfiguration = null,
ServiceLifetime lifeTime = ServiceLifetime.Transient
)
{
//
var exception = XException.InvalidArgs.ToException(); ;
//
// Services ...
var isServicesExists = !services.IsNull();
if (!isServicesExists)
{
//
Log("AddTermsAndConditions failed, services not provided ...");
throw exception;
}
//
// Validate Configuration ...
var isConfigValid = !configuration.IsNull() || !serviceConfiguration.IsNull();
if (!isConfigValid)
{
//
Log("AddTermsAndConditions failed, configuration not provided ...");
throw exception;
}
//
// Lifetime ...
var isLifetimeExists = !lifeTime.IsNull();
if (!isLifetimeExists)
{
//
Log("AddTermsAndConditions failed, lifetime not provided ...");
throw exception;
}
//
// Check Dependencies Exists ...
var isDependenciesPassed = !services
.GetRegisteredService<XAppConfiguration>()
.IsNull() &&
!services.GetRegisteredService<IXStringServiceProvider>()
.IsNull();
if (!isDependenciesPassed)
{
//
Log("AddTermsAndConditions failed due Dependencies Issues, IXStringServiceProvider or XAppConfiguration not provided ...");
throw exception;
}
//
// Service Configuration Handler ...
if (serviceConfiguration.IsNull())
{
serviceConfiguration = configuration.GetXTermsAndConditionsServiceConfiguration();
}
services.AddXTermsAndConditionsConfiguration(serviceConfiguration);
//
// Register Resource Providers ...
services.Add(new ServiceDescriptor(typeof(IXTermsAndConditionsProvider), typeof(XTermsAndConditionsProvider), lifeTime));
}
#endregion
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\Hubs\XTermsHub.cs
using System.Threading;
using xPushService.Base;
using xCommons.Extensions;
using System.Threading.Tasks;
using xPushService.Constants;
using xStringService.Models.Dtos;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
namespace xTermsAndConditionsService.Hubs
{
/// <summary>
/// a Hub for Notifing Terms and Conditions Events to Connected Clients ...
/// </summary>
public class XTermsHub : XBaseHub
{
public XTermsHub(
ILogger<XTermsHub> logger
) : base(logger)
{
}
//
#region Hub Actions ...
/// <summary>
/// Add Specified Terms and Conditions ...
/// </summary>
/// <param name="entity"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task Add(
XStringDto entity,
CancellationToken cancellationToken = default
)
{
//
var connectionId = Context.ConnectionId;
if (!connectionId.IsNullOrEmpty())
{
//
await Clients
.AllExcept(connectionId)
.SendAsync(
XBaseEntityHubAction.Add.GetStringValue(),
entity.ToJSON(camelCase: true),
connectionId,
cancellationToken
);
}
}
/// <summary>
/// Deleted Specified Terms and Conditions ...
/// </summary>
/// <param name="entity"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task Delete(
XStringDto entity,
CancellationToken cancellationToken = default
)
{
//
var connectionId = Context.ConnectionId;
if (!connectionId.IsNullOrEmpty())
{
//
await Clients
.AllExcept(connectionId)
.SendAsync(
XBaseEntityHubAction.Delete.GetStringValue(),
entity.ToJSON(camelCase: true),
connectionId,
cancellationToken
);
}
}
/// <summary>
/// Notify an Entity Updated ...
/// </summary>
/// <param name="entity"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task Update(
XStringDto entity,
CancellationToken cancellationToken = default
)
{
//
var connectionId = Context.ConnectionId;
if (!connectionId.IsNullOrEmpty())
{
//
await Clients
.AllExcept(connectionId)
.SendAsync(
XBaseEntityHubAction.Update.GetStringValue(),
entity.ToJSON(camelCase: true),
connectionId,
cancellationToken
);
}
}
#endregion
}
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\Interfaces\IXTermsAndConditionsController.cs
using xStringService.Interfaces;
namespace xTermsAndConditionsService.Interfaces
{
/// <summary>
/// an Interface for Describing Terms and Conditions Management Controller Actions ...
/// </summary>
public interface IXTermsAndConditionsController : IXBaseResourceProviderController
{ }
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\Interfaces\IXTermsAndConditionsProvider.cs
using xStringService.Interfaces;
namespace xTermsAndConditionsService.Interfaces
{
/// <summary>
/// an Interface for Describing Terms and Conditions Management Provided Actions ...
/// </summary>
public interface IXTermsAndConditionsProvider : IXResourceProvider
{ }
}
### FILE: C:\Users\hkhazaeeasl\Documents\Projets\XProjects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\Providers\XTermsAndConditionsProvider.cs
using xStringService.Providers;
using xStringService.Interfaces.Dtos;
using xTermsAndConditionsService.Interfaces;
using xTermsAndConditionsService.Configurations;
namespace xTermsAndConditionsService.Providers
{
/// <summary>
/// an Implementation of Terms and Conditions Management Provided Actions ...
/// </summary>
public class XTermsAndConditionsProvider : XBaseResourceProvider, IXTermsAndConditionsProvider
{
public XTermsAndConditionsProvider(
IXStringServiceProvider provider,
XTermsAndConditionsServiceConfiguration configuration
) : base(
resource: "NULL",
provider
)
{
//
// Since we nee to Set Resource Title of Terms and Conditions
// during instancing using Provided Configurations ...
// set this value here ...
resource = configuration.ResourceTitle;
}
}
}
File diff suppressed because it is too large Load Diff
+261 -122
View File
@@ -463,41 +463,6 @@
"checkboxes": [],
"comments": []
},
{
"id": "1c3b121a-08d5-44b1-a991-4fc95f7ebe15",
"listId": "1706107f-ace2-4240-aa32-3d2031264a41",
"title": "Add AiService Mechanism ...",
"description": "",
"labels": [],
"checkboxes": [
{
"id": "c6674a4a-2487-4ec1-b674-d5fba8fedbee",
"title": "Create Base AI Service Material",
"checked": false
}
],
"comments": []
},
{
"id": "7af4f8b4-5a5a-43d2-b154-026e13da1d92",
"listId": "1706107f-ace2-4240-aa32-3d2031264a41",
"title": "Add Support for IXAiDataProvider",
"description": "Data Provider needs to Provides all requirements of Data Manipulation of AI Models and also provides Default Project and Default Conversation for Data Handling ...",
"labels": [],
"checkboxes": [
{
"id": "e2abaca1-99cf-424a-870f-21f330445bbe",
"title": "Integrate all requirement actions",
"checked": false
},
{
"id": "f496ccaf-6d9e-43df-ab9e-2e2192bc7ef1",
"title": "Prepare Seeding Default Project and Default Conversation ",
"checked": false
}
],
"comments": []
},
{
"id": "c072d0a6-f2be-419b-8fb2-d127d73dd709",
"listId": "1706107f-ace2-4240-aa32-3d2031264a41",
@@ -507,30 +472,6 @@
"checkboxes": [],
"comments": []
},
{
"id": "d8a9ddfc-0b53-4313-aac1-4afdf5261691",
"listId": "1706107f-ace2-4240-aa32-3d2031264a41",
"title": "Add Mapper Configs for AI Models ...",
"description": "",
"labels": [],
"checkboxes": [],
"comments": []
},
{
"id": "5930bcbb-4c5a-493a-afbe-1afcfd18501f",
"listId": "1706107f-ace2-4240-aa32-3d2031264a41",
"title": "Fix Enricher Issues on X AiData Service ...",
"description": "",
"labels": [],
"checkboxes": [
{
"id": "4752f2ad-1457-4acc-ad5b-ab4e853fd66b",
"title": "Remove all Enrichers from Repository Services ...",
"checked": false
}
],
"comments": []
},
{
"id": "b6d82a67-f038-4d7e-a624-4461c0b63f35",
"listId": "1706107f-ace2-4240-aa32-3d2031264a41",
@@ -540,15 +481,6 @@
"checkboxes": [],
"comments": []
},
{
"id": "17bbcc83-4330-4631-a08a-521c99047ebc",
"listId": "1706107f-ace2-4240-aa32-3d2031264a41",
"title": "Try to Implement Audio, Video and Document Content Extraction in XAi Service ...",
"description": "",
"labels": [],
"checkboxes": [],
"comments": []
},
{
"id": "7785aecc-0b27-4428-a50b-03c046c5a0d6",
"listId": "1706107f-ace2-4240-aa32-3d2031264a41",
@@ -557,15 +489,6 @@
"labels": [],
"checkboxes": [],
"comments": []
},
{
"id": "5f8a8b2f-06ce-46a9-8101-f14e34b51249",
"listId": "1706107f-ace2-4240-aa32-3d2031264a41",
"title": "implement Api Key Mechanism using IDServer and Clients sides ...",
"description": "",
"labels": [],
"checkboxes": [],
"comments": []
}
]
},
@@ -573,6 +496,95 @@
"id": "171f43f3-ae73-49b7-9239-9f3fb695cc2b",
"title": "InProgress",
"cards": [
{
"id": "5f8a8b2f-06ce-46a9-8101-f14e34b51249",
"listId": "171f43f3-ae73-49b7-9239-9f3fb695cc2b",
"title": "implement Api Key Mechanism using IDServer and Clients sides ...",
"description": "",
"labels": [],
"checkboxes": [
{
"id": "66cf978d-2d2f-4bbc-a97c-504ba0ddba65",
"title": "R&D about how i can attach a Custom Entity in IDS Dbs;",
"checked": true
},
{
"id": "1ef7dc8c-5b19-499e-803e-500885eb3bb9",
"title": "Add Entites Related in XIdentityModels ...",
"checked": true
},
{
"id": "1509ec76-9372-40a1-b387-1f8e005d507e",
"title": "Add DbContext DbSets and Model Builder Configurations ...",
"checked": true
},
{
"id": "b4ed3da1-d967-4a11-b2c3-88a3c0aca5ad",
"title": "Test Migration Identity Dbs ...",
"checked": true
},
{
"id": "754d66a3-4e19-40c2-98e3-f7744da444f3",
"title": "Implements Application Manager ...",
"checked": true
},
{
"id": "4c68e1d8-4f1e-4415-913b-218a4e89666b",
"title": "Implement Requirement Tools for Safe Api Key ...",
"checked": true
},
{
"id": "14292514-bd6e-42d4-a8b7-24a91bda9827",
"title": "Implement XApiKeyValidator ...",
"checked": true
},
{
"id": "ba8bfae2-130d-4020-bd1b-daabc5199ddf",
"title": "Implement Rate Limiter ...",
"checked": true
},
{
"id": "d7c9ecdc-15e4-4efe-a593-17601a86db5c",
"title": "Implement Authentication Handler for Api Keys ...",
"checked": true
},
{
"id": "246701eb-7e7b-4756-be04-3af0e490b87b",
"title": "Register Authentication Middleware in App Startup ...",
"checked": true
},
{
"id": "ab67e976-6080-44ee-b49e-54aada0724a3",
"title": "Adding Dto and Enrichment Mechanism ...",
"checked": true
},
{
"id": "2828eb54-a27d-40cd-ac17-cadc06d1a68e",
"title": "Add Application Controller in Ids ...",
"checked": true
},
{
"id": "2e94cc30-d26c-4db6-bff9-8306de8e577c",
"title": "Test All Senarios inside Ids Itself ...",
"checked": false
},
{
"id": "6fafa434-7b2f-44d5-b1d8-53109d43dfd0",
"title": "Others ...",
"checked": false
}
],
"comments": []
},
{
"id": "17bbcc83-4330-4631-a08a-521c99047ebc",
"listId": "171f43f3-ae73-49b7-9239-9f3fb695cc2b",
"title": "Try to Implement Audio, Video and Document Content Extraction in XAi Service ...",
"description": "",
"labels": [],
"checkboxes": [],
"comments": []
},
{
"id": "5d103817-846f-477f-a381-50e86c3cc7d4",
"listId": "171f43f3-ae73-49b7-9239-9f3fb695cc2b",
@@ -583,52 +595,60 @@
{
"id": "18ec2658-7924-4a19-83d2-e0bf99a4c402",
"title": "Projects",
"checked": false
"checked": true
},
{
"id": "cae40bd2-c3b5-42ca-9156-5b77abb41719",
"title": "Conversations",
"checked": false
"checked": true
},
{
"id": "3261fcc6-7b2e-466d-b5ab-08b061dbdd18",
"title": "Mechanism of Base Handling ",
"checked": false
"checked": true
},
{
"id": "d055d603-aab1-4a5a-805c-746f9d13549b",
"title": "Base Controller Implementation",
"checked": true
},
{
"id": "4a21467d-fcd5-4d3f-8c9d-00ddfb38c6fb",
"title": "in Load Projects, Default Conversation not Implemented",
"checked": true
},
{
"id": "58d40c54-a3f9-4232-90ba-4d5c4fdbe609",
"title": "Add Pagination Supporting for Messages",
"checked": true
},
{
"id": "fa9a32b5-db0e-4774-a1cd-dce9f8e2fb4e",
"title": "Add Pagination Supporting for Projects",
"checked": true
},
{
"id": "4a1b063b-2089-47c9-9dbc-ad36d47d3b57",
"title": "Add Pagination Supporting for Conversations",
"checked": true
},
{
"id": "ff4a57fa-81e1-4ea3-a8dc-b924637c2dc2",
"title": "Adding Support for LoadingDefault Project's Conversations and Messages Field",
"checked": true
},
{
"id": "07602ca6-38b8-4d19-b0f6-8730e11de1df",
"title": "Add Support for Files Attachment in AI Asking ",
"checked": true
},
{
"id": "149c4343-16ab-4ac8-899b-23d202000b85",
"title": "Add Support For File Content Extracting based On Level 1 and Level 2",
"checked": false
}
],
"comments": []
},
{
"id": "bbefacad-f6e5-42cf-866f-7ce4fa38a549",
"listId": "171f43f3-ae73-49b7-9239-9f3fb695cc2b",
"title": "Implement Content Provider Base actions for add or update Content using resources ...",
"description": "",
"labels": [],
"checkboxes": [],
"comments": []
},
{
"id": "07747425-25d1-478b-ba84-c56e978574b3",
"listId": "171f43f3-ae73-49b7-9239-9f3fb695cc2b",
"title": "Add Support for Add/Update Content by Providing Locales in Content Service ...",
"description": "",
"labels": [],
"checkboxes": [],
"comments": []
},
{
"id": "d3e18512-b4a6-4049-974b-af1b0f27cc47",
"listId": "171f43f3-ae73-49b7-9239-9f3fb695cc2b",
"title": "add Support for reading Tags in Content Enrichers ...",
"description": "",
"labels": [],
"checkboxes": [],
"comments": []
}
]
},
@@ -637,27 +657,56 @@
"title": "Test",
"cards": [
{
"id": "07e06cce-8c53-479c-8a91-d311b228eba5",
"id": "07747425-25d1-478b-ba84-c56e978574b3",
"listId": "8c15334f-3b2e-4eca-999e-4c887cb628e9",
"title": "Override Content Base Actions for using Enrichers ...",
"title": "Add Support for Add/Update Content by Providing Locales in Content Service ...",
"description": "",
"labels": [],
"checkboxes": [],
"comments": []
},
{
"id": "d82776f1-55fe-4977-9db0-f23d17a1ea37",
"id": "bbefacad-f6e5-42cf-866f-7ce4fa38a549",
"listId": "8c15334f-3b2e-4eca-999e-4c887cb628e9",
"title": "Adding Support for XML Documents Integration for All Exists Assemblies in Swagger",
"title": "Implement Content Provider Base actions for add or update Content using resources ...",
"description": "",
"labels": [],
"checkboxes": [],
"comments": []
},
{
"id": "a42dd42b-8fe0-4853-955e-e06770784e41",
"id": "d3e18512-b4a6-4049-974b-af1b0f27cc47",
"listId": "8c15334f-3b2e-4eca-999e-4c887cb628e9",
"title": "Add Support for Kafka in xEvent Service ...",
"title": "add Support for reading Tags in Content Enrichers ...",
"description": "",
"labels": [],
"checkboxes": [],
"comments": []
},
{
"id": "9757ac0e-2aff-44a1-a0db-7ee4468def1b",
"listId": "8c15334f-3b2e-4eca-999e-4c887cb628e9",
"title": "Detect a Very Good GGUF Model for Performing OCR on Bynary Contents ...",
"description": "",
"labels": [],
"checkboxes": [],
"comments": [
{
"id": "3b3491c8-8f59-4b9e-b05f-51eccab7fd54",
"comment": "this is done by do R&D about this.\ni found my default Thinking model is support Vissioning.\nfor this we have to download gguf for mmproj and attach it to serving command.\ni permor downloading actions and then try to update running files."
}
]
}
]
},
{
"id": "91b2b60d-a61b-4d81-bfc8-44ca4ad152ba",
"title": "Done",
"cards": [
{
"id": "d5e2e05d-4205-452f-9899-5ad6f95007a6",
"listId": "91b2b60d-a61b-4d81-bfc8-44ca4ad152ba",
"title": "Add Support for Tags in Content Service ...",
"description": "",
"labels": [],
"checkboxes": [],
@@ -665,7 +714,7 @@
},
{
"id": "58804ad9-952c-4685-9d59-ca4b67188240",
"listId": "8c15334f-3b2e-4eca-999e-4c887cb628e9",
"listId": "91b2b60d-a61b-4d81-bfc8-44ca4ad152ba",
"title": "Add Support for ZeroMQ in xEventService ...",
"description": "",
"labels": [],
@@ -673,20 +722,110 @@
"comments": []
},
{
"id": "d5e2e05d-4205-452f-9899-5ad6f95007a6",
"listId": "8c15334f-3b2e-4eca-999e-4c887cb628e9",
"title": "Add Support for Tags in Content Service ...",
"id": "a42dd42b-8fe0-4853-955e-e06770784e41",
"listId": "91b2b60d-a61b-4d81-bfc8-44ca4ad152ba",
"title": "Add Support for Kafka in xEvent Service ...",
"description": "",
"labels": [],
"checkboxes": [],
"comments": []
}
]
},
{
"id": "91b2b60d-a61b-4d81-bfc8-44ca4ad152ba",
"title": "Done",
"cards": []
"id": "d82776f1-55fe-4977-9db0-f23d17a1ea37",
"listId": "91b2b60d-a61b-4d81-bfc8-44ca4ad152ba",
"title": "Adding Support for XML Documents Integration for All Exists Assemblies in Swagger",
"description": "",
"labels": [],
"checkboxes": [],
"comments": []
},
{
"id": "07e06cce-8c53-479c-8a91-d311b228eba5",
"listId": "91b2b60d-a61b-4d81-bfc8-44ca4ad152ba",
"title": "Override Content Base Actions for using Enrichers ...",
"description": "",
"labels": [],
"checkboxes": [],
"comments": []
},
{
"id": "5930bcbb-4c5a-493a-afbe-1afcfd18501f",
"listId": "91b2b60d-a61b-4d81-bfc8-44ca4ad152ba",
"title": "Fix Enricher Issues on X AiData Service ...",
"description": "",
"labels": [],
"checkboxes": [
{
"id": "4752f2ad-1457-4acc-ad5b-ab4e853fd66b",
"title": "Remove all Enrichers from Repository Services ...",
"checked": true
}
],
"comments": []
},
{
"id": "d8a9ddfc-0b53-4313-aac1-4afdf5261691",
"listId": "91b2b60d-a61b-4d81-bfc8-44ca4ad152ba",
"title": "Add Mapper Configs for AI Models ...",
"description": "",
"labels": [],
"checkboxes": [],
"comments": []
},
{
"id": "7af4f8b4-5a5a-43d2-b154-026e13da1d92",
"listId": "91b2b60d-a61b-4d81-bfc8-44ca4ad152ba",
"title": "Add Support for IXAiDataProvider",
"description": "Data Provider needs to Provides all requirements of Data Manipulation of AI Models and also provides Default Project and Default Conversation for Data Handling ...",
"labels": [],
"checkboxes": [
{
"id": "e2abaca1-99cf-424a-870f-21f330445bbe",
"title": "Integrate all requirement actions",
"checked": true
},
{
"id": "f496ccaf-6d9e-43df-ab9e-2e2192bc7ef1",
"title": "Prepare Seeding Default Project and Default Conversation ",
"checked": true
}
],
"comments": []
},
{
"id": "38a94972-d86a-49a3-8917-9b2d958df660",
"listId": "91b2b60d-a61b-4d81-bfc8-44ca4ad152ba",
"title": "Complete Audio Transcript ...",
"description": "",
"labels": [],
"checkboxes": [],
"comments": []
},
{
"id": "5cccaa60-c985-4a99-93eb-adb38a1f79db",
"listId": "91b2b60d-a61b-4d81-bfc8-44ca4ad152ba",
"title": "Complete OCR Preparation ...",
"description": "",
"labels": [],
"checkboxes": [],
"comments": []
},
{
"id": "1c3b121a-08d5-44b1-a991-4fc95f7ebe15",
"listId": "91b2b60d-a61b-4d81-bfc8-44ca4ad152ba",
"title": "Add AiService Mechanism ...",
"description": "",
"labels": [],
"checkboxes": [
{
"id": "c6674a4a-2487-4ec1-b674-d5fba8fedbee",
"title": "Create Base AI Service Material",
"checked": true
}
],
"comments": []
}
]
}
],
"archive": {
+977
View File
@@ -0,0 +1,977 @@
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>تحلیل معماری پروژه xAiApi - فن آوران ساحر علم</title>
<style>
:root {
--primary: #1e3a8a;
--secondary: #3b82f6;
--accent: #f59e0b;
--success: #10b981;
--danger: #ef4444;
--bg-light: #f8fafc;
--bg-code: #1e293b;
--text-dark: #0f172a;
--text-muted: #64748b;
--border: #e2e8f0;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Tahoma', 'Segoe UI', sans-serif;
background: linear-gradient(135deg, #f8fafc 0%, #e0e7ff 100%);
color: var(--text-dark);
line-height: 1.8;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
color: white;
padding: 40px;
text-align: center;
position: relative;
}
.header::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" fill="none" stroke="white" stroke-width="0.5" opacity="0.1"/></svg>');
opacity: 0.3;
}
.header h1 {
font-size: 2.2em;
margin-bottom: 10px;
position: relative;
}
.header .subtitle {
font-size: 1.1em;
opacity: 0.95;
position: relative;
}
.meta-bar {
display: flex;
justify-content: space-between;
background: var(--bg-light);
padding: 15px 30px;
border-bottom: 2px solid var(--border);
flex-wrap: wrap;
gap: 15px;
}
.meta-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 0.9em;
color: var(--text-muted);
}
.meta-item strong { color: var(--primary); }
.content { padding: 40px; }
.section {
margin-bottom: 35px;
padding: 25px;
background: var(--bg-light);
border-radius: 12px;
border-right: 5px solid var(--secondary);
}
.section h2 {
color: var(--primary);
font-size: 1.6em;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid var(--border);
display: flex;
align-items: center;
gap: 10px;
}
.section h3 {
color: var(--secondary);
font-size: 1.2em;
margin: 20px 0 12px;
}
.step {
background: white;
padding: 20px;
margin: 15px 0;
border-radius: 10px;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
border-right: 4px solid var(--accent);
}
.step-number {
display: inline-block;
background: var(--accent);
color: white;
width: 32px;
height: 32px;
border-radius: 50%;
text-align: center;
line-height: 32px;
font-weight: bold;
margin-left: 10px;
}
.arch-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
margin: 20px 0;
}
.arch-card {
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
border-top: 4px solid var(--secondary);
transition: transform 0.3s;
}
.arch-card:hover { transform: translateY(-5px); }
.arch-card h4 {
color: var(--primary);
margin-bottom: 12px;
font-size: 1.15em;
}
.arch-card ul {
list-style: none;
padding-right: 0;
}
.arch-card li {
padding: 6px 0;
padding-right: 20px;
position: relative;
font-size: 0.95em;
}
.arch-card li::before {
content: '▸';
position: absolute;
right: 0;
color: var(--accent);
font-weight: bold;
}
.tech-badge {
display: inline-block;
background: var(--secondary);
color: white;
padding: 4px 12px;
border-radius: 20px;
font-size: 0.85em;
margin: 3px;
}
.tech-badge.primary { background: var(--primary); }
.tech-badge.success { background: var(--success); }
.tech-badge.accent { background: var(--accent); }
.tech-badge.danger { background: var(--danger); }
pre {
background: var(--bg-code);
color: #e2e8f0;
padding: 18px;
border-radius: 8px;
overflow-x: auto;
direction: ltr;
text-align: left;
font-family: 'Consolas', 'Courier New', monospace;
font-size: 0.88em;
margin: 15px 0;
border-right: 4px solid var(--accent);
}
code {
background: #fef3c7;
color: #92400e;
padding: 2px 8px;
border-radius: 4px;
font-family: 'Consolas', monospace;
font-size: 0.9em;
direction: ltr;
display: inline-block;
}
table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
}
th {
background: var(--primary);
color: white;
padding: 12px;
text-align: right;
font-weight: bold;
}
td {
padding: 12px;
border-bottom: 1px solid var(--border);
}
tr:hover { background: var(--bg-light); }
.flow-diagram {
background: white;
padding: 25px;
border-radius: 10px;
margin: 20px 0;
text-align: center;
}
.flow-step {
display: inline-block;
background: var(--secondary);
color: white;
padding: 10px 20px;
border-radius: 8px;
margin: 5px;
font-size: 0.9em;
}
.flow-arrow {
display: inline-block;
color: var(--accent);
font-size: 1.5em;
margin: 0 8px;
vertical-align: middle;
}
.alert {
padding: 15px 20px;
border-radius: 8px;
margin: 15px 0;
border-right: 4px solid;
}
.alert-info {
background: #dbeafe;
border-color: var(--secondary);
color: #1e40af;
}
.alert-success {
background: #d1fae5;
border-color: var(--success);
color: #065f46;
}
.alert-warning {
background: #fef3c7;
border-color: var(--accent);
color: #92400e;
}
.footer {
background: var(--primary);
color: white;
padding: 25px;
text-align: center;
margin-top: 40px;
}
.footer p { margin: 5px 0; }
.highlight {
background: linear-gradient(120deg, #fef3c7 0%, #fef3c7 100%);
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
.toc {
background: white;
padding: 20px;
border-radius: 10px;
margin-bottom: 25px;
border: 2px solid var(--border);
}
.toc h3 { color: var(--primary); margin-bottom: 15px; }
.toc ol { padding-right: 25px; }
.toc li { padding: 6px 0; }
.toc a {
color: var(--secondary);
text-decoration: none;
transition: color 0.2s;
}
.toc a:hover { color: var(--primary); text-decoration: underline; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🧠 تحلیل جامع معماری پروژه xAiApi</h1>
<div class="subtitle">پلتفرم یکپارچه ارائه خدمات هوش مصنوعی مبتنی بر ASP.NET Core</div>
</div>
<div class="meta-bar">
<div class="meta-item">👨‍💻 <strong>توسعه‌دهنده:</strong> هادی خزاعی اصل</div>
<div class="meta-item">🏢 <strong>شرکت:</strong> فن آوران ساحر علم</div>
<div class="meta-item">📅 <strong>تاریخ تحلیل:</strong> چهارشنبه ۸ مهر ۱۴۰۵</div>
<div class="meta-item">📦 <strong>نسخه بررسی شده:</strong> 2026.09.30</div>
</div>
<div class="content">
<div class="toc">
<h3>📑 فهرست مطالب</h3>
<ol>
<li><a href="#overview">مرور کلی و چشم‌انداز پروژه</a></li>
<li><a href="#architecture">معماری کلان و ساختار ماژولار</a></li>
<li><a href="#layers">لایه‌بندی و مسئولیت‌ها</a></li>
<li><a href="#ai-providers">ارائه‌دهندگان هوش مصنوعی پشتیبانی شده</a></li>
<li><a href="#data-model">مدل داده و پایگاه داده</a></li>
<li><a href="#api-endpoints">نقاط پایانی API</a></li>
<li><a href="#features">ویژگی‌های کلیدی پیاده‌سازی شده</a></li>
<li><a href="#flow">جریان پردازش درخواست</a></li>
<li><a href="#di">مدیریت وابستگی‌ها (DI)</a></li>
<li><a href="#strengths">نقاط قوت و پیشنهادات بهبود</a></li>
<li><a href="#memory">خلاصه ذخیره شده در حافظه</a></li>
</ol>
</div>
<!-- Section 1: Overview -->
<div class="section" id="overview">
<h2>🎯 گام ۱: مرور کلی و چشم‌انداز پروژه</h2>
<p>
پروژه <span class="highlight">xAiApi</span> یک پلتفرم جامع و مقیاس‌پذیر برای ارائه خدمات هوش مصنوعی است که بر بستر
<code>ASP.NET Core</code> توسعه یافته است. این پروژه با رویکرد <strong>Modular Architecture</strong> طراحی شده
و قابلیت اتصال به چندین ارائه‌دهنده LLM را به صورت یکپارچه فراهم می‌کند.
</p>
<div class="alert alert-info">
<strong>💡 هدف اصلی:</strong> ایجاد یک لایه انتزاعی (Abstraction Layer) بر روی ارائه‌دهندگان مختلف AI
تا توسعه‌دهندگان بتوانند بدون وابستگی به یک ارائه‌دهنده خاص، از قابلیت‌های هوش مصنوعی در محصولات خود استفاده کنند.
</div>
<h3>سه ماژول اصلی پروژه:</h3>
<div class="arch-grid">
<div class="arch-card">
<h4>📡 xAiApi (لایه ارائه)</h4>
<ul>
<li>Controllers و API Endpoints</li>
<li>Database Context و Migrations</li>
<li>Providers و Service Implementations</li>
<li>Configuration و DI Extensions</li>
</ul>
</div>
<div class="arch-card">
<h4>🧩 xAiModels (لایه مدل)</h4>
<ul>
<li>Entities و DTOs</li>
<li>Repository Pattern</li>
<li>Enrichers و Data Providers</li>
<li>GraphQL Support</li>
</ul>
</div>
<div class="arch-card">
<h4>⚙️ xAiService (لایه سرویس)</h4>
<ul>
<li>Vector Helper (Embedding)</li>
<li>Cosine Similarity</li>
<li>User Validation Extensions</li>
<li>Configuration Node Names</li>
</ul>
</div>
</div>
</div>
<!-- Section 2: Architecture -->
<div class="section" id="architecture">
<h2>🏗️ گام ۲: معماری کلان و ساختار ماژولار</h2>
<p>
معماری پروژه بر پایه الگوی <strong>Clean Architecture</strong> همراه با <strong>Repository Pattern</strong>
و <strong>Dependency Injection</strong> گسترده طراحی شده است.
</p>
<h3>سلسله مراتب لایه‌ها:</h3>
<div class="flow-diagram">
<span class="flow-step">🌐 Client</span>
<span class="flow-arrow">→</span>
<span class="flow-step">📡 Controllers</span>
<span class="flow-arrow">→</span>
<span class="flow-step">🧠 AI Services</span>
<span class="flow-arrow">→</span>
<span class="flow-step">💾 Data Provider</span>
<span class="flow-arrow">→</span>
<span class="flow-step">🗄️ Repository</span>
</div>
<h3>ساختار پوشه‌ای پروژه xAiApi:</h3>
<pre>xAiApi/
├── 📂 Controllers/
│ ├── DefaultAiController.cs
│ ├── DefaultEmbeddingController.cs
│ ├── DefaultThinkingAiController.cs
│ ├── StartupController.cs
│ ├── XAiServiceControllerBase.cs
│ └── XAiEmbeddingServiceControllerBase.cs
├── 📂 Providers/
│ ├── XAIServiceBase.cs (هسته اصلی)
│ ├── XAiEmbeddingServiceBase.cs
│ ├── XDefaultAiService.cs
│ ├── XDefaultEmbeddingService.cs
│ └── XDefaultThinkingAiService.cs
├── 📂 Database/
│ ├── XAiApiDbContext.cs
│ └── XAiApiDatabaseDescriptor.cs
├── 📂 Interfaces/
│ ├── IXAiServiceBase.cs
│ ├── IXAiEmbeddingServiceBase.cs
│ └── IXDefault*.cs
├── 📂 Configurations/
│ └── XAiApiConfiguration.cs
├── 📂 Extensions/
│ ├── XModelsExtensions.cs
│ └── XProgramExtensions.cs
├── 📂 DI/
│ └── XDIHelperExtension.cs
├── 📂 Constants/
│ ├── XAiApiConstants.cs
│ └── ConfigurationNodeNames.cs
└── 📂 Migrations/
└── InitialMigrationAiApi.cs</pre>
</div>
<!-- Section 3: Layers -->
<div class="section" id="layers">
<h2>🧱 گام ۳: لایه‌بندی و مسئولیت‌ها</h2>
<h3>۳.۱ لایه Controllers (لایه ارائه)</h3>
<table>
<tr>
<th>Controller</th>
<th>مسئولیت</th>
<th>ویژگی کلیدی</th>
</tr>
<tr>
<td><code>DefaultAiController</code></td>
<td>پاسخ به سوالات عمومی</td>
<td>استفاده از مدل Gemma</td>
</tr>
<tr>
<td><code>DefaultEmbeddingController</code></td>
<td>تبدیل متن به بردار</td>
<td>پشتیبانی از Batch</td>
</tr>
<tr>
<td><code>DefaultThinkingAiController</code></td>
<td>استدلال عمیق (Reasoning)</td>
<td>مدل Qwen با Reasoning</td>
</tr>
<tr>
<td><code>StartupController</code></td>
<td>پیام خوش‌آمدگویی</td>
<td>AllowAnonymous</td>
</tr>
</table>
<div class="alert alert-success">
<strong>✅ نکته مهم:</strong> استفاده از <code>XBaseIdentityApiV1Controller</code> به عنوان کلاس پایه،
مدیریت هویت و مجوزها را به صورت یکپارچه فراهم می‌کند.
</div>
<h3>۳.۲ لایه Providers (هسته منطقی)</h3>
<p>کلاس <code>XAIServiceBase</code> به عنوان <strong>قلب تپنده</strong> پروژه عمل می‌کند و مسئولیت‌های زیر را بر عهده دارد:</p>
<ul style="padding-right: 25px; margin: 15px 0;">
<li>مدیریت ارتباط با LLM از طریق <code>IChatClient</code></li>
<li>پیاده‌سازی Memory Management برای حفظ Context مکالمات</li>
<li>پشتیبانی از Streaming Response (SSE)</li>
<li>مدیریت پروژه‌ها، مکالمات و پیام‌ها</li>
<li>تزریق Introduction Prompt به صورت خودکار</li>
</ul>
<h3>۳.۳ لایه Data Provider</h3>
<p>کلاس <code>XAiDataProvider</code> مسئولیت‌های زیر را مدیریت می‌کند:</p>
<ul style="padding-right: 25px; margin: 15px 0;">
<li>ایجاد خودکار <strong>Default Project</strong> و <strong>Default Conversation</strong> برای هر کاربر</li>
<li>مدیریت Multi-language Resources</li>
<li>ارسال SignalR Notifications</li>
<li>Enrichment اشیاء با اطلاعات مرتبط</li>
</ul>
</div>
<!-- Section 4: AI Providers -->
<div class="section" id="ai-providers">
<h2>🤖 گام ۴: ارائه‌دهندگان هوش مصنوعی پشتیبانی شده</h2>
<p>
پروژه از طریق <code>XAiModelProviderType</code> از چندین ارائه‌دهنده پشتیبانی می‌کند:
</p>
<div class="arch-grid">
<div class="arch-card">
<h4>🟢 OpenAI</h4>
<ul>
<li>استفاده از <code>OpenAIClient</code></li>
<li>پشتیبانی از ApiKey</li>
<li>Endpoint قابل تنظیم</li>
<li>Chat & Embedding</li>
</ul>
</div>
<div class="arch-card">
<h4>🔵 Ollama</h4>
<ul>
<li>اجرای Local Models</li>
<li>بدون نیاز به ApiKey</li>
<li>مناسب برای توسعه</li>
<li>مدل‌های Gemma, Qwen</li>
</ul>
</div>
<div class="arch-card">
<h4>🟡 DeepSeek</h4>
<ul>
<li>پشتیبانی تعریف شده</li>
<li>برای Reasoning Models</li>
<li>در حال توسعه</li>
</ul>
</div>
<div class="arch-card">
<h4>🟣 HuggingFace</h4>
<ul>
<li>دسترسی به هزاران مدل</li>
<li>پشتیبانی تعریف شده</li>
<li>در حال توسعه</li>
</ul>
</div>
</div>
<h3>مدل‌های پیش‌فرض پیکربندی شده:</h3>
<pre>// از XAiApiConstants.cs
XAiDefaultModelName = "Gemma" // مدل پیش‌فرض چت
XAiEmbeddingModelName = "Embed" // مدل Embedding
XAiDefaultThinkingModelName = "Qwen" // مدل Reasoning</pre>
<div class="alert alert-warning">
<strong>⚠️ نکته طراحی:</strong> استفاده از <code>Microsoft.Extensions.AI</code> به عنوان لایه انتزاعی استاندارد،
امکان تعویض ارائه‌دهنده بدون تغییر کد را فراهم می‌کند.
</div>
</div>
<!-- Section 5: Data Model -->
<div class="section" id="data-model">
<h2>🗄️ گام ۵: مدل داده و پایگاه داده</h2>
<p>پایگاه داده شامل ۶ جدول اصلی است:</p>
<table>
<tr>
<th>جدول</th>
<th>کلید</th>
<th>فیلدهای کلیدی</th>
<th>رابطه</th>
</tr>
<tr>
<td><code>AiProjects</code></td>
<td>Guid</td>
<td>OwnerId, Title, Description, Prompt</td>
<td>→ Conversations</td>
</tr>
<tr>
<td><code>AiConversations</code></td>
<td>Guid</td>
<td>OwnerId, Title, ProjectId</td>
<td>→ Messages, Project</td>
</tr>
<tr>
<td><code>AiMessages</code></td>
<td>Guid</td>
<td>Content, Role, MetaDatas, ConversationId</td>
<td>→ Conversation</td>
</tr>
<tr>
<td><code>Files</code></td>
<td>Guid</td>
<td>Name, Path, Thumb, References</td>
<td>مستقل</td>
</tr>
<tr>
<td><code>Strings</code></td>
<td>Int (Auto)</td>
<td>Language, ResourceTitle, TranslatedValue</td>
<td>Multi-language</td>
</tr>
<tr>
<td><code>Tags</code></td>
<td>Int (Auto)</td>
<td>Tag, References</td>
<td>مستقل</td>
</tr>
</table>
<h3>سلسله مراتب داده‌ها:</h3>
<div class="flow-diagram">
<span class="flow-step">👤 User (OwnerId)</span>
<span class="flow-arrow">→</span>
<span class="flow-step">📁 Project</span>
<span class="flow-arrow">→</span>
<span class="flow-step">💬 Conversation</span>
<span class="flow-arrow">→</span>
<span class="flow-step">📝 Message</span>
</div>
<div class="alert alert-info">
<strong>💡 ویژگی مهم:</strong> هر کاربر به صورت خودکار یک <strong>Default Project</strong> و یک
<strong>Default Conversation</strong> دارد که از طریق <code>XAiDataProvider</code> مدیریت می‌شود.
</div>
</div>
<!-- Section 6: API Endpoints -->
<div class="section" id="api-endpoints">
<h2>🔌 گام ۶: نقاط پایانی API</h2>
<h3>۶.۱ Endpoints عمومی (از XAiServiceControllerBase):</h3>
<table>
<tr>
<th>متد</th>
<th>مسیر</th>
<th>توضیح</th>
</tr>
<tr>
<td><span class="tech-badge success">GET</span></td>
<td><code>/AskText?question=</code></td>
<td>پرسش ساده و دریافت پاسخ</td>
</tr>
<tr>
<td><span class="tech-badge success">GET</span></td>
<td><code>/AskTextStream?question=</code></td>
<td>پرسش با پاسخ Streaming (SSE)</td>
</tr>
<tr>
<td><span class="tech-badge primary">POST</span></td>
<td><code>/Ask</code></td>
<td>پرسش با Context (Project/Conversation)</td>
</tr>
<tr>
<td><span class="tech-badge primary">POST</span></td>
<td><code>/AskStream</code></td>
<td>پرسش Context دار با Streaming</td>
</tr>
<tr>
<td><span class="tech-badge success">GET</span></td>
<td><code>/Projects</code></td>
<td>دریافت لیست پروژه‌ها</td>
</tr>
<tr>
<td><span class="tech-badge primary">POST</span></td>
<td><code>/Projects</code></td>
<td>ایجاد پروژه جدید</td>
</tr>
<tr>
<td><span class="tech-badge success">GET</span></td>
<td><code>/Projects/{id}/Conversations</code></td>
<td>مکالمات یک پروژه</td>
</tr>
<tr>
<td><span class="tech-badge success">GET</span></td>
<td><code>/Conversations/{id}/Messages</code></td>
<td>پیام‌های یک مکالمه</td>
</tr>
<tr>
<td><span class="tech-badge success">GET</span></td>
<td><code>*/Query</code></td>
<td>جستجوی پیشرفته با XQuery</td>
</tr>
</table>
<h3>۶.۲ Endpoints Embedding:</h3>
<table>
<tr>
<td><span class="tech-badge success">GET</span></td>
<td><code>/Embedding?content=</code></td>
<td>تبدیل یک متن به بردار</td>
</tr>
<tr>
<td><span class="tech-badge success">GET</span></td>
<td><code>/Embeddings?batch=</code></td>
<td>تبدیل دسته‌ای متون به بردار</td>
</tr>
</table>
</div>
<!-- Section 7: Features -->
<div class="section" id="features">
<h2>✨ گام ۷: ویژگی‌های کلیدی پیاده‌سازی شده</h2>
<div class="arch-grid">
<div class="arch-card">
<h4>🔄 Streaming Responses</h4>
<ul>
<li>پیاده‌سازی SSE (Server-Sent Events)</li>
<li>غیرفعال‌سازی Buffering</li>
<li>پاسخ بلادرنگ به کاربر</li>
</ul>
</div>
<div class="arch-card">
<h4>🧠 Memory Management</h4>
<ul>
<li>حفظ Context مکالمات</li>
<li>Introduction Prompt</li>
<li>Project-specific Prompts</li>
</ul>
</div>
<div class="arch-card">
<h4>🌐 Multi-language Support</h4>
<ul>
<li>Resource-based Localization</li>
<li>Owned Item Resources</li>
<li>Default Language Management</li>
</ul>
</div>
<div class="arch-card">
<h4>📡 Real-time Notifications</h4>
<ul>
<li>SignalR Hub</li>
<li>Project/Conversation/Message Events</li>
<li>Connection-aware Broadcasting</li>
</ul>
</div>
<div class="arch-card">
<h4>🔐 Authorization</h4>
<ul>
<li>OAuth2 Introspection</li>
<li>Policy-based Authorization</li>
<li>Role-based Access Control</li>
</ul>
</div>
<div class="arch-card">
<h4>📚 Swagger Documentation</h4>
<ul>
<li>XML Comments</li>
<li>API V1 Versioning</li>
<li>Auto-generated Docs</li>
</ul>
</div>
<div class="arch-card">
<h4>🎯 Reasoning Models</h4>
<ul>
<li>ReasoningEffort Configuration</li>
<li>ReasoningOutput Options</li>
<li>Thinking AI Service</li>
</ul>
</div>
<div class="arch-card">
<h4>📊 Vector Operations</h4>
<ul>
<li>Vector Normalization</li>
<li>Cosine Similarity</li>
<li>Batch Embedding</li>
</ul>
</div>
</div>
</div>
<!-- Section 8: Flow -->
<div class="section" id="flow">
<h2>🔄 گام ۸: جریان پردازش درخواست</h2>
<h3>۸.۱ جریان پرسش ساده (AskText):</h3>
<div class="flow-diagram">
<span class="flow-step">Client Request</span>
<span class="flow-arrow">→</span>
<span class="flow-step">Controller</span>
<span class="flow-arrow">→</span>
<span class="flow-step">Validation</span>
<span class="flow-arrow">→</span>
<span class="flow-step">XAIServiceBase</span>
<span class="flow-arrow">→</span>
<span class="flow-step">GetHistory()</span>
<span class="flow-arrow">→</span>
<span class="flow-step">GetClient()</span>
<span class="flow-arrow">→</span>
<span class="flow-step">LLM API</span>
</div>
<h3>۸.۲ جریان پرسش با Context (Ask):</h3>
<div class="flow-diagram">
<span class="flow-step">Request + ProjectId + ConversationId</span>
<span class="flow-arrow">→</span>
<span class="flow-step">Load Project</span>
<span class="flow-arrow">→</span>
<span class="flow-step">Load Conversation</span>
<span class="flow-arrow">→</span>
<span class="flow-step">Load Messages</span>
<span class="flow-arrow">→</span>
<span class="flow-step">PrepareMemory()</span>
<span class="flow-arrow">→</span>
<span class="flow-step">Save User Message</span>
<span class="flow-arrow">→</span>
<span class="flow-step">AskLLM()</span>
<span class="flow-arrow">→</span>
<span class="flow-step">Save AI Response</span>
</div>
<h3>۸.۳ جریان Embedding:</h3>
<div class="flow-diagram">
<span class="flow-step">Text Input</span>
<span class="flow-arrow">→</span>
<span class="flow-step">GetEmbeddingGenerator()</span>
<span class="flow-arrow">→</span>
<span class="flow-step">GenerateAsync()</span>
<span class="flow-arrow">→</span>
<span class="flow-step">VectorHelper.Normalize()</span>
<span class="flow-arrow">→</span>
<span class="flow-step">float[] Result</span>
</div>
</div>
<!-- Section 9: DI -->
<div class="section" id="di">
<h2>🔗 گام ۹: مدیریت وابستگی‌ها (DI)</h2>
<h3>سرویس‌های ثبت شده در <code>Startup.ConfigureServices</code>:</h3>
<pre>// سرویس‌های پایه
services.AddXCommons();
services.AddXAppConfiguration(Configuration);
services.AddXCors(appConfiguration.AllowedOrigins);
services.AddXSwagger(Configuration, xmlPath);
services.AddXHttpService(Configuration);
services.AddXApiV1Versioning();
services.AddXAuthorization();
// سرویس‌های هویت و ذخیره‌سازی
services.AddXIdentityService(lifeTime, Configuration);
services.AddXStorageService(Configuration);
services.AddXPushService(lifeTime, Configuration);
// سرویس‌های داده
services.AddXDatabase(lifeTime, descriptor, Configuration);
services.AddXStringService&lt;XAiApiDbContext&gt;(lifeTime, repositoryType);
services.AddXTagService&lt;XAiApiDbContext&gt;(lifeTime, repositoryType);
services.AddXFileService&lt;XAiApiDbContext&gt;(lifeTime, repositoryType);
services.AddXAiDataService&lt;XAiApiDbContext&gt;(lifeTime, repositoryType);
// سرویس‌های AI اختصاصی
services.AddScoped&lt;IXDefaultAiService, XDefaultAiService&gt;();
services.AddScoped&lt;IXDefaultEmbeddingService, XDefaultEmbeddingService&gt;();
services.AddScoped&lt;IXDefaultThinkingAiService, XDefaultThinkingAiService&gt;();</pre>
<div class="alert alert-success">
<strong>✅ الگوی طراحی:</strong> استفاده از <strong>Extension Methods</strong> برای ثبت سرویس‌ها
کد را تمیز، قابل نگهداری و ماژولار نگه می‌دارد.
</div>
</div>
<!-- Section 10: Strengths -->
<div class="section" id="strengths">
<h2>💎 گام ۱۰: نقاط قوت و پیشنهادات بهبود</h2>
<h3>✅ نقاط قوت:</h3>
<ul style="padding-right: 25px; margin: 15px 0;">
<li><strong>معماری ماژولار:</strong> جداسازی واضح لایه‌ها و مسئولیت‌ها</li>
<li><strong>Provider Agnostic:</strong> قابلیت تعویض ارائه‌دهنده AI بدون تغییر کد</li>
<li><strong>Repository Pattern:</strong> پشتیبانی از EF, MongoDB, InMemory</li>
<li><strong>Streaming Support:</strong> پیاده‌سازی کامل SSE برای UX بهتر</li>
<li><strong>Multi-language:</strong> سیستم Resource-based برای چندزبانگی</li>
<li><strong>Real-time:</strong> یکپارچگی SignalR برای Notifications</li>
<li><strong>GraphQL Support:</strong> امکان پرس‌وجوی انعطاف‌پذیر</li>
<li><strong>Enrichment Pattern:</strong> جداسازی منطق غنی‌سازی داده‌ها</li>
</ul>
<h3>🔧 پیشنهادات بهبود:</h3>
<ul style="padding-right: 25px; margin: 15px 0;">
<li>پیاده‌سازی کامل DeepSeek و HuggingFace providers</li>
<li>افزودن Rate Limiting برای کنترل مصرف API</li>
<li>پیاده‌سازی Caching برای پاسخ‌های پرتکرار</li>
<li>افزودن Telemetry و Monitoring (OpenTelemetry)</li>
<li>پیاده‌سازی Unit of Work Pattern به صورت کامل‌تر</li>
<li>افزودن Health Checks برای پایش سرویس‌ها</li>
<li>پیاده‌سازی Circuit Breaker برای resilience</li>
<li>افزودن Unit Tests و Integration Tests</li>
</ul>
</div>
<!-- Section 11: Memory Summary -->
<div class="section" id="memory">
<h2>🧠 گام ۱۱: خلاصه ذخیره شده در حافظه</h2>
<div class="alert alert-success">
<strong>✅ تأیید ذخیره‌سازی:</strong> تحلیل کامل پروژه در حافظه من ذخیره شد و برای دستورات بعدی شما آماده است.
</div>
<h3>📌 نکات کلیدی که به خاطر سپرده‌ام:</h3>
<table>
<tr>
<th>موضوع</th>
<th>جزئیات</th>
</tr>
<tr>
<td><strong>معماری</strong></td>
<td>Clean Architecture + Modular + Repository Pattern</td>
</tr>
<tr>
<td><strong>فناوری پایه</strong></td>
<td>ASP.NET Core, Entity Framework, SignalR, GraphQL</td>
</tr>
<tr>
<td><strong>ارائه‌دهندگان AI</strong></td>
<td>OpenAI, Ollama, DeepSeek, HuggingFace</td>
</tr>
<tr>
<td><strong>مدل‌های پیش‌فرض</strong></td>
<td>Gemma (Chat), Embed (Embedding), Qwen (Reasoning)</td>
</tr>
<tr>
<td><strong>ساختار داده</strong></td>
<td>Project → Conversation → Message</td>
</tr>
<tr>
<td><strong>ویژگی‌های کلیدی</strong></td>
<td>Streaming, Memory, Multi-lang, Real-time, Reasoning</td>
</tr>
<tr>
<td><strong>الگوهای طراحی</strong></td>
<td>DI, Repository, Provider, Enricher, Hub</td>
</tr>
<tr>
<td><strong>فایل‌های کلیدی</strong></td>
<td>XAIServiceBase.cs, XAiDataProvider.cs, Startup.cs</td>
</tr>
</table>
<h3>🎯 آماده برای دستورات بعدی:</h3>
<div class="arch-grid">
<div class="arch-card">
<h4>🔧 توسعه ویژگی جدید</h4>
<ul>
<li>افزودن Provider جدید</li>
<li>پیاده‌سازی Endpoint جدید</li>
<li>افزودن Entity جدید</li>
</ul>
</div>
<div class="arch-card">
<h4>🐛 رفع اشکال</h4>
<ul>
<li>تحلیل خطاهای runtime</li>
<li>بهینه‌سازی performance</li>
<li>بررسی memory leaks</li>
</ul>
</div>
<div class="arch-card">
<h4>📚 مستندسازی</h4>
<ul>
<li>API Documentation</li>
<li>Architecture Diagrams</li>
<li>User Guides</li>
</ul>
</div>
<div class="arch-card">
<h4>🚀 استقرار</h4>
<ul>
<li>Configuration Management</li>
<li>Deployment Strategies</li>
<li>Scaling Considerations</li>
</ul>
</div>
</div>
<div class="alert alert-info">
<strong>💬 پیام به استاد:</strong> تحلیل کامل پروژه با موفقیت انجام شد و تمامی جزئیات معماری،
ساختار کد، الگوهای طراحی و ویژگی‌های پیاده‌سازی شده در حافظه من ثبت گردید. اکنون آماده دریافت
دستورات بعدی شما برای توسعه، بهبود، یا هرگونه تغییر در پروژه هستم.
</div>
</div>
</div>
<div class="footer">
<p><strong>👨‍💻 توسعه‌دهنده:</strong> هادی خزاعی اصل</p>
<p><strong>🏢 شرکت:</strong> فن آوران ساحر علم</p>
<p><strong>📅 تاریخ تهیه مستند:</strong> چهارشنبه ۸ مهر ۱۴۰۵</p>
<p style="margin-top: 15px; opacity: 0.8; font-size: 0.9em;">
🧠 تحلیل جامع معماری پروژه xAiApi - تمامی حقوق محفوظ است
</p>
</div>
</div>
</body>
</html>
+441
View File
@@ -0,0 +1,441 @@
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>رفع خطای وابستگی چرخشی (Circular Dependency) در سرویس OCR - فن آوران ساحر علم</title>
<style>
:root {
--primary: #1e3a8a;
--secondary: #3b82f6;
--accent: #f59e0b;
--success: #10b981;
--danger: #ef4444;
--warning: #f97316;
--bg-light: #f8fafc;
--bg-code: #1e293b;
--text-dark: #0f172a;
--text-muted: #64748b;
--border: #e2e8f0;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Tahoma', 'Segoe UI', sans-serif;
background: linear-gradient(135deg, #f8fafc 0%, #e0e7ff 100%);
color: var(--text-dark);
line-height: 1.8;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
color: white;
padding: 40px;
text-align: center;
}
.header h1 { font-size: 2.1em; margin-bottom: 10px; }
.header .subtitle { font-size: 1.1em; opacity: 0.95; }
.meta-bar {
display: flex;
justify-content: space-between;
background: var(--bg-light);
padding: 15px 30px;
border-bottom: 2px solid var(--border);
flex-wrap: wrap;
gap: 15px;
}
.meta-item { display: flex; align-items: center; gap: 8px; font-size: 0.9em; color: var(--text-muted); }
.meta-item strong { color: var(--primary); }
.content { padding: 40px; }
.section {
margin-bottom: 35px;
padding: 25px;
background: var(--bg-light);
border-radius: 12px;
border-right: 5px solid var(--secondary);
}
.section h2 {
color: var(--primary);
font-size: 1.5em;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid var(--border);
}
.section h3 { color: var(--secondary); font-size: 1.2em; margin: 20px 0 12px; }
pre {
background: var(--bg-code);
color: #e2e8f0;
padding: 18px;
border-radius: 8px;
overflow-x: auto;
direction: ltr;
text-align: left;
font-family: 'Consolas', monospace;
font-size: 0.85em;
margin: 15px 0;
border-right: 4px solid var(--accent);
}
code {
background: #fef3c7;
color: #92400e;
padding: 2px 8px;
border-radius: 4px;
font-family: 'Consolas', monospace;
font-size: 0.9em;
direction: ltr;
display: inline-block;
}
table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
background: white;
border-radius: 8px;
overflow: hidden;
}
th { background: var(--primary); color: white; padding: 12px; text-align: right; }
td { padding: 12px; border-bottom: 1px solid var(--border); }
.alert { padding: 15px 20px; border-radius: 8px; margin: 15px 0; border-right: 4px solid; }
.alert-info { background: #dbeafe; border-color: var(--secondary); color: #1e40af; }
.alert-success { background: #d1fae5; border-color: var(--success); color: #065f46; }
.alert-danger { background: #fee2e2; border-color: var(--danger); color: #991b1b; }
.footer { background: var(--primary); color: white; padding: 25px; text-align: center; }
.toc { background: white; padding: 20px; border-radius: 10px; margin-bottom: 25px; border: 2px solid var(--border); }
.toc h3 { color: var(--primary); margin-bottom: 15px; }
.toc ol { padding-right: 25px; }
.toc li { padding: 6px 0; }
.toc a { color: var(--secondary); text-decoration: none; }
.file-change { background: #f0f9ff; border-right: 4px solid var(--secondary); padding: 15px; margin: 10px 0; border-radius: 8px; }
.file-change .path { font-family: 'Consolas', monospace; color: var(--primary); font-weight: bold; direction: ltr; display: inline-block; }
.badge-modify { background: var(--warning); color: white; padding: 2px 8px; border-radius: 4px; font-size: 0.75em; margin-right: 8px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🔗 رفع خطای وابستگی چرخشی (Circular Dependency)</h1>
<div class="subtitle">تحلیل و رفع چرخه وابستگی بین IXFileContentExtractor و IXDefaultAIOCRService</div>
</div>
<div class="meta-bar">
<div class="meta-item">👨‍💻 <strong>توسعه‌دهنده:</strong> هادی خزاعی اصل</div>
<div class="meta-item">🏢 <strong>شرکت:</strong> فن آوران ساحر علم</div>
<div class="meta-item">📅 <strong>تاریخ:</strong> شنبه ۱۲ مهر ۱۴۰۵</div>
<div class="meta-item">📦 <strong>پروژه:</strong> xAiApi</div>
</div>
<div class="content">
<div class="toc">
<h3>📑 فهرست مطالب</h3>
<ol>
<li><a href="#analysis">تحلیل ریشه خطا</a></li>
<li><a href="#solution">استراتژی رفع خطا</a></li>
<li><a href="#step1">گام ۱: اصلاح Interface سرویس OCR</a></li>
<li><a href="#step2">گام ۲: بازنویسی مستقل کلاس XDefaultAIOCRService</a></li>
<li><a href="#step3">گام ۳: بررسی ثبت در Dependency Injection</a></li>
</ol>
</div>
<!-- Section 1: Analysis -->
<div class="section" id="analysis">
<h2>🔍 گام ۱: تحلیل ریشه خطا</h2>
<p>پیغام خطای DI به وضوح یک <strong>وابستگی چرخشی (Circular Dependency)</strong> را نشان می‌دهد:</p>
<div class="alert alert-danger">
<code>IXFileContentExtractor</code> ➔ <code>XVisionFileContentExtractor</code> ➔ <code>IXDefaultAIOCRService</code> ➔ <code>XDefaultAIOCRService</code> ➔ <code>XAIServiceBase</code> ➔ <code>IXFileContentExtractor</code>
</div>
<p><strong>چرا این اتفاق افتاد؟</strong></p>
<ul style="padding-right: 25px; margin-top: 15px;">
<li>کلاس <code>XVisionFileContentExtractor</code> برای انجام OCR به <code>IXDefaultAIOCRService</code> نیاز دارد.</li>
<li>کلاس <code>XDefaultAIOCRService</code> از <code>XAIServiceBase</code> ارث‌بری کرده است.</li>
<li>سازنده (Constructor) کلاس <code>XAIServiceBase</code> به <code>IXFileContentExtractor</code> نیاز دارد.</li>
</ul>
<p>این زنجیره باعث می‌شود کانتینر DI در یک حلقه بی‌نهایت گیر کند و نتواند هیچ‌یک از این سرویس‌ها را مقداردهی اولیه نماید.</p>
</div>
<!-- Section 2: Solution -->
<div class="section" id="solution">
<h2>💡 گام ۲: استراتژی رفع خطا</h2>
<p>برای شکستن این چرخه، باید <strong>وابستگی <code>XDefaultAIOCRService</code> به <code>XAIServiceBase</code> را حذف کنیم</strong>. </p>
<p>سرویس OCR فقط نیاز به برقراری ارتباط با مدل زبانی (LLM) برای استخراج متن از تصویر دارد و به هیچ‌وجه به <code>IXFileContentExtractor</code>، <code>IXFileProvider</code> یا <code>IXAiDataProvider</code> نیاز ندارد. بنابراین، با حذف ارث‌بری از <code>XAIServiceBase</code> و پیاده‌سازی مستقیم منطق ساخت <code>IChatClient</code> درون همین کلاس، چرخه وابستگی کاملاً شکسته می‌شود.</p>
</div>
<!-- Section 3: Step 1 -->
<div class="section" id="step1">
<h2>🛠️ گام ۳: اصلاح Interface سرویس OCR</h2>
<p>ابتدا باید ارث‌بری غیرضروری <code>IXAiServiceBase</code> را از اینترفیس حذف کنیم، زیرا این سرویس فقط وظیفه OCR را بر عهده دارد و نیازی به متدهای مدیریت پروژه، مکالمه و پیام ندارد.</p>
<div class="file-change">
<span class="badge-modify">MODIFY</span>
<span class="path">xAiApi/Interfaces/IXDefaultAIOCRService.cs</span>
</div>
<pre>using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using System.Collections.Generic;
namespace xAiApi.Interfaces
{
/// &lt;summary&gt;
/// سرویس اختصاصی برای انجام OCR بر روی محتوای تصویری ...
/// &lt;/summary&gt;
public interface IXDefaultAIOCRService
{
/// &lt;summary&gt;
/// درخواست انجام OCR بر روی محتوای داده شده ...
/// &lt;/summary&gt;
Task&lt;string&gt; RequestOCRAsync(
IList&lt;ChatMessage&gt; messages,
CancellationToken cancellationToken = default
);
/// &lt;summary&gt;
/// درخواست انجام OCR بر روی محتوای داده شده به صورت Stream ...
/// &lt;/summary&gt;
IAsyncEnumerable&lt;string&gt; RequestOCRAsEnumerable(
IList&lt;ChatMessage&gt; messages,
CancellationToken cancellationToken = default
);
}
}</pre>
</div>
<!-- Section 4: Step 2 -->
<div class="section" id="step2">
<h2>⚙️ گام ۴: بازنویسی مستقل کلاس XDefaultAIOCRService</h2>
<p>اکنون کلاس پیاده‌سازی را بازنویسی می‌کنیم تا دیگر از <code>XAIServiceBase</code> ارث‌بری نکند. منطق ساخت <code>IChatClient</code> (که قبلاً در کلاس پایه بود) به صورت مستقیم و تمیز درون این کلاس قرار می‌گیرد.</p>
<div class="file-change">
<span class="badge-modify">MODIFY</span>
<span class="path">xAiApi/Providers/XDefaultAIOCRService.cs</span>
</div>
<pre>using System;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using OpenAI;
using OllamaSharp;
using System.ClientModel;
using System.ClientModel.Primitives;
using xAiApi.Configurations;
using xAiApi.Constants;
using xAiApi.Extensions;
using xAiModels.Constants;
using xAiModels.Extensions;
using xCommons.Extensions;
using xExceptions.Constants;
namespace xAiApi.Providers
{
/// &lt;summary&gt;
/// پیاده‌سازی مستقل سرویس OCR بدون وابستگی به XAIServiceBase ...
/// &lt;/summary&gt;
public class XDefaultAIOCRService : IXDefaultAIOCRService
{
private readonly string prompt;
private readonly XAiModelDescriptor descriptor;
private readonly ILogger&lt;XDefaultAIOCRService&gt; logger;
public XDefaultAIOCRService(
ILogger&lt;XDefaultAIOCRService&gt; logger,
XAiApiConfiguration configuration,
string model = null,
string prompt = null
)
{
this.logger = logger;
// ۱. دریافت پیکربندی مدل
if (model.IsNullOrEmpty())
{
model = XAiApiConstants.XAiDefaultVisionModelName;
}
descriptor = configuration.GetModel(model);
if (descriptor.IsNullOrDefault())
{
XException.InvalidData.Throw("Invalid OCR Model Configuration");
}
// ۲. دریافت پرامپت استخراج
if (prompt.IsNullOrEmpty())
{
prompt = configuration.GetPrompt(
name: XAiApiConstants.XAiApiContentExtractionPromptName,
@params: null
);
}
this.prompt = prompt;
if (this.prompt.IsNullOrEmpty())
{
XException.InvalidArgs.Throw("OCR Prompt cannot be empty");
}
}
public virtual async Task&lt;string&gt; RequestOCRAsync(
IList&lt;ChatMessage&gt; messages,
CancellationToken cancellationToken = default
)
{
if (!messages.HasChild())
{
XException.InvalidArgs.Throw();
}
using var client = GetClient();
var pMessage = new ChatMessage(ChatRole.System, prompt);
messages = [pMessage, .. messages];
var response = await client.GetResponseAsync(
messages: messages,
cancellationToken: cancellationToken
);
if (!response.IsValid())
{
XException.ActionFailed.Throw();
}
return response.Text;
}
public virtual async IAsyncEnumerable&lt;string&gt; RequestOCRAsEnumerable(
IList&lt;ChatMessage&gt; messages,
[EnumeratorCancellation] CancellationToken cancellationToken = default
)
{
if (!messages.HasChild())
{
XException.InvalidArgs.Throw();
}
using var client = GetClient();
var pMessage = new ChatMessage(ChatRole.System, prompt);
messages = [pMessage, .. messages];
var enumerable = client.GetStreamingResponseAsync(
options: null,
messages: messages
);
await foreach (var res in enumerable)
{
if (cancellationToken.IsCancellationRequested)
{
yield break;
}
yield return res.Text;
}
}
/// &lt;summary&gt;
/// ساخت کلاینت ارتباط با LLM (جایگزین متد GetClient در XAIServiceBase) ...
/// &lt;/summary&gt;
private IChatClient GetClient()
{
var model = descriptor.LLM;
var apiKey = descriptor.ApiKey;
var url = new Uri(descriptor.Url);
var httpClient = new HttpClient { BaseAddress = url, Timeout = Timeout.InfiniteTimeSpan };
IChatClient result = null;
switch (descriptor.Provider)
{
case XAiModelProviderType.Ollama:
var ollamaClient = new OllamaApiClient(httpClient, model);
result = new ChatClientBuilder(ollamaClient).UseFunctionInvocation().Build();
break;
case XAiModelProviderType.OpenAI:
var openAiClient = new OpenAIClient(
new ApiKeyCredential(apiKey.IsNullOrEmpty() ? XAiApiConstants.XOpenAINoKey : apiKey),
new OpenAIClientOptions
{
Endpoint = url,
Transport = new HttpClientPipelineTransport(httpClient)
}
);
result = new ChatClientBuilder(openAiClient.GetChatClient(model).AsIChatClient()).UseFunctionInvocation().Build();
break;
default:
XException.InvalidData.Throw($"Unsupported provider: {descriptor.Provider}");
break;
}
if (result == null)
{
XException.InvalidData.Throw("Failed to create Chat Client");
}
return result;
}
}
}</pre>
</div>
<!-- Section 5: Step 3 -->
<div class="section" id="step3">
<h2>🔌 گام ۵: بررسی ثبت در Dependency Injection</h2>
<p>با توجه به تغییرات فوق، ثبت سرویس در فایل <code>Startup.cs</code> بدون هیچ تغییری به درستی کار خواهد کرد، زیرا امضای Constructor اکنون ساده‌تر شده و فقط به <code>ILogger</code> و <code>XAiApiConfiguration</code> وابسته است که هر دو از قبل در DI ثبت شده‌اند.</p>
<div class="alert alert-success">
<strong>✅ تأییدیه:</strong><br>
خط <code>services.AddScoped&lt;IXDefaultAIOCRService, XDefaultAIOCRService&gt;();</code> در <code>Startup.cs</code> کاملاً معتبر است و دیگر باعث ایجاد چرخه وابستگی نمی‌شود.
</div>
<h3>خلاصه دستاوردهای این اصلاح:</h3>
<table>
<tr>
<th>مزیت</th>
<th>توضیح</th>
</tr>
<tr>
<td>🚫 حذف Circular Dependency</td>
<td>چرخه معیوب بین Extractor و سرویس OCR کاملاً شکسته شد.</td>
</tr>
<tr>
<td>⚡ افزایش عملکرد (Performance)</td>
<td>سرویس OCR دیگر بار اضافی مقداردهی اولیه DataProvider و FileProvider را تحمل نمی‌کند.</td>
</tr>
<tr>
<td>🎯 اصل تک‌وظیفه‌ای (SRP)</td>
<td>کلاس <code>XDefaultAIOCRService</code> اکنون فقط و فقط مسئول ارتباط با مدل برای OCR است.</td>
</tr>
<tr>
<td>🧪 قابلیت تست‌پذیری (Testability)</td>
<td>تزریق وابستگی‌های کمتر، نوشتن Unit Test برای این سرویس را بسیار ساده‌تر می‌کند.</td>
</tr>
</table>
</div>
</div>
<div class="footer">
<p><strong>👨‍💻 توسعه‌دهنده:</strong> هادی خزاعی اصل</p>
<p><strong>🏢 شرکت:</strong> فن آوران ساحر علم</p>
<p><strong>📅 تاریخ:</strong> شنبه ۱۲ مهر ۱۴۰۵</p>
<p style="margin-top: 15px; opacity: 0.8; font-size: 0.9em;">
🔗 مستند فنی رفع خطای وابستگی چرخشی در xAiApi - تمامی حقوق محفوظ است
</p>
</div>
</div>
</body>
</html>

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