using FlatRender.IdentitySvc.Domain.Entities; using FlatRender.IdentitySvc.Domain.Enums; using Microsoft.EntityFrameworkCore; namespace FlatRender.IdentitySvc.Infrastructure.Data; public class IdentityDbContext(DbContextOptions options) : DbContext(options) { // Tenants public DbSet Tenants => Set(); public DbSet TenantBrandings => Set(); public DbSet TenantApiKeys => Set(); public DbSet TenantWebhooks => Set(); public DbSet TenantWebhookDeliveries => Set(); public DbSet TenantUsageDailies => Set(); // Users public DbSet Users => Set(); public DbSet UserSessions => Set(); public DbSet ConfirmationTokens => Set(); public DbSet PushSubscriptions => Set(); public DbSet MfaFactors => Set(); // Billing public DbSet Plans => Set(); public DbSet UserPlans => Set(); public DbSet Payments => Set(); public DbSet Discounts => Set(); public DbSet UsedDiscounts => Set(); // CRM public DbSet UserCrms => Set(); // OAuth providers public DbSet OAuthConfigs => Set(); // Gamification public DbSet Quests => Set(); public DbSet UserQuestProgresses => Set(); public DbSet Gifts => Set(); public DbSet EarnedGifts => Set(); public DbSet Avatars => Set(); protected override void OnModelCreating(ModelBuilder mb) { mb.HasDefaultSchema("identity"); // Native PostgreSQL enums are registered on the EF provider via npgsql.MapEnum() // in Program.cs (the EF Core 9+ approach), which covers both the model and the // runtime ADO type mapping. No HasPostgresEnum() calls are needed here. ConfigureTenants(mb); ConfigureUsers(mb); ConfigureBilling(mb); ConfigureGamification(mb); mb.Entity(e => { e.ToTable("user_crm"); e.HasKey(x => x.UserId); }); mb.Entity(e => { e.ToTable("oauth_config"); e.HasKey(x => x.Provider); }); } private static void ConfigureTenants(ModelBuilder mb) { mb.Entity(e => { e.ToTable("tenants"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.Slug).HasColumnName("slug").HasColumnType("citext").IsRequired(); e.Property(x => x.Name).HasColumnName("name").IsRequired(); e.Property(x => x.Kind).HasColumnName("kind"); e.Property(x => x.Status).HasColumnName("status"); e.Property(x => x.CustomDomain).HasColumnName("custom_domain").HasColumnType("citext"); e.Property(x => x.DomainVerified).HasColumnName("domain_verified"); e.Property(x => x.AllowedOrigins).HasColumnName("allowed_origins"); e.Property(x => x.ContactName).HasColumnName("contact_name"); e.Property(x => x.ContactEmail).HasColumnName("contact_email").HasColumnType("citext"); e.Property(x => x.ContactPhone).HasColumnName("contact_phone"); e.Property(x => x.BillingEmail).HasColumnName("billing_email").HasColumnType("citext"); e.Property(x => x.MaxUsers).HasColumnName("max_users"); e.Property(x => x.MaxStorageGb).HasColumnName("max_storage_gb"); e.Property(x => x.MonthlyRenderQty).HasColumnName("monthly_render_qty"); e.Property(x => x.MonthlyRenderSec).HasColumnName("monthly_render_sec"); e.Property(x => x.TrialEndsAt).HasColumnName("trial_ends_at"); e.Property(x => x.SuspendedAt).HasColumnName("suspended_at"); e.Property(x => x.SuspensionReason).HasColumnName("suspension_reason"); e.Property(x => x.Metadata).HasColumnName("metadata").HasColumnType("jsonb"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.Property(x => x.UpdatedAt).HasColumnName("updated_at"); e.Property(x => x.DeletedAt).HasColumnName("deleted_at"); e.HasIndex(x => x.Slug).IsUnique(); e.HasOne(x => x.Branding).WithOne(b => b.Tenant).HasForeignKey(b => b.TenantId); e.HasMany(x => x.ApiKeys).WithOne(k => k.Tenant).HasForeignKey(k => k.TenantId); e.HasMany(x => x.Webhooks).WithOne(w => w.Tenant).HasForeignKey(w => w.TenantId); }); mb.Entity(e => { e.ToTable("tenant_branding"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.TenantId).HasColumnName("tenant_id"); e.Property(x => x.DisplayName).HasColumnName("display_name"); e.Property(x => x.LogoUrl).HasColumnName("logo_url"); e.Property(x => x.LogoDarkUrl).HasColumnName("logo_dark_url"); e.Property(x => x.FaviconUrl).HasColumnName("favicon_url"); e.Property(x => x.OgImageUrl).HasColumnName("og_image_url"); e.Property(x => x.PrimaryColor).HasColumnName("primary_color"); e.Property(x => x.SecondaryColor).HasColumnName("secondary_color"); e.Property(x => x.AccentColor).HasColumnName("accent_color"); e.Property(x => x.BackgroundColor).HasColumnName("background_color"); e.Property(x => x.FontFamily).HasColumnName("font_family"); e.Property(x => x.EmailFromName).HasColumnName("email_from_name"); e.Property(x => x.EmailFromAddress).HasColumnName("email_from_address"); e.Property(x => x.EmailReplyTo).HasColumnName("email_reply_to"); e.Property(x => x.EmailFooterHtml).HasColumnName("email_footer_html"); e.Property(x => x.SupportUrl).HasColumnName("support_url"); e.Property(x => x.TermsUrl).HasColumnName("terms_url"); e.Property(x => x.PrivacyUrl).HasColumnName("privacy_url"); e.Property(x => x.EmbedEnabled).HasColumnName("embed_enabled"); e.Property(x => x.EmbedAllowedHosts).HasColumnName("embed_allowed_hosts"); e.Property(x => x.WatermarkText).HasColumnName("watermark_text"); e.Property(x => x.WatermarkImageUrl).HasColumnName("watermark_image_url"); e.Property(x => x.WatermarkEnabled).HasColumnName("watermark_enabled"); e.Property(x => x.UpdatedAt).HasColumnName("updated_at"); }); mb.Entity(e => { e.ToTable("tenant_api_keys"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.TenantId).HasColumnName("tenant_id"); e.Property(x => x.CreatedByUserId).HasColumnName("created_by_user_id"); e.Property(x => x.Name).HasColumnName("name"); e.Property(x => x.Environment).HasColumnName("environment"); e.Property(x => x.KeyPrefix).HasColumnName("key_prefix"); e.Property(x => x.KeyHash).HasColumnName("key_hash"); e.Property(x => x.Last4).HasColumnName("last4"); e.Property(x => x.Scopes).HasColumnName("scopes"); e.Property(x => x.AllowedIps).HasColumnName("allowed_ips"); e.Property(x => x.RateLimitRpm).HasColumnName("rate_limit_rpm"); e.Property(x => x.IsActive).HasColumnName("is_active"); e.Property(x => x.ExpiresAt).HasColumnName("expires_at"); e.Property(x => x.LastUsedAt).HasColumnName("last_used_at"); e.Property(x => x.UsageCount).HasColumnName("usage_count"); e.Property(x => x.RevokeReason).HasColumnName("revoke_reason"); e.Property(x => x.RevokedAt).HasColumnName("revoked_at"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.Property(x => x.UpdatedAt).HasColumnName("updated_at"); }); mb.Entity(e => { e.ToTable("tenant_webhooks"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.TenantId).HasColumnName("tenant_id"); e.Property(x => x.Name).HasColumnName("name"); e.Property(x => x.Url).HasColumnName("url"); e.Property(x => x.Events).HasColumnName("events"); e.Property(x => x.SecretHash).HasColumnName("secret_hash"); e.Property(x => x.IsActive).HasColumnName("is_active"); e.Property(x => x.LastTriggeredAt).HasColumnName("last_triggered_at"); e.Property(x => x.LastStatusCode).HasColumnName("last_status_code"); e.Property(x => x.ConsecutiveFailures).HasColumnName("consecutive_failures"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.Property(x => x.UpdatedAt).HasColumnName("updated_at"); e.HasMany(x => x.Deliveries).WithOne(d => d.Webhook).HasForeignKey(d => d.WebhookId); }); mb.Entity(e => { e.ToTable("tenant_webhook_deliveries"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.WebhookId).HasColumnName("webhook_id"); e.Property(x => x.EventType).HasColumnName("event_type"); e.Property(x => x.RequestUrl).HasColumnName("request_url"); e.Property(x => x.RequestBody).HasColumnName("request_body"); e.Property(x => x.ResponseStatus).HasColumnName("response_status"); e.Property(x => x.ResponseBody).HasColumnName("response_body"); e.Property(x => x.DurationMs).HasColumnName("duration_ms"); e.Property(x => x.Attempt).HasColumnName("attempt"); e.Property(x => x.Succeeded).HasColumnName("succeeded"); e.Property(x => x.ErrorMessage).HasColumnName("error_message"); e.Property(x => x.DeliveredAt).HasColumnName("delivered_at"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); }); mb.Entity(e => { e.ToTable("tenant_usage_daily"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.TenantId).HasColumnName("tenant_id"); e.Property(x => x.UsageDate).HasColumnName("usage_date"); e.Property(x => x.RendersCompleted).HasColumnName("renders_completed"); e.Property(x => x.RenderSeconds).HasColumnName("render_seconds"); e.Property(x => x.StorageBytes).HasColumnName("storage_bytes"); e.Property(x => x.ApiCalls).HasColumnName("api_calls"); e.Property(x => x.ActiveUsers).HasColumnName("active_users"); e.Property(x => x.AmountBilledMinor).HasColumnName("amount_billed_minor"); e.Property(x => x.BillingCurrency).HasColumnName("billing_currency"); e.Property(x => x.BillingStatus).HasColumnName("billing_status"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.HasIndex(x => new { x.TenantId, x.UsageDate }).IsUnique(); }); } private static void ConfigureUsers(ModelBuilder mb) { mb.Entity(e => { e.ToTable("users"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.TenantId).HasColumnName("tenant_id"); e.Property(x => x.Email).HasColumnName("email").HasColumnType("citext"); e.Property(x => x.EmailVerified).HasColumnName("email_verified"); e.Property(x => x.EmailVerifiedAt).HasColumnName("email_verified_at"); e.Property(x => x.PhoneNumber).HasColumnName("phone_number"); e.Property(x => x.PhoneCountryCode).HasColumnName("phone_country_code"); e.Property(x => x.PhoneVerified).HasColumnName("phone_verified"); e.Property(x => x.PhoneVerifiedAt).HasColumnName("phone_verified_at"); e.Property(x => x.PasswordHash).HasColumnName("password_hash"); e.Property(x => x.PasswordSetAt).HasColumnName("password_set_at"); e.Property(x => x.LastPasswordResetDate).HasColumnName("last_password_reset_date"); e.Property(x => x.RegisterMode).HasColumnName("register_mode"); e.Property(x => x.ExternalProvider).HasColumnName("external_provider"); e.Property(x => x.ExternalProviderId).HasColumnName("external_provider_id"); e.Property(x => x.FullName).HasColumnName("full_name"); e.Property(x => x.AvatarUrl).HasColumnName("avatar_url"); e.Property(x => x.BirthDate).HasColumnName("birth_date"); e.Property(x => x.Gender).HasColumnName("gender"); e.Property(x => x.NationalCode).HasColumnName("national_code"); e.Property(x => x.CountryCode).HasColumnName("country_code"); e.Property(x => x.CompanyName).HasColumnName("company_name"); e.Property(x => x.WebsiteName).HasColumnName("website_name"); e.Property(x => x.Slogan).HasColumnName("slogan"); e.Property(x => x.AboutMe).HasColumnName("about_me"); e.Property(x => x.MethodOfIntroduction).HasColumnName("method_of_introduction"); e.Property(x => x.BalanceMinor).HasColumnName("balance_minor"); e.Property(x => x.AffiliateBalanceMinor).HasColumnName("affiliate_balance_minor"); e.Property(x => x.AffiliateOwnerId).HasColumnName("affiliate_owner_id"); e.Property(x => x.ProfitPercentage).HasColumnName("profit_percentage"); e.Property(x => x.LoyaltyScore).HasColumnName("loyalty_score"); e.Property(x => x.PurplePoint).HasColumnName("purple_point"); e.Property(x => x.DailyRemainRenderCount).HasColumnName("daily_remain_render_count"); e.Property(x => x.MaxDailyRenderCount).HasColumnName("max_daily_render_count"); e.Property(x => x.ParallelRenderingCeiling).HasColumnName("parallel_rendering_ceiling"); e.Property(x => x.UserDailyFreeChargeSec).HasColumnName("user_daily_free_charge_sec"); e.Property(x => x.DailyFreeChargeResetDate).HasColumnName("daily_free_charge_reset_date"); e.Property(x => x.MaxPreviewDurationSec).HasColumnName("max_preview_duration_sec"); e.Property(x => x.ForceRenderQueue).HasColumnName("force_render_queue"); e.Property(x => x.RemoveWatermarkService).HasColumnName("remove_watermark_service"); e.Property(x => x.TelegramId).HasColumnName("telegram_id"); e.Property(x => x.TelegramTellMe).HasColumnName("telegram_tell_me"); e.Property(x => x.EmailTellMe).HasColumnName("email_tell_me"); e.Property(x => x.SmsTellMe).HasColumnName("sms_tell_me"); e.Property(x => x.PushTellMe).HasColumnName("push_tell_me"); e.Property(x => x.StorageEndpoint).HasColumnName("storage_endpoint"); e.Property(x => x.UsedStorageBytes).HasColumnName("used_storage_bytes"); e.Property(x => x.IsAdmin).HasColumnName("is_admin"); e.Property(x => x.IsTenantAdmin).HasColumnName("is_tenant_admin"); e.Property(x => x.BanAccount).HasColumnName("ban_account"); e.Property(x => x.BanReason).HasColumnName("ban_reason"); e.Property(x => x.UnblockDate).HasColumnName("unblock_date"); e.Property(x => x.LastActiveDate).HasColumnName("last_active_date"); e.Property(x => x.LastLoginAt).HasColumnName("last_login_at"); e.Property(x => x.LastLoginIp).HasColumnName("last_login_ip"); e.Property(x => x.RegisteredWithMobileApp).HasColumnName("registered_with_mobile_app"); e.Property(x => x.RegisterDate).HasColumnName("register_date"); e.Property(x => x.Metadata).HasColumnName("metadata").HasColumnType("jsonb"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.Property(x => x.UpdatedAt).HasColumnName("updated_at"); e.Property(x => x.DeletedAt).HasColumnName("deleted_at"); e.HasIndex(x => new { x.TenantId, x.Email }).IsUnique(); e.HasMany(x => x.Sessions).WithOne(s => s.User).HasForeignKey(s => s.UserId).OnDelete(DeleteBehavior.Cascade); e.HasMany(x => x.MfaFactors).WithOne(m => m.User).HasForeignKey(m => m.UserId).OnDelete(DeleteBehavior.Cascade); e.HasMany(x => x.PushSubscriptions).WithOne(p => p.User).HasForeignKey(p => p.UserId).OnDelete(DeleteBehavior.Cascade); }); mb.Entity(e => { e.ToTable("user_sessions"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.UserId).HasColumnName("user_id"); e.Property(x => x.TenantId).HasColumnName("tenant_id"); e.Property(x => x.RefreshTokenHash).HasColumnName("refresh_token_hash"); e.Property(x => x.DeviceId).HasColumnName("device_id"); e.Property(x => x.DeviceName).HasColumnName("device_name"); e.Property(x => x.UserAgent).HasColumnName("user_agent"); e.Property(x => x.IpAddress).HasColumnName("ip_address"); e.Property(x => x.IssuedAt).HasColumnName("issued_at"); e.Property(x => x.ExpiresAt).HasColumnName("expires_at"); e.Property(x => x.RevokedAt).HasColumnName("revoked_at"); e.Property(x => x.LastUsedAt).HasColumnName("last_used_at"); e.HasIndex(x => x.RefreshTokenHash).IsUnique(); }); mb.Entity(e => { e.ToTable("confirmation_tokens"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.UserId).HasColumnName("user_id"); e.Property(x => x.TenantId).HasColumnName("tenant_id"); e.Property(x => x.Purpose).HasColumnName("purpose"); e.Property(x => x.Identifier).HasColumnName("identifier").HasColumnType("citext"); e.Property(x => x.NextIdentifier).HasColumnName("next_identifier").HasColumnType("citext"); e.Property(x => x.TokenHash).HasColumnName("token_hash"); e.Property(x => x.Code).HasColumnName("code"); e.Property(x => x.IsConsumed).HasColumnName("is_consumed"); e.Property(x => x.ConsumedAt).HasColumnName("consumed_at"); e.Property(x => x.TryCount).HasColumnName("try_count"); e.Property(x => x.MaxTries).HasColumnName("max_tries"); e.Property(x => x.RequestIp).HasColumnName("request_ip"); e.Property(x => x.ExpiresAt).HasColumnName("expires_at"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); }); mb.Entity(e => { e.ToTable("push_subscriptions"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.UserId).HasColumnName("user_id"); e.Property(x => x.TenantId).HasColumnName("tenant_id"); e.Property(x => x.Endpoint).HasColumnName("endpoint"); e.Property(x => x.P256dhKey).HasColumnName("p256dh_key"); e.Property(x => x.AuthKey).HasColumnName("auth_key"); e.Property(x => x.UserAgent).HasColumnName("user_agent"); e.Property(x => x.IsActive).HasColumnName("is_active"); e.Property(x => x.LastUsedAt).HasColumnName("last_used_at"); e.Property(x => x.FailureCount).HasColumnName("failure_count"); e.Property(x => x.LastFailureAt).HasColumnName("last_failure_at"); e.Property(x => x.LastFailureStatus).HasColumnName("last_failure_status"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.HasIndex(x => new { x.UserId, x.Endpoint }).IsUnique(); }); mb.Entity(e => { e.ToTable("mfa_factors"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.UserId).HasColumnName("user_id"); e.Property(x => x.FactorType).HasColumnName("factor_type"); e.Property(x => x.SecretEncrypted).HasColumnName("secret_encrypted"); e.Property(x => x.IsVerified).HasColumnName("is_verified"); e.Property(x => x.IsPrimary).HasColumnName("is_primary"); e.Property(x => x.Label).HasColumnName("label"); e.Property(x => x.LastUsedAt).HasColumnName("last_used_at"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); }); } private static void ConfigureBilling(ModelBuilder mb) { mb.Entity(e => { e.ToTable("plans"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.TenantId).HasColumnName("tenant_id"); e.Property(x => x.Scope).HasColumnName("scope"); e.Property(x => x.Code).HasColumnName("code"); e.Property(x => x.Name).HasColumnName("name"); e.Property(x => x.Description).HasColumnName("description"); e.Property(x => x.PriceMinor).HasColumnName("price_minor"); e.Property(x => x.BeforePriceMinor).HasColumnName("before_price_minor"); e.Property(x => x.Currency).HasColumnName("currency"); e.Property(x => x.DiscountPercentage).HasColumnName("discount_percentage"); e.Property(x => x.BillingPeriod).HasColumnName("billing_period"); e.Property(x => x.MonthsDuration).HasColumnName("months_duration"); e.Property(x => x.SecondsCharge).HasColumnName("seconds_charge"); e.Property(x => x.MonthlyRendersQuota).HasColumnName("monthly_renders_quota"); e.Property(x => x.StorageGb).HasColumnName("storage_gb"); e.Property(x => x.ParallelRenders).HasColumnName("parallel_renders"); e.Property(x => x.MaxResolution).HasColumnName("max_resolution"); e.Property(x => x.MinVideoLengthSec).HasColumnName("min_video_length_sec"); e.Property(x => x.RenderSpeedFactor).HasColumnName("render_speed_factor"); e.Property(x => x.Sort).HasColumnName("sort"); e.Property(x => x.Icon).HasColumnName("icon"); e.Property(x => x.Cover).HasColumnName("cover"); e.Property(x => x.Color).HasColumnName("color"); e.Property(x => x.IsFeatured).HasColumnName("is_featured"); e.Property(x => x.Features).HasColumnName("features").HasColumnType("jsonb"); e.Property(x => x.IsActive).HasColumnName("is_active"); e.Property(x => x.AvailableFrom).HasColumnName("available_from"); e.Property(x => x.AvailableUntil).HasColumnName("available_until"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.Property(x => x.UpdatedAt).HasColumnName("updated_at"); e.Property(x => x.DeletedAt).HasColumnName("deleted_at"); }); mb.Entity(e => { e.ToTable("user_plans"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.UserId).HasColumnName("user_id"); e.Property(x => x.TenantId).HasColumnName("tenant_id"); e.Property(x => x.PlanId).HasColumnName("plan_id"); e.Property(x => x.PlanCode).HasColumnName("plan_code"); e.Property(x => x.PlanName).HasColumnName("plan_name"); e.Property(x => x.PriceMinorPaid).HasColumnName("price_minor_paid"); e.Property(x => x.Currency).HasColumnName("currency"); e.Property(x => x.InitialSecondsCharge).HasColumnName("initial_seconds_charge"); e.Property(x => x.RemainChargeSec).HasColumnName("remain_charge_sec"); e.Property(x => x.AddedChargeFromPastPlan).HasColumnName("added_charge_from_past_plan"); e.Property(x => x.MonthlyRendersUsed).HasColumnName("monthly_renders_used"); e.Property(x => x.MonthlyRendersResetAt).HasColumnName("monthly_renders_reset_at"); e.Property(x => x.RegisterDate).HasColumnName("register_date"); e.Property(x => x.StartsAt).HasColumnName("starts_at"); e.Property(x => x.ExpiresAt).HasColumnName("expires_at"); e.Property(x => x.CancelledAt).HasColumnName("cancelled_at"); e.Property(x => x.CancelReason).HasColumnName("cancel_reason"); e.Property(x => x.AutoRenew).HasColumnName("auto_renew"); e.Property(x => x.PaymentId).HasColumnName("payment_id"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.Property(x => x.UpdatedAt).HasColumnName("updated_at"); e.HasOne(x => x.User).WithMany(u => u.Plans).HasForeignKey(x => x.UserId); e.HasOne(x => x.Plan).WithMany(p => p.UserPlans).HasForeignKey(x => x.PlanId); }); mb.Entity(e => { e.ToTable("payments"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.TenantId).HasColumnName("tenant_id"); e.Property(x => x.UserId).HasColumnName("user_id"); e.Property(x => x.Gateway).HasColumnName("gateway"); e.Property(x => x.Status).HasColumnName("status"); e.Property(x => x.Action).HasColumnName("action"); e.Property(x => x.AmountMinor).HasColumnName("amount_minor"); e.Property(x => x.Currency).HasColumnName("currency"); e.Property(x => x.BalanceReducerMinor).HasColumnName("balance_reducer_minor"); e.Property(x => x.DiscountValueMinor).HasColumnName("discount_value_minor"); e.Property(x => x.GatewayToken).HasColumnName("gateway_token"); e.Property(x => x.GatewayOrderId).HasColumnName("gateway_order_id"); e.Property(x => x.GatewayTrackId).HasColumnName("gateway_track_id"); e.Property(x => x.GatewayResponse).HasColumnName("gateway_response").HasColumnType("jsonb"); e.Property(x => x.CardLast4).HasColumnName("card_last4"); e.Property(x => x.CardHash).HasColumnName("card_hash"); e.Property(x => x.Title).HasColumnName("title"); e.Property(x => x.Description).HasColumnName("description"); e.Property(x => x.OwnerUserId).HasColumnName("owner_user_id"); e.Property(x => x.AffiliateProfitMinor).HasColumnName("affiliate_profit_minor"); e.Property(x => x.UsedDiscountId).HasColumnName("used_discount_id"); e.Property(x => x.PlanId).HasColumnName("plan_id"); e.Property(x => x.RenderJobId).HasColumnName("render_job_id"); e.Property(x => x.UserProjectId).HasColumnName("user_project_id"); e.Property(x => x.ConfirmedAt).HasColumnName("confirmed_at"); e.Property(x => x.FailedAt).HasColumnName("failed_at"); e.Property(x => x.FailureReason).HasColumnName("failure_reason"); e.Property(x => x.RefundedAt).HasColumnName("refunded_at"); e.Property(x => x.RefundAmountMinor).HasColumnName("refund_amount_minor"); e.Property(x => x.RefundReason).HasColumnName("refund_reason"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.Property(x => x.UpdatedAt).HasColumnName("updated_at"); e.HasOne(x => x.User).WithMany(u => u.Payments).HasForeignKey(x => x.UserId); }); mb.Entity(e => { e.ToTable("discounts"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.TenantId).HasColumnName("tenant_id"); e.Property(x => x.Name).HasColumnName("name"); e.Property(x => x.Code).HasColumnName("code").HasColumnType("citext"); e.Property(x => x.Kind).HasColumnName("kind"); e.Property(x => x.Value).HasColumnName("value"); e.Property(x => x.OwnerUserId).HasColumnName("owner_user_id"); e.Property(x => x.OwnerProfitPercentage).HasColumnName("owner_profit_percentage"); e.Property(x => x.OnlyOwner).HasColumnName("only_owner"); e.Property(x => x.MaxUseCount).HasColumnName("max_use_count"); e.Property(x => x.UsedCount).HasColumnName("used_count"); e.Property(x => x.MinPurchaseMinor).HasColumnName("min_purchase_minor"); e.Property(x => x.AppliesToPlanIds).HasColumnName("applies_to_plan_ids"); e.Property(x => x.StartsAt).HasColumnName("starts_at"); e.Property(x => x.ExpiresAt).HasColumnName("expires_at"); e.Property(x => x.IsActive).HasColumnName("is_active"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.Property(x => x.UpdatedAt).HasColumnName("updated_at"); e.HasIndex(x => new { x.TenantId, x.Code }).IsUnique(); }); mb.Entity(e => { e.ToTable("used_discounts"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.DiscountId).HasColumnName("discount_id"); e.Property(x => x.UserId).HasColumnName("user_id"); e.Property(x => x.PaymentId).HasColumnName("payment_id"); e.Property(x => x.Code).HasColumnName("code").HasColumnType("citext"); e.Property(x => x.AmountDiscountedMinor).HasColumnName("amount_discounted_minor"); e.Property(x => x.UseDate).HasColumnName("use_date"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); }); } private static void ConfigureGamification(ModelBuilder mb) { mb.Entity(e => { e.ToTable("quests"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.TenantId).HasColumnName("tenant_id"); e.Property(x => x.Title).HasColumnName("title"); e.Property(x => x.Challenge).HasColumnName("challenge"); e.Property(x => x.Why).HasColumnName("why"); e.Property(x => x.Hint).HasColumnName("hint"); e.Property(x => x.Aphorism).HasColumnName("aphorism"); e.Property(x => x.Icon).HasColumnName("icon"); e.Property(x => x.QuestType).HasColumnName("quest_type"); e.Property(x => x.TargetEvent).HasColumnName("target_event"); e.Property(x => x.TargetCount).HasColumnName("target_count"); e.Property(x => x.Metadata).HasColumnName("metadata").HasColumnType("jsonb"); e.Property(x => x.PrizeType).HasColumnName("prize_type"); e.Property(x => x.PrizeAmount).HasColumnName("prize_amount"); e.Property(x => x.LevelLimit).HasColumnName("level_limit"); e.Property(x => x.StartUrl).HasColumnName("start_url"); e.Property(x => x.PostActionName).HasColumnName("post_action_name"); e.Property(x => x.OrderValue).HasColumnName("order_value"); e.Property(x => x.StartsAt).HasColumnName("starts_at"); e.Property(x => x.ExpiresAt).HasColumnName("expires_at"); e.Property(x => x.IsActive).HasColumnName("is_active"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.Property(x => x.UpdatedAt).HasColumnName("updated_at"); e.HasMany(x => x.Progress).WithOne(p => p.Quest).HasForeignKey(p => p.QuestId); }); mb.Entity(e => { e.ToTable("user_quest_progress"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.UserId).HasColumnName("user_id"); e.Property(x => x.QuestId).HasColumnName("quest_id"); e.Property(x => x.CurrentCount).HasColumnName("current_count"); e.Property(x => x.TextValue).HasColumnName("text_value"); e.Property(x => x.IsCompleted).HasColumnName("is_completed"); e.Property(x => x.CompletedAt).HasColumnName("completed_at"); e.Property(x => x.PrizeClaimed).HasColumnName("prize_claimed"); e.Property(x => x.PrizeClaimedAt).HasColumnName("prize_claimed_at"); e.Property(x => x.PeriodStart).HasColumnName("period_start"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.Property(x => x.UpdatedAt).HasColumnName("updated_at"); e.HasIndex(x => new { x.UserId, x.QuestId, x.PeriodStart }).IsUnique(); }); mb.Entity(e => { e.ToTable("gifts"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.TenantId).HasColumnName("tenant_id"); e.Property(x => x.Name).HasColumnName("name"); e.Property(x => x.Description).HasColumnName("description"); e.Property(x => x.Icon).HasColumnName("icon"); e.Property(x => x.GiftType).HasColumnName("gift_type"); e.Property(x => x.PrizeType).HasColumnName("prize_type"); e.Property(x => x.Value).HasColumnName("value"); e.Property(x => x.Unit).HasColumnName("unit"); e.Property(x => x.AssignedByUserId).HasColumnName("assigned_by_user_id"); e.Property(x => x.IsActive).HasColumnName("is_active"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.HasMany(x => x.EarnedGifts).WithOne(eg => eg.Gift).HasForeignKey(eg => eg.GiftId); }); mb.Entity(e => { e.ToTable("earned_gifts"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.UserId).HasColumnName("user_id"); e.Property(x => x.GiftId).HasColumnName("gift_id"); e.Property(x => x.NotificationId).HasColumnName("notification_id"); e.Property(x => x.Source).HasColumnName("source"); e.Property(x => x.SourceRef).HasColumnName("source_ref"); e.Property(x => x.EarnedAt).HasColumnName("earned_at"); e.Property(x => x.ExpiresAt).HasColumnName("expires_at"); e.Property(x => x.IsUsed).HasColumnName("is_used"); e.Property(x => x.UsedAt).HasColumnName("used_at"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); }); mb.Entity(e => { e.ToTable("avatars"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.Code).HasColumnName("code"); e.Property(x => x.Url).HasColumnName("url"); e.Property(x => x.Description).HasColumnName("description"); e.Property(x => x.Sort).HasColumnName("sort"); e.Property(x => x.IsActive).HasColumnName("is_active"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.HasIndex(x => x.Code).IsUnique(); }); } }