using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Net.Http; using System.Reflection; using System.Runtime.Loader; using System.Threading.Tasks; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.AspNetCore.Localization; using Microsoft.Extensions.DependencyInjection; using Microsoft.JSInterop; using Oqtane.Modules; using Oqtane.Providers; using Oqtane.Services; using Oqtane.Shared; using Oqtane.UI; namespace Oqtane.Client { public class Program { public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("app"); HttpClient httpClient = new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)}; builder.Services.AddSingleton(httpClient); builder.Services.AddOptions(); // Register localization services builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); // register auth services builder.Services.AddAuthorizationCore(); builder.Services.AddScoped(); builder.Services.AddScoped(s => s.GetRequiredService()); // register scoped core services builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); await LoadClientAssemblies(httpClient); var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies(); foreach (var assembly in assemblies) { // dynamically register module services var implementationTypes = assembly.GetInterfaces(); foreach (var implementationType in implementationTypes) { if (implementationType.AssemblyQualifiedName != null) { var serviceType = Type.GetType(implementationType.AssemblyQualifiedName.Replace(implementationType.Name, $"I{implementationType.Name}")); builder.Services.AddScoped(serviceType ?? implementationType, implementationType); } } // register client startup services var startUps = assembly.GetInstances(); foreach (var startup in startUps) { startup.ConfigureServices(builder.Services); } } var host = builder.Build(); var jsRuntime = host.Services.GetRequiredService(); var interop = new Interop(jsRuntime); var localizationCookie = await interop.GetCookie(CookieRequestCultureProvider.DefaultCookieName); var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie).UICultures[0].Value; var localizationService = host.Services.GetRequiredService(); var cultures = await localizationService.GetCulturesAsync(); if (culture == null || !cultures.Any(c => c.Name.Equals(culture, StringComparison.OrdinalIgnoreCase))) { culture = cultures.Single(c => c.IsDefault).Name; } SetCulture(culture); ServiceActivator.Configure(host.Services); await host.RunAsync(); } private static async Task LoadClientAssemblies(HttpClient http) { // get list of loaded assemblies on the client var assemblies = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().Name).ToList(); // get assemblies from server and load into client app domain var zip = await http.GetByteArrayAsync($"/~/api/Installation/load"); // asemblies and debug symbols are packaged in a zip file using (ZipArchive archive = new ZipArchive(new MemoryStream(zip))) { var dlls = new Dictionary(); var pdbs = new Dictionary(); foreach (ZipArchiveEntry entry in archive.Entries) { if (!assemblies.Contains(Path.GetFileNameWithoutExtension(entry.Name))) { using (var memoryStream = new MemoryStream()) { entry.Open().CopyTo(memoryStream); byte[] file = memoryStream.ToArray(); switch (Path.GetExtension(entry.Name)) { case ".dll": dlls.Add(entry.Name, file); break; case ".pdb": pdbs.Add(entry.Name, file); break; } } } } foreach (var item in dlls) { if (pdbs.ContainsKey(item.Key)) { AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value), new MemoryStream(pdbs[item.Key])); } else { AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value)); } } } } private static void SetCulture(string culture) { var cultureInfo = CultureInfo.GetCultureInfo(culture); CultureInfo.DefaultThreadCurrentCulture = cultureInfo; CultureInfo.DefaultThreadCurrentUICulture = cultureInfo; } } }