using System; using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using SNote.Server.Data; using SNote.Server.Endpoints; using SNote.Server.Security; namespace SNote.Server; public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Add Configuration & DB Context (SQLite) var dbPath = Path.Combine(AppContext.BaseDirectory, "snote_server.db"); builder.Services.AddDbContext(options => options.UseSqlite($"Data Source={dbPath}")); // Register custom security managers & HTTP utilities builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); // Register OpenAPI explorer builder.Services.AddOpenApi(); var app = builder.Build(); // 1. Initialize SQLite Database Schema using (var scope = app.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); db.Database.EnsureCreated(); } // Configure Development Tools if (app.Environment.IsDevelopment()) { app.MapOpenApi(); } // 2. Map Modular API Endpoint Classes app.MapAuthEndpoints(); app.MapNodeEndpoints(); app.MapSyncEndpoints(); // Welcome / Healthcheck root app.MapGet("/", () => Results.Ok(new { service = "SNote Server", status = "Online", time = DateTime.UtcNow })); // 3. Start Background Sync Runner var hostApplicationLifetime = app.Services.GetRequiredService(); var services = app.Services; // Dynamic Bootstrapping check on startup var config = app.Configuration; var bootstrapUrl = config["BootstrapFromPeer"]; if (!string.IsNullOrEmpty(bootstrapUrl)) { Task.Run(async () => { // Wait briefly for server startup await Task.Delay(2000); using var scope = services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var certManager = scope.ServiceProvider.GetRequiredService(); var client = scope.ServiceProvider.GetRequiredService(); await SyncEndpoints.BootstrapFromPeerServerAsync(bootstrapUrl, db, certManager, client); }); } // Dynamic Handshake: Connect to target destination server and register local address on startup var destUrl = config["Sync:DestinationServerUrl"]; var localUrl = config["Sync:LocalServerUrl"]; if (!string.IsNullOrEmpty(destUrl) && !string.IsNullOrEmpty(localUrl)) { Task.Run(async () => { // Wait briefly for server startup await Task.Delay(2000); using var scope = services.CreateScope(); var certManager = scope.ServiceProvider.GetRequiredService(); var client = scope.ServiceProvider.GetRequiredService(); await SyncEndpoints.RegisterPeerWithDestinationAsync(destUrl, localUrl, certManager, client); }); } // Heartbeat Loop: Periodic Downstream Heartbeat pinger pings downstream peers every 30 seconds Task.Run(async () => { var token = hostApplicationLifetime.ApplicationStopping; using var scope = services.CreateScope(); var peerCache = scope.ServiceProvider.GetRequiredService(); var certManager = scope.ServiceProvider.GetRequiredService(); var client = scope.ServiceProvider.GetRequiredService(); await SyncEndpoints.StartHeartbeatPingerAsync(peerCache, certManager, client, token); }); app.Run(); } }