182 lines
8.6 KiB
C#
182 lines
8.6 KiB
C#
using LehrerApp.Core.Interfaces;
|
||
using LehrerApp.Core.Services;
|
||
using LehrerApp.Data;
|
||
using LehrerApp.Data.Repositories;
|
||
using LehrerApp.Desktop.ViewModels;
|
||
using LehrerApp.Desktop.ViewModels.Groups;
|
||
using LehrerApp.Desktop.ViewModels.Students;
|
||
using LehrerApp.Sync;
|
||
using LehrerApp.Sync.Crypto;
|
||
using LehrerApp.Sync.Models;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
|
||
namespace LehrerApp.Desktop;
|
||
|
||
public static class AppBootstrapper
|
||
{
|
||
public static IServiceProvider BuildServices()
|
||
{
|
||
var services = new ServiceCollection();
|
||
|
||
// ── Pfade ─────────────────────────────────────────────────────────────
|
||
var appData = Path.Combine(
|
||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||
"LehrerApp");
|
||
Directory.CreateDirectory(appData);
|
||
|
||
var dbPath = Path.Combine(appData, "lehrerapp.db");
|
||
var queuePath = Path.Combine(appData, "syncqueue.db");
|
||
var keyPath = Path.Combine(appData, "sync.key");
|
||
|
||
// ── Datenbank ─────────────────────────────────────────────────────────
|
||
services.AddSingleton(_ => new LiteDbContext(dbPath));
|
||
|
||
// ── Repositories ──────────────────────────────────────────────────────
|
||
services.AddSingleton<IStudentRepository, StudentRepository>();
|
||
services.AddSingleton<IGroupRepository, GroupRepository>();
|
||
services.AddSingleton<IEnrollmentRepository, EnrollmentRepository>();
|
||
services.AddSingleton<IExamRepository, ExamRepository>();
|
||
services.AddSingleton<IExamResultRepository, ExamResultRepository>();
|
||
services.AddSingleton<IGradeRepository, GradeRepository>();
|
||
services.AddSingleton<IUnitRepository, UnitRepository>();
|
||
services.AddSingleton<ILessonRepository, LessonRepository>();
|
||
services.AddSingleton<IDocumentationRepository, DocumentationRepository>();
|
||
services.AddSingleton<IWorkTaskRepository, WorkTaskRepository>();
|
||
services.AddSingleton<ITimeEntryRepository, TimeEntryRepository>();
|
||
|
||
// ── Core Services ─────────────────────────────────────────────────────
|
||
services.AddSingleton<GradingService>();
|
||
services.AddSingleton<SchoolYearService>();
|
||
|
||
// ── Sync ──────────────────────────────────────────────────────────────
|
||
services.AddSingleton(_ => new EventQueue(queuePath));
|
||
services.AddSingleton(sp =>
|
||
new ConflictResolver(sp.GetRequiredService<EventQueue>()));
|
||
|
||
// Schlüssel laden oder neu generieren
|
||
services.AddSingleton<byte[]>(_ =>
|
||
{
|
||
var key = SyncCrypto.LoadKey(keyPath) ?? SyncCrypto.GenerateKey();
|
||
SyncCrypto.SaveKey(key, keyPath);
|
||
return key;
|
||
});
|
||
|
||
// EventApplier – wendet gezogene Events auf LiteDB an
|
||
services.AddSingleton<EventApplier>(sp => new EventApplier(
|
||
key: sp.GetRequiredService<byte[]>(),
|
||
students: sp.GetRequiredService<IStudentRepository>(),
|
||
groups: sp.GetRequiredService<IGroupRepository>(),
|
||
enrollments: sp.GetRequiredService<IEnrollmentRepository>(),
|
||
exams: sp.GetRequiredService<IExamRepository>(),
|
||
examResults: sp.GetRequiredService<IExamResultRepository>(),
|
||
grades: sp.GetRequiredService<IGradeRepository>(),
|
||
units: sp.GetRequiredService<IUnitRepository>(),
|
||
lessons: sp.GetRequiredService<ILessonRepository>(),
|
||
docs: sp.GetRequiredService<IDocumentationRepository>(),
|
||
tasks: sp.GetRequiredService<IWorkTaskRepository>(),
|
||
timeEntries: sp.GetRequiredService<ITimeEntryRepository>()));
|
||
|
||
// SyncEngine + ReadableSnapshotService nur wenn Server konfiguriert
|
||
var serverUrl = LoadServerUrl(appData);
|
||
var deviceId = LoadOrCreateDeviceId(appData);
|
||
|
||
services.AddSingleton<SyncEngine?>(sp =>
|
||
{
|
||
if (string.IsNullOrEmpty(serverUrl)) return null;
|
||
|
||
var http = BuildHttpClient(serverUrl, appData);
|
||
var config = new SyncConfig
|
||
{
|
||
ServerUrl = serverUrl,
|
||
DeviceId = deviceId,
|
||
DeviceType = DeviceType.Desktop,
|
||
AutoSyncIntervalMinutes = 5,
|
||
};
|
||
return new SyncEngine(
|
||
sp.GetRequiredService<EventQueue>(),
|
||
sp.GetRequiredService<ConflictResolver>(),
|
||
http,
|
||
config);
|
||
});
|
||
|
||
services.AddSingleton<ReadableSnapshotService?>(sp =>
|
||
{
|
||
if (string.IsNullOrEmpty(serverUrl)) return null;
|
||
|
||
return new ReadableSnapshotService(
|
||
http: BuildHttpClient(serverUrl, appData),
|
||
students: sp.GetRequiredService<IStudentRepository>(),
|
||
groups: sp.GetRequiredService<IGroupRepository>(),
|
||
enrollments: sp.GetRequiredService<IEnrollmentRepository>(),
|
||
exams: sp.GetRequiredService<IExamRepository>(),
|
||
examResults: sp.GetRequiredService<IExamResultRepository>(),
|
||
grades: sp.GetRequiredService<IGradeRepository>(),
|
||
units: sp.GetRequiredService<IUnitRepository>(),
|
||
lessons: sp.GetRequiredService<ILessonRepository>(),
|
||
tasks: sp.GetRequiredService<IWorkTaskRepository>(),
|
||
schoolYear: sp.GetRequiredService<SchoolYearService>(),
|
||
deviceId: deviceId);
|
||
});
|
||
|
||
services.AddSingleton<SnapshotService?>(sp =>
|
||
{
|
||
if (string.IsNullOrEmpty(serverUrl)) return null;
|
||
|
||
return new SnapshotService(
|
||
http: BuildHttpClient(serverUrl, appData),
|
||
db: sp.GetRequiredService<LiteDbContext>(),
|
||
syncKey: sp.GetRequiredService<byte[]>(),
|
||
deviceType: DeviceType.Desktop,
|
||
dbPath: dbPath,
|
||
keyPath: keyPath);
|
||
});
|
||
|
||
// ── ViewModels ────────────────────────────────────────────────────────
|
||
services.AddSingleton<MainWindowViewModel>();
|
||
services.AddSingleton<DashboardViewModel>();
|
||
services.AddTransient<GroupListViewModel>();
|
||
services.AddTransient<GroupDetailViewModel>();
|
||
services.AddTransient<StudentListViewModel>();
|
||
services.AddTransient<StudentDetailViewModel>();
|
||
services.AddSingleton<SyncStatusViewModel>();
|
||
services.AddTransient<DevicePairingViewModel>(sp =>
|
||
new DevicePairingViewModel(
|
||
sp.GetRequiredService<SnapshotService?>()
|
||
?? throw new InvalidOperationException(
|
||
"Kein Server konfiguriert.")));
|
||
|
||
return services.BuildServiceProvider();
|
||
}
|
||
|
||
// ── Hilfsmethoden ─────────────────────────────────────────────────────────
|
||
|
||
private static HttpClient BuildHttpClient(string serverUrl, string appData)
|
||
{
|
||
var http = new HttpClient { BaseAddress = new Uri(serverUrl) };
|
||
var tokenPath = Path.Combine(appData, "auth.token");
|
||
if (File.Exists(tokenPath))
|
||
{
|
||
var token = File.ReadAllText(tokenPath).Trim();
|
||
http.DefaultRequestHeaders.Authorization =
|
||
new System.Net.Http.Headers.AuthenticationHeaderValue(
|
||
"Bearer", token);
|
||
}
|
||
return http;
|
||
}
|
||
|
||
private static string LoadServerUrl(string appData)
|
||
{
|
||
var path = Path.Combine(appData, "server.txt");
|
||
return File.Exists(path) ? File.ReadAllText(path).Trim() : "";
|
||
}
|
||
|
||
private static string LoadOrCreateDeviceId(string appData)
|
||
{
|
||
var path = Path.Combine(appData, "device.id");
|
||
if (File.Exists(path)) return File.ReadAllText(path).Trim();
|
||
var id = Guid.NewGuid().ToString();
|
||
File.WriteAllText(path, id);
|
||
return id;
|
||
}
|
||
}
|