1f52f53cf7
Build backend images / build content-svc (push) Failing after 51s
Build backend images / build file-svc (push) Failing after 53s
Build backend images / build gateway (push) Failing after 1m1s
Build backend images / build identity-svc (push) Failing after 48s
Build backend images / build notification-svc (push) Failing after 42s
Build backend images / build render-svc (push) Failing after 47s
Build backend images / build studio-svc (push) Failing after 1m13s
Business rule: each user has a daily render limit. Admin-stop refunds the used
charge (not the user's fault); a user's own cancel does not.
- identity: ConsumeRenderChargeAsync / RefundRenderChargeAsync on DailyRemainRenderCount
with lazy daily reset (mig 24: daily_renders_reset_at). Convention: max=0 ⇒ UNLIMITED,
so existing 0/0 users keep rendering until an admin sets a real limit.
- identity InternalController (service-token): POST /v1/internal/render-charge/{consume,refund}
- render-svc: identityclient + on Create consume (block 429 when limit reached, fail-open
on identity outage); on admin Stop refund the job owner; user /cancel unchanged
- compose: IDENTITY_URL for render-svc, ServiceToken for identity-svc
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
174 lines
6.1 KiB
C#
174 lines
6.1 KiB
C#
using FlatRender.IdentitySvc.Domain.Enums;
|
|
|
|
namespace FlatRender.IdentitySvc.Domain.Entities;
|
|
|
|
public class User
|
|
{
|
|
public Guid Id { get; set; } = Guid.NewGuid();
|
|
public Guid TenantId { get; set; }
|
|
public Tenant Tenant { get; set; } = default!;
|
|
|
|
// Auth
|
|
public string? Email { get; set; }
|
|
public bool EmailVerified { get; set; }
|
|
public DateTime? EmailVerifiedAt { get; set; }
|
|
public string? PhoneNumber { get; set; }
|
|
public string? PhoneCountryCode { get; set; }
|
|
public bool PhoneVerified { get; set; }
|
|
public DateTime? PhoneVerifiedAt { get; set; }
|
|
public string? PasswordHash { get; set; }
|
|
public DateTime? PasswordSetAt { get; set; }
|
|
public DateTime? LastPasswordResetDate { get; set; }
|
|
public RegisterMode RegisterMode { get; set; } = RegisterMode.Email;
|
|
public string? ExternalProvider { get; set; }
|
|
public string? ExternalProviderId { get; set; }
|
|
|
|
// Profile
|
|
public string? FullName { get; set; }
|
|
public string? AvatarUrl { get; set; }
|
|
public DateOnly? BirthDate { get; set; }
|
|
public GenderKind? Gender { get; set; }
|
|
public string? NationalCode { get; set; }
|
|
public string? CountryCode { get; set; }
|
|
public string? CompanyName { get; set; }
|
|
public string? WebsiteName { get; set; }
|
|
public string? Slogan { get; set; }
|
|
public string? AboutMe { get; set; }
|
|
public string? MethodOfIntroduction { get; set; }
|
|
|
|
// Balances
|
|
public long BalanceMinor { get; set; }
|
|
public long AffiliateBalanceMinor { get; set; }
|
|
public Guid? AffiliateOwnerId { get; set; }
|
|
public decimal ProfitPercentage { get; set; }
|
|
|
|
// Gamification
|
|
public int LoyaltyScore { get; set; }
|
|
public int PurplePoint { get; set; }
|
|
|
|
// Render quotas
|
|
public int DailyRemainRenderCount { get; set; }
|
|
public int MaxDailyRenderCount { get; set; }
|
|
public DateTime? DailyRendersResetAt { get; set; }
|
|
public int ParallelRenderingCeiling { get; set; } = 1;
|
|
public int UserDailyFreeChargeSec { get; set; }
|
|
public DateTime? DailyFreeChargeResetDate { get; set; }
|
|
public int MaxPreviewDurationSec { get; set; } = 30;
|
|
public bool ForceRenderQueue { get; set; }
|
|
public bool RemoveWatermarkService { get; set; }
|
|
|
|
// Telegram
|
|
public string? TelegramId { get; set; }
|
|
public bool TelegramTellMe { get; set; }
|
|
|
|
// Comms prefs
|
|
public bool EmailTellMe { get; set; } = true;
|
|
public bool SmsTellMe { get; set; }
|
|
public bool PushTellMe { get; set; } = true;
|
|
|
|
// Storage
|
|
public string? StorageEndpoint { get; set; }
|
|
public long UsedStorageBytes { get; set; }
|
|
|
|
// Status
|
|
public bool IsAdmin { get; set; }
|
|
public bool IsTenantAdmin { get; set; }
|
|
public bool BanAccount { get; set; }
|
|
public string? BanReason { get; set; }
|
|
public DateTime? UnblockDate { get; set; }
|
|
|
|
// Activity
|
|
public DateTime? LastActiveDate { get; set; }
|
|
public DateTime? LastLoginAt { get; set; }
|
|
public string? LastLoginIp { get; set; }
|
|
public bool RegisteredWithMobileApp { get; set; }
|
|
public DateTime RegisterDate { get; set; } = DateTime.UtcNow;
|
|
|
|
public string Metadata { get; set; } = "{}";
|
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
|
public DateTime? DeletedAt { get; set; }
|
|
|
|
public ICollection<UserSession> Sessions { get; set; } = [];
|
|
public ICollection<MfaFactor> MfaFactors { get; set; } = [];
|
|
public ICollection<PushSubscription> PushSubscriptions { get; set; } = [];
|
|
public ICollection<UserPlan> Plans { get; set; } = [];
|
|
public ICollection<Payment> Payments { get; set; } = [];
|
|
}
|
|
|
|
public class UserSession
|
|
{
|
|
public Guid Id { get; set; } = Guid.NewGuid();
|
|
public Guid UserId { get; set; }
|
|
public User User { get; set; } = default!;
|
|
public Guid TenantId { get; set; }
|
|
|
|
public string RefreshTokenHash { get; set; } = default!;
|
|
public string? DeviceId { get; set; }
|
|
public string? DeviceName { get; set; }
|
|
public string? UserAgent { get; set; }
|
|
public string? IpAddress { get; set; }
|
|
|
|
public DateTime IssuedAt { get; set; } = DateTime.UtcNow;
|
|
public DateTime ExpiresAt { get; set; }
|
|
public DateTime? RevokedAt { get; set; }
|
|
public DateTime? LastUsedAt { get; set; }
|
|
}
|
|
|
|
public class ConfirmationToken
|
|
{
|
|
public Guid Id { get; set; } = Guid.NewGuid();
|
|
public Guid? UserId { get; set; }
|
|
public Guid TenantId { get; set; }
|
|
public TokenPurpose Purpose { get; set; }
|
|
|
|
public string Identifier { get; set; } = default!;
|
|
public string? NextIdentifier { get; set; }
|
|
public string TokenHash { get; set; } = default!;
|
|
public string? Code { get; set; }
|
|
|
|
public bool IsConsumed { get; set; }
|
|
public DateTime? ConsumedAt { get; set; }
|
|
public int TryCount { get; set; }
|
|
public int MaxTries { get; set; } = 5;
|
|
|
|
public string? RequestIp { get; set; }
|
|
public DateTime ExpiresAt { get; set; }
|
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
}
|
|
|
|
public class PushSubscription
|
|
{
|
|
public Guid Id { get; set; } = Guid.NewGuid();
|
|
public Guid UserId { get; set; }
|
|
public User User { get; set; } = default!;
|
|
public Guid TenantId { get; set; }
|
|
|
|
public string Endpoint { get; set; } = default!;
|
|
public string P256dhKey { get; set; } = default!;
|
|
public string AuthKey { get; set; } = default!;
|
|
public string? UserAgent { get; set; }
|
|
|
|
public bool IsActive { get; set; } = true;
|
|
public DateTime? LastUsedAt { get; set; }
|
|
public int FailureCount { get; set; }
|
|
public DateTime? LastFailureAt { get; set; }
|
|
public int? LastFailureStatus { get; set; }
|
|
|
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
}
|
|
|
|
public class MfaFactor
|
|
{
|
|
public Guid Id { get; set; } = Guid.NewGuid();
|
|
public Guid UserId { get; set; }
|
|
public User User { get; set; } = default!;
|
|
public MfaFactorType FactorType { get; set; }
|
|
public string? SecretEncrypted { get; set; }
|
|
public bool IsVerified { get; set; }
|
|
public bool IsPrimary { get; set; }
|
|
public string? Label { get; set; }
|
|
public DateTime? LastUsedAt { get; set; }
|
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
}
|