diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 92a593a..6a86a48 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -11,5 +11,6 @@ + \ No newline at end of file diff --git a/src/FiscalOS.AdminCLI/.editorconfig b/src/FiscalOS.AdminCLI/.editorconfig index e04d110..e0edca3 100644 --- a/src/FiscalOS.AdminCLI/.editorconfig +++ b/src/FiscalOS.AdminCLI/.editorconfig @@ -1,3 +1,4 @@ [*.cs] dotnet_diagnostic.CA1303.severity = none +dotnet_diagnostic.CA2007.severity = none diff --git a/src/FiscalOS.AdminCLI/App.cs b/src/FiscalOS.AdminCLI/App.cs new file mode 100644 index 0000000..4ebd969 --- /dev/null +++ b/src/FiscalOS.AdminCLI/App.cs @@ -0,0 +1,52 @@ +namespace FiscalOS.AdminCLI; + +internal sealed class App( + IAnsiConsole console, + IPasswordHasher passwordHasher, + IServiceScopeFactory serviceScopeFactory +) : IHostedService +{ + private readonly IAnsiConsole _console = console; + private readonly IServiceScopeFactory _serviceScopeFactory = serviceScopeFactory; + + public async Task StartAsync(CancellationToken cancellationToken) + { + using var scope = _serviceScopeFactory.CreateScope(); + var config = scope.ServiceProvider.GetRequiredService(); + var appDbContext = scope.ServiceProvider.GetRequiredService(); + + var selectionPrompt = new SelectionPrompt() + .Title("What do you want to do?") + .AddChoices(Commands.All); + + var command = await _console.PromptAsync(selectionPrompt, cancellationToken); + + switch (command) + { + case Commands.CreateUser: + var username = await _console.AskAsync("Enter the [green]username[/]:", cancellationToken); + var password = _console.Prompt( + new TextPrompt("Enter the [green]password[/]:") + .PromptStyle("red") + .Secret() + ); + + var hashedPassword = passwordHasher.Hash(password); + var user = User.From(username, hashedPassword); + + await appDbContext.Users.AddAsync(user, cancellationToken); + await appDbContext.SaveChangesAsync(cancellationToken); + + _console.MarkupLine($"User [green]{username}[/] created successfully."); + break; + case Commands.Exit: + default: + break; + } + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} diff --git a/src/FiscalOS.AdminCLI/Commands.cs b/src/FiscalOS.AdminCLI/Commands.cs new file mode 100644 index 0000000..031eb96 --- /dev/null +++ b/src/FiscalOS.AdminCLI/Commands.cs @@ -0,0 +1,9 @@ +namespace FiscalOS.AdminCLI; + +internal static class Commands +{ + public const string CreateUser = "Create User"; + public const string Exit = "Exit"; + + public static readonly string[] All = [CreateUser, Exit]; +} \ No newline at end of file diff --git a/src/FiscalOS.AdminCLI/FiscalOS.AdminCLI.csproj b/src/FiscalOS.AdminCLI/FiscalOS.AdminCLI.csproj index 6a70a1a..bdc14e5 100644 --- a/src/FiscalOS.AdminCLI/FiscalOS.AdminCLI.csproj +++ b/src/FiscalOS.AdminCLI/FiscalOS.AdminCLI.csproj @@ -4,4 +4,20 @@ Exe + + + + + + + + PreserveNewest + + + + + + + + diff --git a/src/FiscalOS.AdminCLI/Program.cs b/src/FiscalOS.AdminCLI/Program.cs index b5ed5d6..4f575c1 100644 --- a/src/FiscalOS.AdminCLI/Program.cs +++ b/src/FiscalOS.AdminCLI/Program.cs @@ -1,11 +1,15 @@ -// TODO: We need a way to add -// a user to the system with -// a properly hashed password -// so need something like this: -// -// 1. Ask for username -// 2. Ask for user password -// 3. Hash user password -// 4. Save user to database - -Console.WriteLine("Hello, World!"); \ No newline at end of file +await Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration( + static c => c.SetBasePath(AppContext.BaseDirectory) + .AddJsonFile("appsettings.json") + .AddEnvironmentVariables() + ) + .ConfigureLogging(static c => c.ClearProviders()) + .ConfigureServices(static (_, services) => + { + services.AddSingleton(AnsiConsole.Console); + services.AddInfrastructure(); + services.AddHostedService(); + }) + .Build() + .StartAsync(); diff --git a/src/FiscalOS.AdminCLI/Usings.cs b/src/FiscalOS.AdminCLI/Usings.cs new file mode 100644 index 0000000..4b1a40b --- /dev/null +++ b/src/FiscalOS.AdminCLI/Usings.cs @@ -0,0 +1,12 @@ +global using FiscalOS.AdminCLI; +global using FiscalOS.Core.Authentication; +global using FiscalOS.Core.Identity; +global using FiscalOS.Infra.Data; +global using FiscalOS.Infra.DependencyInjection; + +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Logging; + +global using Spectre.Console; diff --git a/src/FiscalOS.AdminCLI/appsettings.Example.json b/src/FiscalOS.AdminCLI/appsettings.Example.json new file mode 100644 index 0000000..a2cd868 --- /dev/null +++ b/src/FiscalOS.AdminCLI/appsettings.Example.json @@ -0,0 +1,5 @@ +{ + "AppDbContextOptions": { + "DatabaseFilePath": "DatabaseFilePath" + } +} diff --git a/src/FiscalOS.Infra/Data/AppDbContext.cs b/src/FiscalOS.Infra/Data/AppDbContext.cs index 1858447..db90d6f 100644 --- a/src/FiscalOS.Infra/Data/AppDbContext.cs +++ b/src/FiscalOS.Infra/Data/AppDbContext.cs @@ -51,9 +51,7 @@ public sealed class AppDbContext(IOptions ctxOptions) : DbC .OnDelete(DeleteBehavior.Cascade); eb.Property(static u => u.Username); - eb.Property(static u => u.HashedPassword); - }); - + eb.Property(static u => u.HashedPassword); }); modelBuilder.Entity(static eb => { eb.Property(static t => t.Id); diff --git a/src/FiscalOS.Infra/Data/AppDbContextOptions.cs b/src/FiscalOS.Infra/Data/AppDbContextOptions.cs index b0d48e4..231cf41 100644 --- a/src/FiscalOS.Infra/Data/AppDbContextOptions.cs +++ b/src/FiscalOS.Infra/Data/AppDbContextOptions.cs @@ -3,6 +3,7 @@ namespace FiscalOS.Infra.Data; public sealed record AppDbContextOptions { public string DatabaseFilePath { get; init; } = string.Empty; + public string GetFullyQualifiedDatabasePath() { return Path.GetFullPath(DatabaseFilePath, AppContext.BaseDirectory); diff --git a/src/FiscalOS.Infra/DependencyInjection/ServiceCollectionExtensions.cs b/src/FiscalOS.Infra/DependencyInjection/ServiceCollectionExtensions.cs index 158d635..e5754c2 100644 --- a/src/FiscalOS.Infra/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/FiscalOS.Infra/DependencyInjection/ServiceCollectionExtensions.cs @@ -5,6 +5,7 @@ public static class ServiceCollectionExtensions public static IServiceCollection AddInfrastructure(this IServiceCollection services) { services.AddSingleton(TimeProvider.System); + services.ConfigureOptions(); services.AddSingleton(TokenGenerator.From); services.AddSingleton(PasswordHasher.From); services.ConfigureOptions();