mirror of
				https://github.com/gnh1201/welsonjs.git
				synced 2025-10-31 12:57:31 +00:00 
			
		
		
		
	Add support the Azure AI, Optimize the HTTPClient object uses.
This commit is contained in:
		
							parent
							
								
									b26d979b8d
								
							
						
					
					
						commit
						5b2863058a
					
				|  | @ -70,11 +70,20 @@ namespace WelsonJS.Launcher.Properties { | |||
|         } | ||||
|          | ||||
|         /// <summary> | ||||
|         ///   https://ai-catswords656881030318.services.ai.azure.com/models/chat/completions?api-version=2024-05-01-preview과(와) 유사한 지역화된 문자열을 찾습니다. | ||||
|         ///   2024-05-01-preview과(와) 유사한 지역화된 문자열을 찾습니다. | ||||
|         /// </summary> | ||||
|         internal static string AzureAiServiceUrl { | ||||
|         internal static string AzureAiServiceApiVersion { | ||||
|             get { | ||||
|                 return ResourceManager.GetString("AzureAiServiceUrl", resourceCulture); | ||||
|                 return ResourceManager.GetString("AzureAiServiceApiVersion", resourceCulture); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         /// <summary> | ||||
|         ///   https://ai-catswords656881030318.services.ai.azure.com/과(와) 유사한 지역화된 문자열을 찾습니다. | ||||
|         /// </summary> | ||||
|         internal static string AzureAiServicePrefix { | ||||
|             get { | ||||
|                 return ResourceManager.GetString("AzureAiServicePrefix", resourceCulture); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|  |  | |||
|  | @ -163,8 +163,8 @@ | |||
|   <data name="AzureAiServiceApiKey" xml:space="preserve"> | ||||
|     <value /> | ||||
|   </data> | ||||
|   <data name="AzureAiServiceUrl" xml:space="preserve"> | ||||
|     <value>https://ai-catswords656881030318.services.ai.azure.com/models/chat/completions?api-version=2024-05-01-preview</value> | ||||
|   <data name="AzureAiServicePrefix" xml:space="preserve"> | ||||
|     <value>https://ai-catswords656881030318.services.ai.azure.com/</value> | ||||
|   </data> | ||||
|   <data name="DnsServerAddress" xml:space="preserve"> | ||||
|     <value>1.1.1.1</value> | ||||
|  | @ -181,4 +181,7 @@ | |||
|   <data name="BlobServerPrefix" xml:space="preserve"> | ||||
|     <value>https://catswords.blob.core.windows.net/welsonjs/</value> | ||||
|   </data> | ||||
|   <data name="AzureAiServiceApiVersion" xml:space="preserve"> | ||||
|     <value>2024-05-01-preview</value> | ||||
|   </data> | ||||
| </root> | ||||
|  | @ -23,8 +23,8 @@ namespace WelsonJS.Launcher | |||
|         private string _prefix; | ||||
|         private string _resourceName; | ||||
|         private List<IResourceTool> _tools = new List<IResourceTool>(); | ||||
|         private const int _blobTimeout = 5000; | ||||
|         private readonly HttpClient _blobClient = new HttpClient(); | ||||
|         private const int _httpClientTimeout = 5000; | ||||
|         private readonly HttpClient _httpClient = new HttpClient(); | ||||
|         private readonly string _defaultMimeType = "application/octet-stream"; | ||||
| 
 | ||||
|         public ResourceServer(string prefix, string resourceName) | ||||
|  | @ -33,15 +33,15 @@ namespace WelsonJS.Launcher | |||
|             _listener = new HttpListener(); | ||||
|             _listener.Prefixes.Add(prefix); | ||||
|             _resourceName = resourceName; | ||||
|             _blobClient.Timeout = TimeSpan.FromMilliseconds(_blobTimeout); | ||||
|             _httpClient.Timeout = TimeSpan.FromMilliseconds(_httpClientTimeout); | ||||
| 
 | ||||
|             // Add resource tools | ||||
|             _tools.Add(new ResourceTools.Completion(this)); | ||||
|             _tools.Add(new ResourceTools.Config(this)); | ||||
|             _tools.Add(new ResourceTools.DevTools(this)); | ||||
|             _tools.Add(new ResourceTools.DnsQuery(this)); | ||||
|             _tools.Add(new ResourceTools.Tfa(this)); | ||||
|             _tools.Add(new ResourceTools.Whois(this)); | ||||
|             _tools.Add(new ResourceTools.Completion(this, _httpClient)); | ||||
|             _tools.Add(new ResourceTools.Settings(this, _httpClient)); | ||||
|             _tools.Add(new ResourceTools.DevTools(this, _httpClient)); | ||||
|             _tools.Add(new ResourceTools.DnsQuery(this, _httpClient)); | ||||
|             _tools.Add(new ResourceTools.Tfa(this, _httpClient)); | ||||
|             _tools.Add(new ResourceTools.Whois(this, _httpClient)); | ||||
|         } | ||||
| 
 | ||||
|         public string GetPrefix() | ||||
|  | @ -158,7 +158,7 @@ namespace WelsonJS.Launcher | |||
| 
 | ||||
|                 HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url); | ||||
|                 request.Headers.UserAgent.ParseAdd(context.Request.UserAgent); | ||||
|                 HttpResponseMessage response = await _blobClient.SendAsync(request); | ||||
|                 HttpResponseMessage response = await _httpClient.SendAsync(request); | ||||
| 
 | ||||
|                 if (!response.IsSuccessStatusCode) | ||||
|                 { | ||||
|  |  | |||
|  | @ -1,139 +0,0 @@ | |||
| using System; | ||||
| using System.IO; | ||||
| using System.Net; | ||||
| using System.Net.Http; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace WelsonJS.Launcher.ResourceTools | ||||
| { | ||||
|     public class AzureAi : IResourceTool | ||||
|     { | ||||
|         private ResourceServer Server; | ||||
|         private const string Prefix = "azure-ai/"; | ||||
|         private const int ChunkSize = 4096; | ||||
|         private readonly string AzureAiServiceUrl; | ||||
|         private readonly string AzureAiServiceApiKey; | ||||
| 
 | ||||
|         public AzureAi(ResourceServer server) | ||||
|         { | ||||
|             Server = server; | ||||
| 
 | ||||
|             AzureAiServiceUrl = Program.GetAppConfig("AzureAiServiceUrl"); | ||||
|             AzureAiServiceApiKey = Program.GetAppConfig("AzureAiServiceApiKey"); | ||||
|         } | ||||
| 
 | ||||
|         public bool CanHandle(string path) | ||||
|         { | ||||
|             return path.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase); | ||||
|         } | ||||
| 
 | ||||
|         public async Task HandleAsync(HttpListenerContext context, string path) | ||||
|         { | ||||
|             string apiKey = AzureAiServiceApiKey; | ||||
|             if (string.IsNullOrEmpty(apiKey)) | ||||
|             { | ||||
|                 WriteError(context, "Missing the API key.", HttpStatusCode.BadRequest); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             string requestBody = ReadRequestBody(context); | ||||
|             bool isStreaming = ContainsSequentialKeywords(requestBody, new[] { "\"stream\"", "true" }, 30); | ||||
| 
 | ||||
|             try | ||||
|             { | ||||
|                 using (var response = await SendAzureRequestAsync(apiKey, requestBody, isStreaming)) | ||||
|                 { | ||||
|                     await ForwardResponseAsync(context, response, isStreaming); | ||||
|                 } | ||||
|             } | ||||
|             catch (HttpRequestException ex) | ||||
|             { | ||||
|                 WriteError(context, "Request error: " + ex.Message, HttpStatusCode.BadGateway); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private string ReadRequestBody(HttpListenerContext context) | ||||
|         { | ||||
|             using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding)) | ||||
|             { | ||||
|                 return reader.ReadToEnd(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private async Task<HttpResponseMessage> SendAzureRequestAsync(string apiKey, string requestBody, bool isStreaming) | ||||
|         { | ||||
|             using (var client = new HttpClient()) | ||||
|             { | ||||
|                 var requestMessage = new HttpRequestMessage(HttpMethod.Post, AzureAiServiceUrl); | ||||
|                 requestMessage.Headers.Add("api-key", apiKey); | ||||
|                 requestMessage.Content = new StringContent(requestBody, Encoding.UTF8, "application/json"); | ||||
| 
 | ||||
|                 var completionOption = isStreaming | ||||
|                     ? HttpCompletionOption.ResponseHeadersRead | ||||
|                     : HttpCompletionOption.ResponseContentRead; | ||||
| 
 | ||||
|                 return await client.SendAsync(requestMessage, completionOption); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private async Task ForwardResponseAsync(HttpListenerContext context, HttpResponseMessage response, bool isStreaming) | ||||
|         { | ||||
|             context.Response.StatusCode = (int)response.StatusCode; | ||||
|             context.Response.ContentType = response.Content.Headers.ContentType?.ToString() ?? "application/json"; | ||||
| 
 | ||||
|             using (var responseStream = await response.Content.ReadAsStreamAsync()) | ||||
|             { | ||||
|                 if (isStreaming) | ||||
|                 { | ||||
|                     context.Response.SendChunked = true; | ||||
|                     context.Response.Headers.Add("Transfer-Encoding", "chunked"); | ||||
|                     context.Response.Headers.Add("Cache-Control", "no-cache"); | ||||
| 
 | ||||
|                     byte[] buffer = new byte[ChunkSize]; | ||||
|                     int bytesRead; | ||||
|                     while ((bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length)) > 0) | ||||
|                     { | ||||
|                         await context.Response.OutputStream.WriteAsync(buffer, 0, bytesRead); | ||||
|                         context.Response.OutputStream.Flush(); | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     await responseStream.CopyToAsync(context.Response.OutputStream); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private void WriteError(HttpListenerContext context, string message, HttpStatusCode statusCode) | ||||
|         { | ||||
|             context.Response.StatusCode = (int)statusCode; | ||||
|             using (var writer = new StreamWriter(context.Response.OutputStream, Encoding.UTF8)) | ||||
|             { | ||||
|                 writer.Write(message); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public static bool ContainsSequentialKeywords(string input, string[] keywords, int maxDistance) | ||||
|         { | ||||
|             if (input == null || keywords == null || keywords.Length < 2) | ||||
|                 return false; | ||||
| 
 | ||||
|             int position = 0; | ||||
| 
 | ||||
|             for (int i = 0; i < keywords.Length; i++) | ||||
|             { | ||||
|                 int index = input.IndexOf(keywords[i], position, StringComparison.OrdinalIgnoreCase); | ||||
|                 if (index < 0) | ||||
|                     return false; | ||||
| 
 | ||||
|                 if (i > 0 && (index - position) > maxDistance) | ||||
|                     return false; | ||||
| 
 | ||||
|                 position = index + keywords[i].Length; | ||||
|             } | ||||
| 
 | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -8,18 +8,21 @@ using System.Linq; | |||
| using System.Text.RegularExpressions; | ||||
| using System.Threading.Tasks; | ||||
| using System.Xml.Linq; | ||||
| using System.Net.Http; | ||||
| 
 | ||||
| namespace WelsonJS.Launcher.ResourceTools | ||||
| { | ||||
|     public class Completion : IResourceTool | ||||
|     { | ||||
|         private ResourceServer Server; | ||||
|         private readonly ResourceServer Server; | ||||
|         private readonly HttpClient _httpClient; | ||||
|         private const string Prefix = "completion/"; | ||||
|         private List<string> Executables = new List<string>(); | ||||
| 
 | ||||
|         public Completion(ResourceServer server) | ||||
|         public Completion(ResourceServer server, HttpClient httpClient) | ||||
|         { | ||||
|             Server = server; | ||||
|             _httpClient = httpClient; | ||||
| 
 | ||||
|             new Task(() => | ||||
|             { | ||||
|  |  | |||
|  | @ -1,39 +0,0 @@ | |||
| using System; | ||||
| using System.Net; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace WelsonJS.Launcher.ResourceTools | ||||
| { | ||||
|     public class Config : IResourceTool | ||||
|     { | ||||
|         private ResourceServer Server; | ||||
|         private const string Prefix = "config/"; | ||||
| 
 | ||||
|         public Config(ResourceServer server) | ||||
|         { | ||||
|             Server = server; | ||||
|         } | ||||
| 
 | ||||
|         public bool CanHandle(string path) | ||||
|         { | ||||
|             return path.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase); | ||||
|         } | ||||
| 
 | ||||
|         public async Task HandleAsync(HttpListenerContext context, string path) | ||||
|         { | ||||
|             await Task.Delay(0); | ||||
| 
 | ||||
|             string configName = path.Substring(Prefix.Length); | ||||
| 
 | ||||
|             try | ||||
|             { | ||||
|                 string configValue = Program.GetAppConfig(configName); | ||||
|                 Server.ServeResource(context, configValue, "text/plain"); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 Server.ServeResource(context, $"<error>Failed to process Config request. {ex.Message}</error>", "application/xml", 500); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -8,12 +8,13 @@ namespace WelsonJS.Launcher.ResourceTools | |||
|     public class DevTools : IResourceTool | ||||
|     { | ||||
|         private ResourceServer Server; | ||||
|         private readonly HttpClient _httpClient; | ||||
|         private const string Prefix = "devtools/"; | ||||
|         private const double Timeout = 5000; | ||||
| 
 | ||||
|         public DevTools(ResourceServer server) | ||||
|         public DevTools(ResourceServer server, HttpClient httpClient) | ||||
|         { | ||||
|             Server = server; | ||||
|             _httpClient = httpClient; | ||||
|         } | ||||
| 
 | ||||
|         public bool CanHandle(string path) | ||||
|  | @ -27,15 +28,10 @@ namespace WelsonJS.Launcher.ResourceTools | |||
| 
 | ||||
|             try | ||||
|             { | ||||
|                 using (HttpClient client = new HttpClient()) | ||||
|                 { | ||||
|                     client.Timeout = TimeSpan.FromMilliseconds(Timeout); | ||||
|                 string url = Program.GetAppConfig("DevToolsPrefix") + endpoint; | ||||
|                 string data = await _httpClient.GetStringAsync(url); | ||||
| 
 | ||||
|                     string url = Program.GetAppConfig("DevToolsPrefix") + endpoint; | ||||
|                     string data = await client.GetStringAsync(url); | ||||
| 
 | ||||
|                     Server.ServeResource(context, data, "application/json"); | ||||
|                 } | ||||
|                 Server.ServeResource(context, data, "application/json"); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Net; | ||||
| using System.Net.Http; | ||||
| using System.Net.Sockets; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | @ -10,16 +11,19 @@ namespace WelsonJS.Launcher.ResourceTools | |||
| { | ||||
|     public class DnsQuery : IResourceTool | ||||
|     { | ||||
|         private ResourceServer Server; | ||||
|         private readonly ResourceServer Server; | ||||
|         private readonly HttpClient _httpClient; | ||||
|         private const string Prefix = "dns-query/"; | ||||
|         private string DnsServer; | ||||
|         private const int DnsPort = 53; | ||||
|         private const int Timeout = 5000; | ||||
|         private static readonly Random _random = new Random(); | ||||
| 
 | ||||
|         public DnsQuery(ResourceServer server) | ||||
|         public DnsQuery(ResourceServer server, HttpClient httpClient) | ||||
|         { | ||||
|             Server = server; | ||||
|             _httpClient = httpClient; | ||||
| 
 | ||||
|             DnsServer = Program.GetAppConfig("DnsServerAddress"); | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										85
									
								
								WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Settings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								WelsonJS.Toolkit/WelsonJS.Launcher/ResourceTools/Settings.cs
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,85 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Configuration; | ||||
| using System.Linq; | ||||
| using System.Net; | ||||
| using System.Net.Http; | ||||
| using System.Reflection; | ||||
| using System.Resources; | ||||
| using System.Threading.Tasks; | ||||
| using System.Xml.Linq; | ||||
| 
 | ||||
| namespace WelsonJS.Launcher.ResourceTools | ||||
| { | ||||
|     public class Settings : IResourceTool | ||||
|     { | ||||
|         private readonly ResourceServer Server; | ||||
|         private readonly HttpClient _httpClient; | ||||
|         private const string Prefix = "settings"; | ||||
| 
 | ||||
|         public Settings(ResourceServer server, HttpClient httpClient) | ||||
|         { | ||||
|             Server = server; | ||||
|             _httpClient = httpClient; | ||||
|         } | ||||
| 
 | ||||
|         public bool CanHandle(string path) | ||||
|         { | ||||
|             return path.Equals(Prefix, StringComparison.OrdinalIgnoreCase); | ||||
|         } | ||||
| 
 | ||||
|         public async Task HandleAsync(HttpListenerContext context, string path) | ||||
|         { | ||||
|             await Task.Delay(0); | ||||
|              | ||||
|             // Get current namespace (e.g., WelsonJS.Launcher) | ||||
|             string ns = typeof(Program).Namespace; | ||||
| 
 | ||||
|             // Build resource base name (e.g., WelsonJS.Launcher.Properties.Resources) | ||||
|             string resourceBaseName = ns + ".Properties.Resources"; | ||||
| 
 | ||||
|             // Load resource strings using ResourceManager | ||||
|             var resourceManager = new ResourceManager(resourceBaseName, Assembly.GetExecutingAssembly()); | ||||
|             var resourceSet = resourceManager.GetResourceSet( | ||||
|                 System.Globalization.CultureInfo.CurrentCulture, | ||||
|                 true, | ||||
|                 true | ||||
|             ); | ||||
| 
 | ||||
|             var resourceStrings = new Dictionary<string, string>(); | ||||
|             foreach (System.Collections.DictionaryEntry entry in resourceSet) | ||||
|             { | ||||
|                 string key = (string)entry.Key; | ||||
|                 object value = entry.Value; | ||||
| 
 | ||||
|                 if (value is string strValue) | ||||
|                 { | ||||
|                     resourceStrings[key] = strValue; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             // Load settings from app.config | ||||
|             var appConfig = ConfigurationManager.AppSettings.AllKeys | ||||
|                 .ToDictionary(k => k, k => ConfigurationManager.AppSettings[k]); | ||||
| 
 | ||||
|             // Merge keys from both sources (app.config has priority) | ||||
|             var allKeys = new HashSet<string>(resourceStrings.Keys.Concat(appConfig.Keys)); | ||||
|             var finalConfig = new Dictionary<string, string>(); | ||||
| 
 | ||||
|             foreach (var key in allKeys) | ||||
|             { | ||||
|                 if (appConfig.ContainsKey(key)) | ||||
|                     finalConfig[key] = appConfig[key]; | ||||
|                 else if (resourceStrings.ContainsKey(key)) | ||||
|                     finalConfig[key] = resourceStrings[key]; | ||||
|             } | ||||
| 
 | ||||
|             // Generate XML using XElement | ||||
|             XElement xml = new XElement("settings", | ||||
|                 finalConfig.Select(kv => new XElement(kv.Key, kv.Value)) | ||||
|             ); | ||||
| 
 | ||||
|             Server.ServeResource(context, xml.ToString(), "application/xml"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -4,18 +4,21 @@ using System.Security.Cryptography; | |||
| using System.Collections.Generic; | ||||
| using System.Net; | ||||
| using System.Threading.Tasks; | ||||
| using System.Net.Http; | ||||
| 
 | ||||
| namespace WelsonJS.Launcher.ResourceTools | ||||
| { | ||||
|     public class Tfa : IResourceTool | ||||
|     { | ||||
|         private ResourceServer Server; | ||||
|         private readonly ResourceServer Server; | ||||
|         private readonly HttpClient _httpClient; | ||||
|         private const string Prefix = "tfa/"; | ||||
|         private const string Base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; | ||||
| 
 | ||||
|         public Tfa(ResourceServer server) | ||||
|         public Tfa(ResourceServer server, HttpClient httpClient) | ||||
|         { | ||||
|             Server = server; | ||||
|             _httpClient = httpClient; | ||||
|         } | ||||
| 
 | ||||
|         public bool CanHandle(string path) | ||||
|  |  | |||
|  | @ -8,13 +8,14 @@ namespace WelsonJS.Launcher.ResourceTools | |||
| { | ||||
|     public class Whois : IResourceTool | ||||
|     { | ||||
|         private ResourceServer Server; | ||||
|         private readonly ResourceServer Server; | ||||
|         private readonly HttpClient _httpClient; | ||||
|         private const string Prefix = "whois/"; | ||||
|         private const int Timeout = 5000; | ||||
| 
 | ||||
|         public Whois(ResourceServer server) | ||||
|         public Whois(ResourceServer server, HttpClient httpClient) | ||||
|         { | ||||
|             Server = server; | ||||
|             _httpClient = httpClient; | ||||
|         } | ||||
| 
 | ||||
|         public bool CanHandle(string path) | ||||
|  | @ -32,32 +33,27 @@ namespace WelsonJS.Launcher.ResourceTools | |||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             using (var client = new HttpClient()) | ||||
|             string clientAddress = Program.GetAppConfig("WhoisClientAddress"); | ||||
| 
 | ||||
|             HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, Program.GetAppConfig("WhoisServerUrl")) | ||||
|             { | ||||
|                 client.Timeout = TimeSpan.FromMilliseconds(Timeout); | ||||
|                 Content = new StringContent($"query={Uri.EscapeDataString(query)}&ip={clientAddress}", Encoding.UTF8, "application/x-www-form-urlencoded") | ||||
|             }; | ||||
| 
 | ||||
|                 string clientAddress = Program.GetAppConfig("WhoisClientAddress"); | ||||
|             request.Headers.Add("Accept", "*/*"); | ||||
|             request.Headers.Add("User-Agent", context.Request.UserAgent); | ||||
|             _httpClient.DefaultRequestHeaders.Referrer = new Uri(Program.GetAppConfig("WhoisReferrerUrl")); | ||||
| 
 | ||||
|                 HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, Program.GetAppConfig("WhoisServerUrl")) | ||||
|                 { | ||||
|                     Content = new StringContent($"query={Uri.EscapeDataString(query)}&ip={clientAddress}", Encoding.UTF8, "application/x-www-form-urlencoded") | ||||
|                 }; | ||||
|             try | ||||
|             { | ||||
|                 HttpResponseMessage response = await _httpClient.SendAsync(request); | ||||
|                 string responseBody = await response.Content.ReadAsStringAsync(); | ||||
| 
 | ||||
|                 request.Headers.Add("Accept", "*/*"); | ||||
|                 request.Headers.Add("User-Agent", context.Request.UserAgent); | ||||
|                 client.DefaultRequestHeaders.Referrer = new Uri(Program.GetAppConfig("WhoisReferrerUrl")); | ||||
| 
 | ||||
|                 try | ||||
|                 { | ||||
|                     HttpResponseMessage response = await client.SendAsync(request); | ||||
|                     string responseBody = await response.Content.ReadAsStringAsync(); | ||||
| 
 | ||||
|                     Server.ServeResource(context, responseBody, "text/plain", (int)response.StatusCode); | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     Server.ServeResource(context, $"<error>Failed to process WHOIS request. {ex.Message}</error>", "application/xml", 500); | ||||
|                 } | ||||
|                 Server.ServeResource(context, responseBody, "text/plain", (int)response.StatusCode); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 Server.ServeResource(context, $"<error>Failed to process WHOIS request. {ex.Message}</error>", "application/xml", 500); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -74,8 +74,7 @@ | |||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Compile Include="IResourceTool.cs" /> | ||||
|     <Compile Include="ResourceTools\AzureAi.cs" /> | ||||
|     <Compile Include="ResourceTools\Config.cs" /> | ||||
|     <Compile Include="ResourceTools\Settings.cs" /> | ||||
|     <Compile Include="ResourceTools\Completion.cs" /> | ||||
|     <Compile Include="ResourceTools\DevTools.cs" /> | ||||
|     <Compile Include="ResourceTools\DnsQuery.cs" /> | ||||
|  |  | |||
|  | @ -5,8 +5,9 @@ | |||
|     <add key="RepositoryUrl" value="https://github.com/gnh1201/welsonjs"/> | ||||
|     <add key="CopilotUrl" value="https://copilot.microsoft.com/"/> | ||||
|     <add key="DevToolsPrefix" value="http://localhost:9222/"/> | ||||
|     <add key="AzureAiServiceUrl" value="https://ai-catswords656881030318.services.ai.azure.com/models/chat/completions?api-version=2024-05-01-preview"/> | ||||
|     <add key="AzureAiServicePrefix" value="https://ai-catswords656881030318.services.ai.azure.com/"/> | ||||
|     <add key="AzureAiServiceApiKey" value=""/> | ||||
|     <add key="AzureAiServiceApiVersion" value="2024-05-01-preview"/> | ||||
|     <add key="WhoisServerUrl" value="https://xn--c79as89aj0e29b77z.xn--3e0b707e/kor/whois.jsc"/> | ||||
|     <add key="WhoisReferrerUrl" value="https://xn--c79as89aj0e29b77z.xn--3e0b707e/kor/whois/whois.jsp"/> | ||||
|     <add key="WhoisClientAddress" value="141.101.82.1"/> | ||||
|  |  | |||
|  | @ -57,7 +57,19 @@ | |||
|                         <span class="icon mif-rocket"></span> | ||||
|                         <span class="caption">Copilot</span> | ||||
|                     </button> | ||||
|                     <span class="title">Generative</span> | ||||
|                     <button id="btnAzureAi" class="ribbon-button"> | ||||
|                         <span class="icon mif-rocket"></span> | ||||
|                         <span class="caption">Azure AI</span> | ||||
|                     </button> | ||||
|                     <button id="btnSavePrompt" class="ribbon-button"> | ||||
|                         <span class="icon mif-floppy-disks"></span> | ||||
|                         <span class="caption">Save</span> | ||||
|                     </button> | ||||
|                     <button id="btnLoadPrompt" class="ribbon-button"> | ||||
|                         <span class="icon mif-file-upload"></span> | ||||
|                         <span class="caption">Load</span> | ||||
|                     </button> | ||||
|                     <span class="title">Generative AI</span> | ||||
|                 </div> | ||||
|                 <div class="group"> | ||||
|                     <button id="btnWhois" class="ribbon-button"> | ||||
|  | @ -93,25 +105,36 @@ | |||
|     <script src="http://localhost:3000/ajax/libs/monaco-editor/0.52.2/min/vs/loader.js" integrity="sha384-pHG02SG8pId94Np3AbPmBEJ1yPqaH0IkJGLSNGXYmuGhkazT8Lr/57WYpbkGjJtu" crossorigin="anonymous"></script> | ||||
|     <script src="http://localhost:3000/ajax/libs/monaco-editor/0.52.2/min/vs/editor/editor.main.js" integrity="sha384-fj9z+NUc93I3woCCy5IRQfrQ8Amu1E27tllwgb5gz3d9Vr1ymS13xcF6two3e4KH" crossorigin="anonymous"></script> | ||||
|     <script> | ||||
|         var editor; | ||||
|         var currentFileName = "sayhello.js"; | ||||
|         var serverBaseUrl = "http://localhost:3000"; | ||||
|         let editor; | ||||
|         let currentFileName = "sayhello.js"; | ||||
|         const serverPrefix = "http://localhost:3000/"; | ||||
|         let settings = {}; | ||||
|         let promptMessages = []; | ||||
| 
 | ||||
|         function fetchSettings() { | ||||
|             axios.get(`${serverPrefix}settings`) | ||||
|                 .then(response => { | ||||
|                     const parser = new XMLParser(); | ||||
|                     const result = parser.parse(response.data); | ||||
|                     settings = result.settings; | ||||
|                 }); | ||||
|         } | ||||
| 
 | ||||
|         function resizeEditor() { | ||||
|             if (editor) { | ||||
|                 var ribbonHeight = document.querySelector('nav').offsetHeight; | ||||
|                 var bannerHeight = document.querySelector('.banner').offsetHeight; | ||||
|                 var containerHeight = document.documentElement.clientHeight - ribbonHeight - bannerHeight; | ||||
|                 const ribbonHeight = document.querySelector('nav').offsetHeight; | ||||
|                 const bannerHeight = document.querySelector('.banner').offsetHeight; | ||||
|                 const containerHeight = document.documentElement.clientHeight - ribbonHeight - bannerHeight; | ||||
|                 document.getElementById('container').style.height = containerHeight + 'px'; | ||||
|                 editor.layout(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         function getSuggestions(word, range) { | ||||
|             return axios.get(`${serverBaseUrl}/completion/${encodeURIComponent(word)}`) | ||||
|                 .then(function (response) { | ||||
|                     var parser = new XMLParser(); | ||||
|                     var result = parser.parse(response.data); | ||||
|             return axios.get(`${serverPrefix}completion/${encodeURIComponent(word)}`) | ||||
|                 .then(response => { | ||||
|                     const parser = new XMLParser(); | ||||
|                     const result = parser.parse(response.data); | ||||
| 
 | ||||
|                     if (!result.suggestions || !result.suggestions.item) { | ||||
|                         return { | ||||
|  | @ -119,8 +142,8 @@ | |||
|                         }; | ||||
|                     } | ||||
| 
 | ||||
|                     var items = Array.isArray(result.suggestions.item) ? result.suggestions.item : [result.suggestions.item]; | ||||
|                     var suggestions = items.map(function (item) { | ||||
|                     const items = Array.isArray(result.suggestions.item) ? result.suggestions.item : [result.suggestions.item]; | ||||
|                     const suggestions = items.map(function (item) { | ||||
|                         return { | ||||
|                             label: item.label, | ||||
|                             kind: monaco.languages.CompletionItemKind.Text, | ||||
|  | @ -149,8 +172,8 @@ | |||
| 
 | ||||
|             monaco.languages.registerCompletionItemProvider('javascript', { | ||||
|                 provideCompletionItems: function (model, position) { | ||||
|                     var word = model.getWordUntilPosition(position); | ||||
|                     var range = { | ||||
|                     const word = model.getWordUntilPosition(position); | ||||
|                     const range = { | ||||
|                         startLineNumber: position.lineNumber, | ||||
|                         endLineNumber: position.lineNumber, | ||||
|                         startColumn: word.startColumn, | ||||
|  | @ -165,8 +188,8 @@ | |||
|         window.addEventListener('resize', resizeEditor); | ||||
| 
 | ||||
|         function getFileLanguage(fileName) { | ||||
|             var extension = fileName.split('.').pop().toLowerCase(); | ||||
|             var languageMap = { | ||||
|             const extension = fileName.split('.').pop().toLowerCase(); | ||||
|             const languageMap = { | ||||
|                 'js': 'javascript', | ||||
|                 'ts': 'typescript', | ||||
|                 'html': 'html', | ||||
|  | @ -186,7 +209,7 @@ | |||
|         } | ||||
| 
 | ||||
|         function navigate(href) { | ||||
|             var a = document.createElement("a"); | ||||
|             const a = document.createElement("a"); | ||||
|             a.href = href; | ||||
|             a.target = "_blank"; | ||||
|             document.body.appendChild(a); | ||||
|  | @ -205,13 +228,14 @@ | |||
|         } | ||||
| 
 | ||||
|         document.getElementById("fileInput").onchange = function (event) { | ||||
|             var file = event.target.files[0]; | ||||
|             const file = event.target.files[0]; | ||||
|             if (!file) return; | ||||
| 
 | ||||
|             currentFileName = file.name; | ||||
|             var reader = new FileReader(); | ||||
| 
 | ||||
|             const reader = new FileReader(); | ||||
|             reader.onload = function (e) { | ||||
|                 var language = getFileLanguage(file.name); | ||||
|                 let language = getFileLanguage(file.name); | ||||
|                 monaco.editor.setModelLanguage(editor.getModel(), language); | ||||
|                 editor.setValue(e.target.result); | ||||
|             }; | ||||
|  | @ -223,13 +247,14 @@ | |||
|         }; | ||||
| 
 | ||||
|         document.getElementById("btnSaveFile").onclick = function () { | ||||
|             var text = editor.getValue(); | ||||
|             var fileName = prompt("Enter file name:", currentFileName); | ||||
|             const text = editor.getValue(); | ||||
|             const fileName = prompt("Enter file name:", currentFileName); | ||||
|             if (!fileName) return; | ||||
| 
 | ||||
|             currentFileName = fileName; | ||||
|             var blob = new Blob([text], { type: 'text/plain' }); | ||||
|             var a = document.createElement('a'); | ||||
| 
 | ||||
|             const blob = new Blob([text], { type: 'text/plain' }); | ||||
|             const a = document.createElement('a'); | ||||
|             a.href = URL.createObjectURL(blob); | ||||
|             a.download = fileName; | ||||
|             document.body.appendChild(a); | ||||
|  | @ -244,7 +269,12 @@ | |||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             appendTextToEditor(`\n//${promptMessage}... Generating text with Copilot...`); | ||||
|             appendTextToEditor(`\n// ${promptMessage}... Generating text with Copilot...`); | ||||
| 
 | ||||
|             promptMessages.push({ | ||||
|                 role: 'user', | ||||
|                 content: promptMessage | ||||
|             }) | ||||
| 
 | ||||
|             (async function () { | ||||
|                 const targetWsUrl = await getTargetByUrl('copilot.microsoft.com'); | ||||
|  | @ -256,16 +286,94 @@ | |||
|             })(); | ||||
|         }; | ||||
| 
 | ||||
|         document.getElementById("btnWhois").onclick = function () { | ||||
|             const hostname = prompt("Enter a hostname or IP address:", ''); | ||||
|             if (!hostname || hostname.trim() == '') { | ||||
|                 alert("A hostname or IP address is required."); | ||||
|         document.getElementById("btnAzureAi").onclick = function () { | ||||
|             const promptMessage = prompt("Enter a prompt message:", ''); | ||||
|             if (!promptMessage || promptMessage.trim() == '') { | ||||
|                 alert("A prompt message is required."); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             axios.get(`${serverBaseUrl}/whois/${hostname}`).then(response => { | ||||
|                 const responseText = DOMPurify.sanitize(response.data, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] }); | ||||
|             appendTextToEditor(`\n// ${promptMessage}... Generating text with Azure AI...`); | ||||
| 
 | ||||
|             const apiKey = settings.AzureAiServiceApiKey; | ||||
|             const url = `${settings.AzureAiServicePrefix}models/chat/completions?api-version=${settings.AzureAiServiceApiVersion}`; | ||||
| 
 | ||||
|             promptMessages.push({ | ||||
|                 role: 'user', | ||||
|                 content: promptMessage | ||||
|             }) | ||||
| 
 | ||||
|             const data = { | ||||
|                 messages: promptMessages, | ||||
|                 max_tokens: 2048, | ||||
|                 temperature: 0.8, | ||||
|                 top_p: 0.1, | ||||
|                 presence_penalty: 0, | ||||
|                 frequency_penalty: 0, | ||||
|                 model: 'Phi-4' | ||||
|             }; | ||||
| 
 | ||||
|             axios.post(url, data, { | ||||
|                     headers: { | ||||
|                         'Content-Type': 'application/json', | ||||
|                         'api-key': apiKey | ||||
|                     } | ||||
|                 }) | ||||
|                 .then(response => { | ||||
|                     response.data.choices.forEach(x => { | ||||
|                         const responseContent = x.message.content; | ||||
|                         promptMessages.push({ | ||||
|                             role: 'assistant', | ||||
|                             content: responseContent | ||||
|                         }); | ||||
| 
 | ||||
|                         const responseText = DOMPurify.sanitize(responseContent, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] }); | ||||
|                         appendTextToEditor(`/*\n${responseText}\n*/`); | ||||
|                     }); | ||||
|                 }) | ||||
|                 .catch(error => { | ||||
|                     console.error('Error:', error.response?.data || error.message); | ||||
|                 }); | ||||
|         }; | ||||
| 
 | ||||
|         document.getElementById("btnSavePrompt").onclick = function () { | ||||
|             const text = JSON.stringify(promptMessages, null, 4); | ||||
|             const blob = new Blob([text], { type: 'text/plain' }); | ||||
|             const a = document.createElement('a'); | ||||
|             a.href = URL.createObjectURL(blob); | ||||
|             a.download = "prompt.json"; | ||||
|             document.body.appendChild(a); | ||||
|             a.click(); | ||||
|             document.body.removeChild(a); | ||||
|         }; | ||||
| 
 | ||||
|         document.getElementById("btnLoadPrompt").onclick = function () { | ||||
|             const fileInput = document.createElement('input'); | ||||
|             fileInput.type = 'file'; | ||||
|             fileInput.accept = '.json'; | ||||
|             fileInput.onchange = function (event) { | ||||
|                 const file = event.target.files[0]; | ||||
|                 if (!file) return; | ||||
| 
 | ||||
|                 let reader = new FileReader(); | ||||
|                 reader.onload = function (e) { | ||||
|                     promptMessages = JSON.parse(e.target.result); | ||||
|                     appendTextToEditor("\n//Prompt loaded successfully."); | ||||
|                 }; | ||||
|                 reader.readAsText(file); | ||||
|             }; | ||||
|             fileInput.click(); | ||||
|         }; | ||||
| 
 | ||||
|         document.getElementById("btnWhois").onclick = function () { | ||||
|             const hostname = prompt("Enter a hostname or IP address:", ''); | ||||
|             if (!hostname || hostname.trim() == '') { | ||||
|                 appendTextToEditor("\n// A hostname or IP address is required."); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             axios.get(`${serverPrefix}whois/${hostname}`).then(response => { | ||||
|                 const responseText = DOMPurify.sanitize(response.data, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] }); | ||||
|                 appendTextToEditor(`/*\n${responseText}\n*/`); | ||||
|             }).catch(error => { | ||||
|                 console.error(error); | ||||
|  | @ -275,11 +383,11 @@ | |||
|         document.getElementById("btnDnsQuery").onclick = function () { | ||||
|             const hostname = prompt("Enter a hostname or IP address:", ''); | ||||
|             if (!hostname || hostname.trim() == '') { | ||||
|                 alert("A hostname or IP address is required."); | ||||
|                 appendTextToEditor("\n// A hostname or IP address is required."); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             axios.get(`${serverBaseUrl}/dns-query/${hostname}`).then(response => { | ||||
|             axios.get(`${serverPrefix}dns-query/${hostname}`).then(response => { | ||||
|                 const responseText = response.data; | ||||
|                 appendTextToEditor(`/*\n${responseText}\n*/`); | ||||
|             }).catch(error => { | ||||
|  | @ -288,7 +396,7 @@ | |||
|         }; | ||||
| 
 | ||||
|         async function getTargetByUrl(urlPart) { | ||||
|             const response = await fetch(`${serverBaseUrl}/devtools/json`); | ||||
|             const response = await fetch(`${serverPrefix}devtools/json`); | ||||
|             const targets = await response.json(); | ||||
| 
 | ||||
|             const target = targets.find(target => target.url.includes(urlPart)); | ||||
|  | @ -347,11 +455,20 @@ | |||
|                 console.log("Sent successfully:", response.result); | ||||
| 
 | ||||
|                 if (response.id == 3) { | ||||
|                     appendTextToEditor(response.result.result.value); | ||||
|                     const responseContent = response.result.result.value; | ||||
| 
 | ||||
|                     promptMessages.push({ | ||||
|                         role: 'assistant', | ||||
|                         content: responseContent | ||||
|                     }); | ||||
| 
 | ||||
|                     appendTextToEditor(responseContent); | ||||
|                     socket.close(); | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         fetchSettings(); | ||||
|     </script> | ||||
| </body> | ||||
| </html> | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user