feat: implement identity authentication, authorization policies, and MAUI platform support with Docker orchestration

This commit is contained in:
2026-04-29 20:37:41 +02:00
parent 10efed0369
commit 0210611edf
55 changed files with 2359 additions and 949 deletions
+31 -28
View File
@@ -9,41 +9,44 @@ public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
try
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>();
builder.Services.AddMauiBlazorWebView();
builder.Services.AddMauiBlazorWebView();
#if DEBUG
builder.Services.AddBlazorWebViewDeveloperTools();
builder.Logging.AddDebug();
builder.Services.AddBlazorWebViewDeveloperTools();
builder.Logging.AddDebug();
#endif
// Infrastructure
builder.Services.AddSingleton<IPlatformService, MauiPlatformService>();
builder.Services.AddSingleton<INativeStorageService, MauiStorageService>();
// Identity
builder.Services.AddScoped<IIdentityService, IdentityService>();
builder.Services.AddScoped<NexusAuthenticationStateProvider>();
builder.Services.AddScoped<Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider>(sp =>
sp.GetRequiredService<NexusAuthenticationStateProvider>());
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddAuthorizationCore();
// Minimal Infrastructure
builder.Services.AddSingleton<IPlatformService, MauiPlatformService>();
builder.Services.AddSingleton<INativeStorageService, MauiStorageService>();
// Minimal Identity (Safe Mode)
builder.Services.AddScoped<NexusAuthenticationStateProvider>();
builder.Services.AddScoped<Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider>(sp =>
sp.GetRequiredService<NexusAuthenticationStateProvider>());
builder.Services.AddAuthorizationCore();
// Network
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("http://localhost:5000") }); // Update with real API URL later
// Basic Network
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("http://10.0.2.2:5000") });
// Shared UI State
builder.Services.AddScoped<IThemeService, ThemeService>();
builder.Services.AddScoped<IFocusModeService, FocusModeService>();
builder.Services.AddScoped<IQuizStateService, QuizStateService>();
// UI State
builder.Services.AddScoped<IThemeService, ThemeService>();
builder.Services.AddScoped<IFocusModeService, FocusModeService>();
builder.Services.AddScoped<IQuizStateService, QuizStateService>();
return builder.Build();
return builder.Build();
}
catch (Exception ex)
{
// This might help the debugger catch the exception more reliably
System.Diagnostics.Debug.WriteLine($"MAUI Startup Error: {ex}");
throw;
}
}
}
@@ -0,0 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.nexus.ereader">
<application android:allowBackup="true" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
@@ -0,0 +1,19 @@
using Android.App;
using Android.Content.PM;
using Android.OS;
using Android.Util;
namespace NexusReader.Maui;
[Activity(Theme = "@style/Maui.MainTheme.NoActionBar", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
private const string Tag = "NEXUS_MAUI";
protected override void OnCreate(Bundle? savedInstanceState)
{
Log.Debug(Tag, "MainActivity.OnCreate starting...");
base.OnCreate(savedInstanceState);
Log.Debug(Tag, "MainActivity.OnCreate completed");
}
}
@@ -0,0 +1,34 @@
using Android.App;
using Android.Runtime;
using Android.Util;
namespace NexusReader.Maui;
[Application]
public class MainApplication : MauiApplication
{
private const string Tag = "NEXUS_MAUI";
public MainApplication(IntPtr handle, JniHandleOwnership ownership)
: base(handle, ownership)
{
Log.Debug(Tag, "MainApplication constructor called");
}
protected override MauiApp CreateMauiApp()
{
Log.Debug(Tag, "CreateMauiApp starting...");
try
{
var app = MauiProgram.CreateMauiApp();
Log.Debug(Tag, "CreateMauiApp successful");
return app;
}
catch (Exception ex)
{
Log.Error(Tag, $"CreateMauiApp FAILED: {ex.Message}");
Log.Error(Tag, ex.StackTrace);
throw;
}
}
}
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#512BD4</color>
<color name="colorPrimaryDark">#2B0B98</color>
<color name="colorAccent">#DFD8F7</color>
</resources>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<style name="Maui.MainTheme.NoActionBar" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowContentOverlay">@null</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
@@ -6,7 +6,7 @@
<Style TargetType="Label">
<Setter Property="TextColor" Value="{StaticResource Gray100}" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<!-- <Setter Property="FontFamily" Value="OpenSansRegular" /> -->
<Setter Property="FontSize" Value="14" />
</Style>
@@ -0,0 +1,110 @@
using FluentResults;
using Microsoft.Maui.Storage;
using NexusReader.Application.Abstractions.Services;
namespace NexusReader.Maui.Services;
public class MauiStorageService : INativeStorageService
{
public Result SaveString(string key, string value)
{
try
{
Preferences.Default.Set(key, value);
return Result.Ok();
}
catch (Exception ex)
{
return Result.Fail(ex.Message);
}
}
public Result<string?> GetString(string key)
{
try
{
return Result.Ok(Preferences.Default.Get<string?>(key, null));
}
catch (Exception ex)
{
return Result.Fail(ex.Message);
}
}
public Result SaveBool(string key, bool value)
{
try
{
Preferences.Default.Set(key, value);
return Result.Ok();
}
catch (Exception ex)
{
return Result.Fail(ex.Message);
}
}
public Result<bool> GetBool(string key, bool defaultValue = false)
{
try
{
return Result.Ok(Preferences.Default.Get(key, defaultValue));
}
catch (Exception ex)
{
return Result.Fail(ex.Message);
}
}
public Result Remove(string key)
{
try
{
Preferences.Default.Remove(key);
return Result.Ok();
}
catch (Exception ex)
{
return Result.Fail(ex.Message);
}
}
public async Task<Result> SaveSecureString(string key, string value)
{
try
{
await SecureStorage.Default.SetAsync(key, value);
return Result.Ok();
}
catch (Exception ex)
{
return Result.Fail(ex.Message);
}
}
public async Task<Result<string?>> GetSecureString(string key)
{
try
{
var value = await SecureStorage.Default.GetAsync(key);
return Result.Ok(value);
}
catch (Exception ex)
{
return Result.Fail(ex.Message);
}
}
public Result RemoveSecure(string key)
{
try
{
SecureStorage.Default.Remove(key);
return Result.Ok();
}
catch (Exception ex)
{
return Result.Fail(ex.Message);
}
}
}