namespace Flaro.Core.Users; /// Ein Benutzerkonto. /// /// /// Mehrbenutzer von Anfang an — auch wenn der erste Server nur einen hat. /// Fortschritt und Gesehen-Status je Benutzer nachträglich einzuziehen wäre /// ein Umbau am Datenmodell und an jeder Abfrage darauf, kein Zusatz. /// public sealed class User { /// Zeitlich geordnet, damit der Index nicht zerfasert. public Guid Id { get; init; } = Guid.CreateVersion7(); public required string UserName { get; set; } /// Im PHC-Format, siehe . public required string PasswordHash { get; set; } public bool IsAdministrator { get; set; } /// Dem Besitzer kann niemand das Adminrecht nehmen. /// /// /// /// Damit sich ein Server nicht selbst aussperren kann. Sobald /// Administratoren einander das Recht entziehen können, gibt es einen /// Zustand ohne jeden Administrator — und aus dem führt kein Weg zurück /// ausser dem an der Datenbank. Ein Konto, das ausgenommen ist, schliesst /// diesen Zustand aus. /// /// /// Es ist das erste Konto, also das, mit dem der Server /// eingerichtet wurde. Übertragen lässt es sich — an einen anderen /// Administrator, und nur vom Besitzer selbst. /// /// public bool IsOwner { get; set; } /// Die bevorzugte Sprache — null heisst: die des Servers. /// /// /// Und sie ist nicht kostenlos. Metadaten werden in der Sprache des /// Servers geholt und gespeichert. Wer eine andere wählt, bekommt, was in /// der Übersetzungstabelle liegt — und für den Rest die Serversprache. /// Darauf wird beim Umstellen deutlich hingewiesen; es stillschweigend /// halb zu übersetzen wäre schlechter als gar nicht. /// public string? Language { get; set; } /// Das eigene Bild — der Schlüssel aus derselben Ablage wie Plakate. /// /// /// Kein eigener Speicher für Profilbilder. Es ist ein Bild wie /// jedes andere: derselbe Ordner, dieselbe Adresse, derselbe /// Zwischenspeicher im Browser. Ein zweiter Weg hiesse ein zweites Mal /// Pfadprüfung, Grössengrenze und Aufräumen — dreimal dieselbe Arbeit für /// ein Bild von 200 Pixeln. /// /// null heisst: der Buchstabenkreis. Der ist keine Notlösung, /// sondern eine gültige Antwort — niemand muss ein Bild haben. /// public string? AvatarImage { get; set; } public DateTimeOffset CreatedAt { get; init; } = DateTimeOffset.UtcNow; }