using JSMR.Application.DI; using JSMR.Infrastructure.Data; using JSMR.Infrastructure.DI; using JSMR.Worker.Options; using JSMR.Worker.Services; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System.CommandLine; using System.Text; HostApplicationBuilder builder = Host.CreateApplicationBuilder(args); // Build a single configuration pipeline builder.Configuration .SetBasePath(builder.Environment.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables(); if (builder.Environment.IsDevelopment()) { builder.Configuration.AddUserSecrets(typeof(Program).Assembly, optional: true); } // Pull the connection string from config (appsettings or secrets or env) string connectionString = builder.Configuration.GetConnectionString("AppDb") ?? throw new InvalidOperationException("Missing ConnectionStrings:AppDb"); //builder.Services.AddSerilog(o => o // .WriteTo.Console() // .MinimumLevel.Information()); builder.Services .AddApplication() .AddInfrastructure() .AddMemoryCache(); //string connectionString = builder.Configuration.GetConnectionString("AppDb") // ?? throw new InvalidOperationException("Missing ConnectionStrings:AppDb"); builder.Services.AddDbContextFactory(optionsBuilder => optionsBuilder .UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)) .EnableSensitiveDataLogging(false)); // Worker services builder.Services.AddSingleton(); builder.Services.AddTransient(); RootCommand rootCommand = new("JSMR worker"); Command scan = new("scan", "Scan and update the database"); Option localeOption = new("--locale", "-l") { Description = "Locale (Japanese/English)", Required = false }; Option startOption = new("--start", "-s") { Description = "Start page (default = checkpoint+1 or 1)" }; Option endOption = new("--end", "-e") { Description = "End page (optional)" }; Option sizeOption = new("--pageSize", "-ps") { Description = "Page size (default from config or 100)", DefaultValueFactory = _ => 100 }; Option watchOption = new("--watch", "-w") { Description = "Loop forever", DefaultValueFactory = _ => false }; Option everyOption = new("--every", "-e") { Description = "Interval when --watch is set", DefaultValueFactory = _ => TimeSpan.FromMinutes(5) }; scan.Add(localeOption); scan.Add(startOption); scan.Add(endOption); scan.Add(sizeOption); scan.Add(watchOption); scan.Add(everyOption); scan.SetAction(async (parseResult, cancellationToken) => { using var host = builder.Build(); var runner = host.Services.GetRequiredService(); ScanOptions options = new() { Locale = parseResult.GetValue(localeOption) ?? default!, StartPage = parseResult.GetValue(startOption), EndPage = parseResult.GetValue(endOption), PageSize = parseResult.GetValue(sizeOption), Watch = parseResult.GetValue(watchOption), Interval = parseResult.GetValue(everyOption) }; using CancellationTokenSource cancellationTokenSource = new(); Console.CancelKeyPress += (_, eventArgs) => { eventArgs.Cancel = true; cancellationTokenSource.Cancel(); }; await runner.RunAsync(options, cancellationTokenSource.Token); }); rootCommand.Add(scan); //rootCommand.SetAction(async (parseResult, cancellationToken) => await rootCommand.InvokeAsync("scan")); Command schemaDumpCommand = new("schema-dump", "Emit EF model as a full create script (desired.sql)"); schemaDumpCommand.SetAction(async (parseResult, cancellationToken) => { using var host = builder.Build(); await using var scope = host.Services.CreateAsyncScope(); var db = scope.ServiceProvider.GetRequiredService(); var sql = db.Database.GenerateCreateScript(); var outPath = Path.GetFullPath("desired.sql"); await File.WriteAllTextAsync(outPath, sql, cancellationToken); Console.WriteLine($"[OK] Wrote EF model create script to: {outPath}"); }); rootCommand.Add(schemaDumpCommand); Console.OutputEncoding = Encoding.UTF8; return await rootCommand.Parse(args).InvokeAsync();