using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Nexus.Api.Data; public class NexusUser { public Guid Id { get; set; } = Guid.NewGuid(); [MaxLength(120)] public string Email { get; set; } = string.Empty; [MaxLength(120)] public string NormalizedEmail { get; set; } = string.Empty; [MaxLength(100)] public string DisplayName { get; set; } = string.Empty; public string PasswordHash { get; set; } = string.Empty; public string Role { get; set; } = "owner"; public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; public DateTimeOffset UpdatedAt { get; set; } = DateTimeOffset.UtcNow; public DateTimeOffset? LastLoginAt { get; set; } public ICollection RefreshTokens { get; set; } = new List(); } /// /// Tracks one-time seed operations so they are never re-executed — even /// if the underlying data is deleted. This is the single guard that /// prevents owner-password drift after DB resets or volume recreations. /// public class SeedAudit { [Key] [MaxLength(80)] public string Key { get; set; } = string.Empty; public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; } public class RefreshToken { public Guid Id { get; set; } = Guid.NewGuid(); public Guid UserId { get; set; } [MaxLength(64)] public string TokenHash { get; set; } = string.Empty; public Guid FamilyId { get; set; } = Guid.NewGuid(); public DateTimeOffset ExpiresAt { get; set; } public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; public DateTimeOffset? RevokedAt { get; set; } [MaxLength(64)] public string? ReplacedByTokenHash { get; set; } public Guid ConcurrencyStamp { get; set; } = Guid.NewGuid(); public NexusUser User { get; set; } = null!; }