using System.Diagnostics; using Flurl.Http; using Hellang.Middleware.ProblemDetails; using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.Services; using InnovEnergy.App.Backend.Websockets; using InnovEnergy.App.Backend.DeleteOldData; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Mvc; using Microsoft.OpenApi.Models; using InnovEnergy.App.Backend.DataTypes.Methods; using InnovEnergy.Lib.S3Utils; using InnovEnergy.Lib.S3Utils.DataTypes; using InnovEnergy.Lib.Utils; namespace InnovEnergy.App.Backend; public static class Program { public static async Task Main(String[] args) { //First, we initialize the database. This is an empty constructor of the Db class that will be called. //In addition, we initialize WatchDog in order to restart the backend service in case of failure. //Finally, we start all the backend services. We call the InitializeEnvironment function of RabbitMqManager to create the queue (factory/connection) //Then, we generate a consumer that binds to the queue. This is a separate async Task so it must not be awaited (it acts as a separate thread). //Finally, we call the MonitorSalimaxInstallationTable and MonitorSalidomoInstallationTable from the WebsocketManager class. //Those methods will build in-memory data structures to track the connected frontends and update them regarding the offline installations. Watchdog.NotifyReady(); Db.Init(); LoadEnvFile(); EnsureDocumentBucketExists().SupressAwaitWarning(); DiagnosticService.Initialize(); TicketDiagnosticService.Initialize(); NetworkProviderService.Initialize(); AlarmReviewService.StartDailyScheduler(); DailyIngestionService.StartScheduler(); ReportAggregationService.StartScheduler(); var builder = WebApplication.CreateBuilder(args); RabbitMqManager.InitializeEnvironment(); RabbitMqManager.StartRabbitMqConsumer().SupressAwaitWarning(); WebsocketManager.MonitorInstallationTable().SupressAwaitWarning(); DeleteOldDataFromS3.StartScheduler(); builder.Services.AddControllers(); builder.Services.AddProblemDetails(setup => { //This includes the stacktrace in Development Env setup.IncludeExceptionDetails = (_, _) => builder.Environment.IsDevelopment() || builder.Environment.IsStaging(); //This handles our Exceptions setup.Map(exception => new ProblemDetails { Detail = exception.Detail, Status = exception.Status, Type = exception.Type, Instance = exception.Instance }); }); builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", OpenApiInfo); c.UseAllOfToExtendReferenceSchemas(); c.SupportNonNullableReferenceTypes(); }); var app = builder.Build(); app.Use(async (context, next) => { context.Request.WriteLine(); await next(context); }); app.UseWebSockets(); app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseCors(p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()) ; //app.UseHttpsRedirection(); app.MapControllers(); app.UseProblemDetails(); app.Run(); } private static void LoadEnvFile() { var envPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ".env"); if (!File.Exists(envPath)) envPath = ".env"; // fallback for dev if (!File.Exists(envPath)) return; foreach (var line in File.ReadAllLines(envPath)) { var trimmed = line.Trim(); if (trimmed.Length == 0 || trimmed.StartsWith('#')) continue; var idx = trimmed.IndexOf('='); if (idx <= 0) continue; var key = trimmed[..idx].Trim(); var value = trimmed[(idx + 1)..].Trim(); Environment.SetEnvironmentVariable(key, value); } } public const String DocumentBucketName = "inesco-documents"; private static async Task EnsureDocumentBucketExists() { try { var region = new S3Region("https://sos-ch-dk-2.exo.io", ExoCmd.S3Credentials); var buckets = await region.ListAllBuckets(); if (buckets.Buckets.All(b => b.BucketName != DocumentBucketName)) { await region.PutBucket(DocumentBucketName); Console.WriteLine($"[Documents] Created S3 bucket: {DocumentBucketName}"); } else { Console.WriteLine($"[Documents] S3 bucket already exists: {DocumentBucketName}"); } } catch (Exception ex) { Console.WriteLine($"[Documents] Warning: Could not ensure bucket exists: {ex.Message}"); } } private static OpenApiInfo OpenApiInfo { get; } = new OpenApiInfo { Title = "Inesco Backend API", Version = "v1" }; }