Program.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. using IES.ExamServer.DI;
  2. using IES.ExamServer.DI.SignalRHost;
  3. using IES.ExamServer.Helper;
  4. using Microsoft.AspNetCore.Hosting.Server.Features;
  5. using Microsoft.AspNetCore.Hosting.Server;
  6. using Microsoft.AspNetCore.SpaServices;
  7. using Microsoft.AspNetCore.StaticFiles;
  8. using Microsoft.Extensions.Caching.Memory;
  9. using System.Text.Json;
  10. using System.Text.Json.Nodes;
  11. using VueCliMiddleware;
  12. using IES.ExamServer.Services;
  13. using System.Text;
  14. using IES.ExamServer.Filters;
  15. using IES.ExamServer.Helpers;
  16. using Microsoft.Extensions.Hosting;
  17. using System.Security.Principal;
  18. using Microsoft.Extensions.FileProviders;
  19. using System.Text.Encodings.Web;
  20. using System.Text.Unicode;
  21. using Microsoft.Extensions.Logging;
  22. namespace IES.ExamServer
  23. {
  24. public class Program: IDisposable
  25. {
  26. public async static Task Main(string[] args)
  27. {
  28. //var mutex = new Mutex(true, "IES.ExamServer", out var createdNew);
  29. //if (!createdNew)
  30. //{
  31. // // 防止多开,重复启动
  32. // Console.WriteLine("The application is already running.");
  33. // return;
  34. //}
  35. //ProcessHelper.CloseConhost();
  36. //AppDomain.CurrentDomain.ProcessExit += OnExit;
  37. var builder = WebApplication.CreateBuilder(args);
  38. string path = $"{builder.Environment.ContentRootPath}/Configs";
  39. //builder.WebHost.UseKestrel(options =>
  40. //{
  41. // options.ListenAnyIP(5001, listenOptions =>
  42. // {
  43. // listenOptions.UseHttps($"{path}/cert.pfx", "cdhabook") ;
  44. // });
  45. //});
  46. builder.Configuration.SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
  47. builder.Services.AddSpaStaticFiles(opt => opt.RootPath = "ClientApp/dist");
  48. // Add services to the container.
  49. builder.Services.AddControllersWithViews().AddJsonOptions(options =>
  50. {
  51. // 设置 JSON 序列化选项
  52. options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All); // 允许所有 Unicode 字符
  53. options.JsonSerializerOptions.WriteIndented = true; // 格式化输出(可选)
  54. }); ;
  55. builder.Services.AddHttpClient();
  56. builder.Services.AddSignalR();
  57. builder.Services.AddHttpContextAccessor();
  58. string localAppDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
  59. string dbpath = $"{localAppDataPath}\\ExamServer\\LiteDB";
  60. if (!System.IO.Directory.Exists(dbpath))
  61. {
  62. System.IO.Directory.CreateDirectory(dbpath);
  63. }
  64. string liteDBPath = $"Filename={dbpath}\\data.db;Connection=shared";
  65. var connections_LiteDB = new List<LiteDBFactoryOptions>
  66. {
  67. new LiteDBFactoryOptions { Name = "Master", Connectionstring = liteDBPath}
  68. };
  69. builder.Services.AddLiteDB(connections_LiteDB);
  70. builder.Services.AddMemoryCache();
  71. // 注册 ConnectionService 为单例
  72. builder.Services.AddSingleton<DataCenterConnectionService>();
  73. builder.Services.AddCors(options =>
  74. {
  75. options.AddDefaultPolicy(
  76. builder =>
  77. {
  78. builder.AllowAnyOrigin()
  79. .AllowAnyHeader()
  80. .AllowAnyMethod();
  81. });
  82. });
  83. builder.Services.AddMvcFilter<AuthTokenActionFilter>();
  84. // 添加自定义日志提供程序
  85. //builder.Logging.ClearProviders();
  86. //bool enableConsoleOutput = true;
  87. //builder.Logging.AddProvider(new CustomFileLoggerProvider(Path.Combine(Directory.GetCurrentDirectory(), "Logs"), enableConsoleOutput));
  88. // 添加日志服务
  89. var app = builder.Build();
  90. // Configure the HTTP request pipeline.
  91. if (!app.Environment.IsDevelopment())
  92. {
  93. app.UseExceptionHandler("/Home/Error");
  94. // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
  95. app.UseHsts();
  96. }
  97. else { app.UseDeveloperExceptionPage(); }
  98. app.UseHttpsRedirection();
  99. app.UseDefaultFiles();
  100. var contentTypeProvider = new FileExtensionContentTypeProvider();
  101. contentTypeProvider.Mappings[".txt"] = "text/plain";
  102. contentTypeProvider.Mappings[".jpg"] = "image/jpeg";
  103. contentTypeProvider.Mappings[".jpeg"] = "image/jpeg";
  104. contentTypeProvider.Mappings[".png"] = "image/png";
  105. contentTypeProvider.Mappings[".html"] = "text/html";
  106. contentTypeProvider.Mappings[".js"] = "application/javascript";
  107. contentTypeProvider.Mappings[".css"] = "text/css";
  108. contentTypeProvider.Mappings[".mp4"] = "video/mp4";
  109. contentTypeProvider.Mappings[".mp3"] = "audio/mpeg";
  110. contentTypeProvider.Mappings[".json"] = "application/json";
  111. contentTypeProvider.Mappings[".pdf"] = "application/pdf";
  112. string packagePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package");
  113. if (!Directory.Exists(packagePath))
  114. {
  115. Directory.CreateDirectory(packagePath);
  116. }
  117. app.UseStaticFiles(new StaticFileOptions
  118. {
  119. FileProvider = new PhysicalFileProvider(
  120. Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package")),
  121. RequestPath = "/package",
  122. ContentTypeProvider = contentTypeProvider,
  123. });
  124. app.UseRouting();
  125. app.UseAuthorization();
  126. app.UseEndpoints(endpoints =>
  127. {
  128. endpoints.MapHub<SignalRExamServerHub>("/signalr/exam").RequireCors("any");
  129. endpoints.MapControllers();
  130. // NOTE: VueCliProxy is meant for developement and hot module reload
  131. // NOTE: SSR has not been tested
  132. // Production systems should only need the UseSpaStaticFiles() (above)
  133. // You could wrap this proxy in either
  134. // if (System.Diagnostics.Debugger.IsAttached)
  135. // or a preprocessor such as #if DEBUG
  136. /*
  137. npm install -g @vue
  138. vue create app
  139. */
  140. //#if DEBUG
  141. endpoints.MapToVueCliProxy(
  142. "{*path}",
  143. new SpaOptions { SourcePath = "ClientApp" },
  144. npmScript: (System.Diagnostics.Debugger.IsAttached) ? "serve" : null,
  145. // regex: "Compiled successfully",
  146. forceKill: true
  147. );
  148. //#else
  149. // endpoints.MapFallbackToFile("index.html");
  150. //#endif
  151. });
  152. IMemoryCache? cache = app.Services.GetRequiredService<IMemoryCache>();
  153. IHttpClientFactory? clientFactory = app.Services.GetRequiredService<IHttpClientFactory>();
  154. LiteDBFactory liteDBFactory = app.Services.GetRequiredService<LiteDBFactory>();
  155. JsonNode? data = null;
  156. int hybrid = 0;
  157. string remote = "127.0.0.1";
  158. string region = "局域网·内网";
  159. try
  160. {
  161. string? CenterUrl = builder.Configuration.GetValue<string>("ExamServer:CenterUrl");
  162. var httpclient = clientFactory.CreateClient();
  163. httpclient.Timeout= TimeSpan.FromSeconds(10);
  164. HttpResponseMessage message = await httpclient.PostAsJsonAsync($"{CenterUrl}/core/system-info", new { });
  165. if (message.IsSuccessStatusCode)
  166. {
  167. string content = await message.Content.ReadAsStringAsync();
  168. data = JsonSerializer.Deserialize<JsonNode>(content);
  169. data!["centerUrl"]=CenterUrl;
  170. cache.Set(Constant._KeyServerCenter, data);
  171. remote=$"{data["ip"]}";
  172. region=$"{data["region"]}";
  173. hybrid =1;
  174. }
  175. }
  176. catch (Exception ex)
  177. {
  178. //云端服务连接失败
  179. hybrid = 0;
  180. }
  181. //单例模式存储云端数据中心连接状态
  182. DataCenterConnectionService connectionService= app.Services.GetRequiredService<DataCenterConnectionService>();
  183. connectionService.centerUrl = hybrid == 1 ? $"{data?["centerUrl"]}" : null;
  184. connectionService.dataCenterIsConnected = hybrid==1 ? true : false;
  185. var lifetime = app.Services.GetRequiredService<IHostApplicationLifetime>();
  186. lifetime.ApplicationStarted.Register(() =>
  187. {
  188. var server = app.Services.GetService<IServer>();
  189. var logger = app.Services.GetRequiredService<ILogger<Program>>();
  190. var d = server?.Features.Get<IServerAddressesFeature>();
  191. IEnumerable<string>? _url = server?.Features.Get<IServerAddressesFeature>()?.Addresses;
  192. ServerDevice serverDevice = IndexService.GetServerDevice(remote, region, _url);
  193. //int domainStatus =0;
  194. //string domain = builder.Configuration.GetValue<string>("ExamClient:Domain");
  195. //foreach (var network in serverDevice.networks)
  196. //{
  197. // try
  198. // {
  199. // string domain_entry = $"{network.ip} {domain}";
  200. // string hostsFilePath = @"C:\Windows\System32\drivers\etc\hosts";
  201. // string content = File.ReadAllText(hostsFilePath, Encoding.UTF8);
  202. // if (!content.Contains(domain_entry))
  203. // {
  204. // content += Environment.NewLine + domain_entry;
  205. // // 使用管理员权限运行此程序,不然会抛出UnauthorizedAccessException
  206. // File.WriteAllText(hostsFilePath, content, Encoding.UTF8);
  207. // domainStatus=1;
  208. // // Console.WriteLine("Hosts file updated successfully.");
  209. // }
  210. // else
  211. // {
  212. // domainStatus=1;
  213. // //Console.WriteLine("The entry already exists in the hosts file.");
  214. // }
  215. // }
  216. // catch (UnauthorizedAccessException)
  217. // {
  218. // domainStatus=2;
  219. // // Console.WriteLine("You need to run this program with administrative privileges to modify the hosts file.");
  220. // }
  221. // catch (Exception ex)
  222. // {
  223. // domainStatus=0;
  224. // // Console.WriteLine($"An error occurred: {ex.Message}");
  225. // }
  226. //}
  227. //serverDevice.domainStatus=domainStatus;
  228. //serverDevice.domain=domain;
  229. logger.LogInformation($"服务端设备信息:{JsonSerializer.Serialize(serverDevice,options: new JsonSerializerOptions { Encoder =JavaScriptEncoder.Create(UnicodeRanges.All)})}");
  230. cache.Set(Constant._KeyServerDevice, serverDevice);
  231. });
  232. // 退出程序
  233. lifetime.ApplicationStopping.Register(() =>
  234. {
  235. Console.WriteLine("The application is stopping. Performing cleanup...");
  236. // 在这里添加清理资源、保存数据等逻辑
  237. });
  238. app.MapGet("/hello", (ILogger<Program> logger) =>
  239. {
  240. logger.LogInformation("This is an information log.");
  241. logger.LogError("This is an error log.");
  242. var data = new { Id = 123, Name = "Test Data服务端设备信息" };
  243. logger.LogData(data, data.Id.ToString());
  244. return "Hello World!";
  245. });
  246. await app.RunAsync();
  247. }
  248. //static void OnExit(object sender, EventArgs e)
  249. //{
  250. // Console.WriteLine("正在退出程序...");
  251. // // 执行任何需要的清理工作
  252. // // 例如: 保存状态,关闭文件和数据库连接等
  253. // // 通过调用 Environment.Exit 来结束进程
  254. // Environment.Exit(0);
  255. //}
  256. public void Dispose()
  257. {
  258. // 清理代码
  259. //Console.WriteLine("正在退出...");
  260. // 释放资源、关闭连接等
  261. }
  262. }
  263. public class SystemInfo
  264. {
  265. public string? id { get; set; }
  266. public string? version { get; set; }
  267. public string? description { get; set; }
  268. public long nowtime { get; set; }
  269. public string? region { get; set; }
  270. public string? ip { get; set; }
  271. public string? date { get; set; }
  272. public string? centerUrl { get; set; }
  273. }
  274. }