feat: V2 microservices stack — backend services, gateway, JWT auth
Add full V2 architecture: identity, content, studio (.NET 10) and file, render, notification, gateway (Go) services with vendored deps, plus DB migrations, event/API contracts, and an init-db script. Wire the Next.js frontend to the gateway: server-side JWT auth routes (login/register/refresh/logout/me), gateway fetch helper, and session/ cookie/jwt helpers under src/lib. Containerize the stack via docker-compose.v2.yml and per-service Dockerfiles. Base images resolve through a Nexus mirror (Docker Hub) and MCR directly; npm/NuGet pull from Nexus groups. Self-host fonts via next/font/local to avoid Google Fonts (geo-blocked). Add CI workflow and ignore .env.v2, *.stackdump, and .NET bin/obj. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,599 @@
|
||||
using FlatRender.IdentitySvc.Domain.Entities;
|
||||
using FlatRender.IdentitySvc.Domain.Enums;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace FlatRender.IdentitySvc.Infrastructure.Data;
|
||||
|
||||
public class IdentityDbContext(DbContextOptions<IdentityDbContext> options) : DbContext(options)
|
||||
{
|
||||
// Tenants
|
||||
public DbSet<Tenant> Tenants => Set<Tenant>();
|
||||
public DbSet<TenantBranding> TenantBrandings => Set<TenantBranding>();
|
||||
public DbSet<TenantApiKey> TenantApiKeys => Set<TenantApiKey>();
|
||||
public DbSet<TenantWebhook> TenantWebhooks => Set<TenantWebhook>();
|
||||
public DbSet<TenantWebhookDelivery> TenantWebhookDeliveries => Set<TenantWebhookDelivery>();
|
||||
public DbSet<TenantUsageDaily> TenantUsageDailies => Set<TenantUsageDaily>();
|
||||
|
||||
// Users
|
||||
public DbSet<User> Users => Set<User>();
|
||||
public DbSet<UserSession> UserSessions => Set<UserSession>();
|
||||
public DbSet<ConfirmationToken> ConfirmationTokens => Set<ConfirmationToken>();
|
||||
public DbSet<PushSubscription> PushSubscriptions => Set<PushSubscription>();
|
||||
public DbSet<MfaFactor> MfaFactors => Set<MfaFactor>();
|
||||
|
||||
// Billing
|
||||
public DbSet<Plan> Plans => Set<Plan>();
|
||||
public DbSet<UserPlan> UserPlans => Set<UserPlan>();
|
||||
public DbSet<Payment> Payments => Set<Payment>();
|
||||
public DbSet<Discount> Discounts => Set<Discount>();
|
||||
public DbSet<UsedDiscount> UsedDiscounts => Set<UsedDiscount>();
|
||||
|
||||
// Gamification
|
||||
public DbSet<Quest> Quests => Set<Quest>();
|
||||
public DbSet<UserQuestProgress> UserQuestProgresses => Set<UserQuestProgress>();
|
||||
public DbSet<Gift> Gifts => Set<Gift>();
|
||||
public DbSet<EarnedGift> EarnedGifts => Set<EarnedGift>();
|
||||
public DbSet<Avatar> Avatars => Set<Avatar>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder mb)
|
||||
{
|
||||
mb.HasDefaultSchema("identity");
|
||||
|
||||
// Native PostgreSQL enums are registered on the EF provider via npgsql.MapEnum<T>()
|
||||
// in Program.cs (the EF Core 9+ approach), which covers both the model and the
|
||||
// runtime ADO type mapping. No HasPostgresEnum<T>() calls are needed here.
|
||||
|
||||
ConfigureTenants(mb);
|
||||
ConfigureUsers(mb);
|
||||
ConfigureBilling(mb);
|
||||
ConfigureGamification(mb);
|
||||
}
|
||||
|
||||
private static void ConfigureTenants(ModelBuilder mb)
|
||||
{
|
||||
mb.Entity<Tenant>(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<TenantBranding>(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<TenantBranding>(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<TenantApiKey>(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<TenantWebhook>(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<TenantWebhookDelivery>(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<TenantUsageDaily>(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<User>(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<UserSession>(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<ConfirmationToken>(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<PushSubscription>(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<MfaFactor>(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<Plan>(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<UserPlan>(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<Payment>(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<Discount>(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<UsedDiscount>(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<Quest>(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<UserQuestProgress>(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<Gift>(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<EarnedGift>(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<Avatar>(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();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user