Program.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. using System.Text.Json.Serialization;
  2. using Microsoft.AspNetCore.Components;
  3. using Microsoft.AspNetCore.DataProtection;
  4. using Microsoft.AspNetCore.Hosting.StaticWebAssets;
  5. using RackPeek.Domain;
  6. using RackPeek.Domain.Git;
  7. using RackPeek.Domain.Persistence;
  8. using RackPeek.Domain.Persistence.Yaml;
  9. using RackPeek.Web.Api;
  10. using RackPeek.Web.Components;
  11. using Shared.Rcl;
  12. namespace RackPeek.Web;
  13. public class Program {
  14. public static async Task<WebApplication> BuildApp(WebApplicationBuilder builder) {
  15. StaticWebAssetsLoader.UseStaticWebAssets(
  16. builder.Environment,
  17. builder.Configuration
  18. );
  19. var yamlDir = builder.Configuration.GetValue<string>("RPK_YAML_DIR") ?? "./config";
  20. var yamlFileName = "config.yaml";
  21. var basePath = Directory.GetCurrentDirectory();
  22. var yamlPath = Path.IsPathRooted(yamlDir)
  23. ? yamlDir
  24. : Path.Combine(basePath, yamlDir);
  25. Directory.CreateDirectory(yamlPath);
  26. var yamlFilePath = Path.Combine(yamlPath, yamlFileName);
  27. if (!File.Exists(yamlFilePath)) {
  28. try {
  29. await using var fs = new FileStream(
  30. yamlFilePath,
  31. FileMode.CreateNew,
  32. FileAccess.Write,
  33. FileShare.None);
  34. await using var writer = new StreamWriter(fs);
  35. await writer.WriteLineAsync("# default config");
  36. }
  37. catch (IOException) when (File.Exists(yamlFilePath)) {
  38. // Another instance created the file between the existence
  39. // check and CreateNew — the config is there, carry on.
  40. }
  41. }
  42. // Persist DataProtection keys next to the config so they live on the
  43. // mounted volume: they survive container recreation, and key writes
  44. // no longer depend on a writable user profile or /tmp — both of
  45. // which are unavailable in hardened Docker setups (#312).
  46. var keysPath = Path.Combine(yamlPath, ".dataprotection");
  47. Directory.CreateDirectory(keysPath);
  48. builder.Services.AddDataProtection()
  49. .PersistKeysToFileSystem(new DirectoryInfo(keysPath))
  50. .SetApplicationName("RackPeek");
  51. builder.Services.ConfigureHttpJsonOptions(options => {
  52. options.SerializerOptions.Converters.Add(
  53. new JsonStringEnumConverter());
  54. });
  55. builder.Services.AddScoped<ITextFileStore, PhysicalTextFileStore>();
  56. builder.Services.AddScoped(sp => {
  57. NavigationManager nav = sp.GetRequiredService<NavigationManager>();
  58. return new HttpClient {
  59. BaseAddress = new Uri(nav.BaseUri)
  60. };
  61. });
  62. builder.Services.AddGitServices(builder.Configuration, yamlPath);
  63. var resources = new ResourceCollection();
  64. builder.Services.AddSingleton(resources);
  65. builder.Services.AddScoped<RackPeekConfigMigrationDeserializer>();
  66. builder.Services.AddScoped<IResourceYamlMigrationService, ResourceYamlMigrationService>();
  67. builder.Services.AddScoped<IResourceCollection>(sp =>
  68. new YamlResourceCollection(
  69. yamlFilePath,
  70. sp.GetRequiredService<ITextFileStore>(),
  71. sp.GetRequiredService<ResourceCollection>(),
  72. sp.GetRequiredService<IResourceYamlMigrationService>()));
  73. // Infrastructure
  74. builder.Services.AddYamlRepos();
  75. builder.Services.AddUseCases();
  76. builder.Services.AddCommands();
  77. builder.Services.AddScoped<IConsoleEmulator, ConsoleEmulator>();
  78. // Razor Components
  79. builder.Services.AddRazorComponents()
  80. .AddInteractiveServerComponents();
  81. WebApplication app = builder.Build();
  82. if (!app.Environment.IsDevelopment()) {
  83. app.UseExceptionHandler("/Error");
  84. app.UseHsts();
  85. }
  86. app.UseHttpsRedirection();
  87. app.UseStaticFiles();
  88. app.UseRouting();
  89. app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);
  90. app.UseAntiforgery();
  91. app.MapInventoryApi();
  92. app.MapStaticAssets();
  93. app.MapRazorComponents<App>()
  94. .AddInteractiveServerRenderMode();
  95. return app;
  96. }
  97. public static async Task Main(string[] args) {
  98. WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
  99. WebApplication app = await BuildApp(builder);
  100. await app.RunAsync();
  101. }
  102. }