diff --git a/src/Yavsc.Org.Tests/NonRegression/SetActivityCountryValidationViewTests.cs b/src/Yavsc.Org.Tests/NonRegression/SetActivityCountryValidationViewTests.cs
new file mode 100644
index 00000000..23ace458
--- /dev/null
+++ b/src/Yavsc.Org.Tests/NonRegression/SetActivityCountryValidationViewTests.cs
@@ -0,0 +1,70 @@
+namespace Yavsc.Org.Tests.NonRegression;
+
+///
+/// Guard rails for the SetActivity performer settings page:
+/// - countries are provided to the ComboBox via ViewBag.Countries
+/// - client-side SIREN validation is wired to country-specific regex rules
+/// - controller exposes the validation catalog to the view
+///
+public class SetActivityCountryValidationViewTests
+{
+ [Fact]
+ public void SetActivity_cshtml_binds_country_combo_to_ViewBag_Countries()
+ {
+ var content = File.ReadAllText(ResolveSetActivityViewPath());
+
+ Assert.Contains("asp-for=\"ExerciseCountryCode\"", content);
+ Assert.Contains("asp-items=\"ViewBag.Countries\"", content);
+ }
+
+ [Fact]
+ public void SetActivity_cshtml_contains_country_aware_siren_javascript_validation()
+ {
+ var content = File.ReadAllText(ResolveSetActivityViewPath());
+
+ Assert.Contains("$.validator.addMethod(\"sirenByCountry\"", content);
+ Assert.Contains("new RegExp(selectedRule.regex)", content);
+ Assert.Contains("const countryInput = $(\"#ExerciseCountryCode\")", content);
+ Assert.Contains("const sirenInput = $(\"#SIREN\")", content);
+ }
+
+ [Fact]
+ public void ManageController_exposes_countries_and_country_validation_rules_to_view()
+ {
+ var content = File.ReadAllText(ResolveManageControllerPath());
+
+ Assert.Contains("ViewBag.Countries = countries;", content);
+ Assert.Contains("ViewBag.CountryCodeValidationRules = PerformerCodeInputValidationCatalog.Rules;", content);
+ Assert.Contains("ModelState.Remove(nameof(PerformerProfile.ExerciseCountryCode));", content);
+ }
+
+ private static string ResolveSetActivityViewPath()
+ {
+ return ResolveFromWorkspaceRoot(
+ "src", "Yavsc.Org", "Views", "Manage", "SetActivity.cshtml");
+ }
+
+ private static string ResolveManageControllerPath()
+ {
+ return ResolveFromWorkspaceRoot(
+ "src", "Yavsc.Org", "Controllers", "Accounting", "ManageController.cs");
+ }
+
+ private static string ResolveFromWorkspaceRoot(params string[] relative)
+ {
+ var dir = AppContext.BaseDirectory;
+ for (var i = 0; i < 10 && dir is not null; i++)
+ {
+ var candidate = Path.Combine(new[] { dir }.Concat(relative).ToArray());
+ if (File.Exists(candidate))
+ {
+ return candidate;
+ }
+
+ dir = Path.GetDirectoryName(dir);
+ }
+
+ throw new FileNotFoundException(
+ "Could not locate test target from " + AppContext.BaseDirectory);
+ }
+}
diff --git a/src/Yavsc.Org/Controllers/Accounting/ManageController.cs b/src/Yavsc.Org/Controllers/Accounting/ManageController.cs
index 2c8cbec9..6326726d 100644
--- a/src/Yavsc.Org/Controllers/Accounting/ManageController.cs
+++ b/src/Yavsc.Org/Controllers/Accounting/ManageController.cs
@@ -568,7 +568,17 @@ namespace Yavsc.Controllers
{
var user = GetCurrentUserAsync().Result;
var uid = user.Id;
+ var postedCountryCode = model.ExerciseCountryCode;
model.ExerciseCountryCode = NormalizeCountryCodeOrDefault(model.ExerciseCountryCode);
+
+ // Model binding validated the raw posted payload before entering
+ // the action. If country was empty, we fallback to "fr" above,
+ // so remove the stale min-length error attached to the empty value.
+ if (string.IsNullOrWhiteSpace(postedCountryCode))
+ {
+ ModelState.Remove(nameof(PerformerProfile.ExerciseCountryCode));
+ }
+
try
{
if (ModelState.IsValid)
@@ -636,7 +646,7 @@ namespace Yavsc.Controllers
return View(model);
}
- private static string NormalizeCountryCodeOrDefault(string? code)
+ private static string NormalizeCountryCodeOrDefault(string code)
{
var normalized = PerformerCodeInputValidationCatalog.NormalizeCountryCode(code);
if (string.IsNullOrWhiteSpace(normalized))
@@ -647,14 +657,17 @@ namespace Yavsc.Controllers
return normalized;
}
- private void SetExerciseCountries(string? selectedCountryCode)
+ private void SetExerciseCountries(string selectedCountryCode)
{
var selected = NormalizeCountryCodeOrDefault(selectedCountryCode);
- ViewBag.ExerciseCountries = new SelectList(
+ var countries = new SelectList(
PerformerCodeInputValidationCatalog.Countries,
nameof(Country.Code),
nameof(Country.DisplayName),
selected);
+ ViewBag.ExerciseCountries = countries;
+ ViewBag.Countries = countries;
+ ViewBag.CountryCodeValidationRules = PerformerCodeInputValidationCatalog.Rules;
}
[HttpPost]
diff --git a/src/Yavsc.Org/Migrations/20260831040104_AddPerformerCountryValidation.Designer.cs b/src/Yavsc.Org/Migrations/20260831040104_AddPerformerCountryValidation.Designer.cs
new file mode 100644
index 00000000..3348b9c4
--- /dev/null
+++ b/src/Yavsc.Org/Migrations/20260831040104_AddPerformerCountryValidation.Designer.cs
@@ -0,0 +1,4734 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using Yavsc.Models;
+
+#nullable disable
+
+namespace Yavsc.Migrations
+{
+ [DbContext(typeof(ApplicationDbContext))]
+ [Migration("20260831040104_AddPerformerCountryValidation")]
+ partial class AddPerformerCountryValidation
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "10.0.9")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResource", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id"));
+
+ b.Property("AllowedAccessTokenSigningAlgorithms")
+ .HasColumnType("text");
+
+ b.Property("Created")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("DisplayName")
+ .HasColumnType("text");
+
+ b.Property("Enabled")
+ .HasColumnType("boolean");
+
+ b.Property("LastAccessed")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .HasColumnType("text");
+
+ b.Property("NonEditable")
+ .HasColumnType("boolean");
+
+ b.Property("ShowInDiscoveryDocument")
+ .HasColumnType("boolean");
+
+ b.Property("Updated")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("ApiResources");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ApiResourceId")
+ .HasColumnType("integer");
+
+ b.Property("ApiResourceId1")
+ .HasColumnType("integer");
+
+ b.Property("Type")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ApiResourceId");
+
+ b.HasIndex("ApiResourceId1");
+
+ b.ToTable("ApiResourceClaims");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceProperty", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ApiResourceId")
+ .HasColumnType("integer");
+
+ b.Property("ApiResourceId1")
+ .HasColumnType("integer");
+
+ b.Property("Key")
+ .HasColumnType("text");
+
+ b.Property("Value")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ApiResourceId");
+
+ b.HasIndex("ApiResourceId1");
+
+ b.ToTable("ApiResourceProperties");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceScope", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ApiResourceId")
+ .HasColumnType("integer");
+
+ b.Property("ApiResourceId1")
+ .HasColumnType("integer");
+
+ b.Property("Scope")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ApiResourceId");
+
+ b.HasIndex("ApiResourceId1");
+
+ b.ToTable("ApiResourceScopes");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceSecret", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ApiResourceId")
+ .HasColumnType("integer");
+
+ b.Property("ApiResourceId1")
+ .HasColumnType("integer");
+
+ b.Property("Created")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("Expiration")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Type")
+ .HasColumnType("text");
+
+ b.Property("Value")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ApiResourceId");
+
+ b.HasIndex("ApiResourceId1");
+
+ b.ToTable("ApiResourceSecrets");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScope", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id"));
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("DisplayName")
+ .HasColumnType("text");
+
+ b.Property("Emphasize")
+ .HasColumnType("boolean");
+
+ b.Property("Enabled")
+ .HasColumnType("boolean");
+
+ b.Property("Name")
+ .HasColumnType("text");
+
+ b.Property("Required")
+ .HasColumnType("boolean");
+
+ b.Property("ShowInDiscoveryDocument")
+ .HasColumnType("boolean");
+
+ b.HasKey("Id");
+
+ b.ToTable("ApiScopes");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ScopeId")
+ .HasColumnType("integer");
+
+ b.Property("ScopeId1")
+ .HasColumnType("integer");
+
+ b.Property("Type")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ScopeId");
+
+ b.HasIndex("ScopeId1");
+
+ b.ToTable("ApiScopeClaims");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeProperty", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Key")
+ .HasColumnType("text");
+
+ b.Property("ScopeId")
+ .HasColumnType("integer");
+
+ b.Property("ScopeId1")
+ .HasColumnType("integer");
+
+ b.Property("Value")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ScopeId");
+
+ b.HasIndex("ScopeId1");
+
+ b.ToTable("ApiScopeProperties");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.Client", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id"));
+
+ b.Property("AbsoluteRefreshTokenLifetime")
+ .HasColumnType("integer");
+
+ b.Property("AccessTokenLifetime")
+ .HasColumnType("integer");
+
+ b.Property("AccessTokenType")
+ .HasColumnType("integer");
+
+ b.Property("AllowAccessTokensViaBrowser")
+ .HasColumnType("boolean");
+
+ b.Property("AllowOfflineAccess")
+ .HasColumnType("boolean");
+
+ b.Property("AllowPlainTextPkce")
+ .HasColumnType("boolean");
+
+ b.Property("AllowRememberConsent")
+ .HasColumnType("boolean");
+
+ b.Property("AllowedIdentityTokenSigningAlgorithms")
+ .HasColumnType("text");
+
+ b.Property("AlwaysIncludeUserClaimsInIdToken")
+ .HasColumnType("boolean");
+
+ b.Property("AlwaysSendClientClaims")
+ .HasColumnType("boolean");
+
+ b.Property("AuthorizationCodeLifetime")
+ .HasColumnType("integer");
+
+ b.Property("BackChannelLogoutSessionRequired")
+ .HasColumnType("boolean");
+
+ b.Property("BackChannelLogoutUri")
+ .HasColumnType("text");
+
+ b.Property("ClientClaimsPrefix")
+ .HasColumnType("text");
+
+ b.Property("ClientId")
+ .HasColumnType("text");
+
+ b.Property("ClientName")
+ .HasColumnType("text");
+
+ b.Property("ClientUri")
+ .HasColumnType("text");
+
+ b.Property("ConsentLifetime")
+ .HasColumnType("integer");
+
+ b.Property("Created")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("DeviceCodeLifetime")
+ .HasColumnType("integer");
+
+ b.Property("EnableLocalLogin")
+ .HasColumnType("boolean");
+
+ b.Property("Enabled")
+ .HasColumnType("boolean");
+
+ b.Property("FrontChannelLogoutSessionRequired")
+ .HasColumnType("boolean");
+
+ b.Property("FrontChannelLogoutUri")
+ .HasColumnType("text");
+
+ b.Property("IdentityTokenLifetime")
+ .HasColumnType("integer");
+
+ b.Property("IncludeJwtId")
+ .HasColumnType("boolean");
+
+ b.Property("LastAccessed")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("LogoUri")
+ .HasColumnType("text");
+
+ b.Property("NonEditable")
+ .HasColumnType("boolean");
+
+ b.Property("PairWiseSubjectSalt")
+ .HasColumnType("text");
+
+ b.Property("ProtocolType")
+ .HasColumnType("text");
+
+ b.Property("RefreshTokenExpiration")
+ .HasColumnType("integer");
+
+ b.Property("RefreshTokenUsage")
+ .HasColumnType("integer");
+
+ b.Property("RequireClientSecret")
+ .HasColumnType("boolean");
+
+ b.Property("RequireConsent")
+ .HasColumnType("boolean");
+
+ b.Property("RequirePkce")
+ .HasColumnType("boolean");
+
+ b.Property("RequireRequestObject")
+ .HasColumnType("boolean");
+
+ b.Property("SlidingRefreshTokenLifetime")
+ .HasColumnType("integer");
+
+ b.Property("UpdateAccessTokenClaimsOnRefresh")
+ .HasColumnType("boolean");
+
+ b.Property("Updated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UserCodeType")
+ .HasColumnType("text");
+
+ b.Property("UserSsoLifetime")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("Clients");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id"));
+
+ b.Property("ClientId")
+ .HasColumnType("integer");
+
+ b.Property("Type")
+ .HasColumnType("text");
+
+ b.Property("Value")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ClientId");
+
+ b.ToTable("ClientClaims");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientCorsOrigin", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id"));
+
+ b.Property("ClientId")
+ .HasColumnType("integer");
+
+ b.Property("ClientId1")
+ .HasColumnType("integer");
+
+ b.Property("Origin")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ClientId");
+
+ b.HasIndex("ClientId1");
+
+ b.ToTable("ClientCorsOrigins");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientGrantType", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id"));
+
+ b.Property("ClientId")
+ .HasColumnType("integer");
+
+ b.Property("GrantType")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ClientId");
+
+ b.ToTable("ClientGrantTypes");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientIdPRestriction", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id"));
+
+ b.Property("ClientId")
+ .HasColumnType("integer");
+
+ b.Property("ClientId1")
+ .HasColumnType("integer");
+
+ b.Property("Provider")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ClientId");
+
+ b.HasIndex("ClientId1");
+
+ b.ToTable("ClientIdPRestrictions");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientPostLogoutRedirectUri", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id"));
+
+ b.Property("ClientId")
+ .HasColumnType("integer");
+
+ b.Property("ClientId1")
+ .HasColumnType("integer");
+
+ b.Property("PostLogoutRedirectUri")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ClientId");
+
+ b.HasIndex("ClientId1");
+
+ b.ToTable("ClientPostLogoutRedirectUris");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientProperty", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id"));
+
+ b.Property("ClientId")
+ .HasColumnType("integer");
+
+ b.Property("ClientId1")
+ .HasColumnType("integer");
+
+ b.Property("Key")
+ .HasColumnType("text");
+
+ b.Property("Value")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ClientId");
+
+ b.HasIndex("ClientId1");
+
+ b.ToTable("ClientProperties");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientRedirectUri", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id"));
+
+ b.Property("ClientId")
+ .HasColumnType("integer");
+
+ b.Property("RedirectUri")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ClientId");
+
+ b.ToTable("ClientRedirectUris");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientScope", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id"));
+
+ b.Property("ClientId")
+ .HasColumnType("integer");
+
+ b.Property("Scope")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ClientId");
+
+ b.ToTable("ClientScopes");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientSecret", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id"));
+
+ b.Property("ClientId")
+ .HasColumnType("integer");
+
+ b.Property("ClientId1")
+ .HasColumnType("integer");
+
+ b.Property("Created")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("Expiration")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Type")
+ .HasColumnType("text");
+
+ b.Property("Value")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ClientId");
+
+ b.HasIndex("ClientId1");
+
+ b.ToTable("ClientSecrets");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.DeviceFlowCodes", b =>
+ {
+ b.Property("UserCode")
+ .HasColumnType("text");
+
+ b.Property("DeviceCode")
+ .HasColumnType("text");
+
+ b.Property("ClientId")
+ .HasColumnType("text");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Data")
+ .HasColumnType("text");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("Expiration")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("SessionId")
+ .HasColumnType("text");
+
+ b.Property("SubjectId")
+ .HasColumnType("text");
+
+ b.HasKey("UserCode", "DeviceCode");
+
+ b.ToTable("DeviceFlowCodes");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResource", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Created")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("DisplayName")
+ .HasColumnType("text");
+
+ b.Property("Emphasize")
+ .HasColumnType("boolean");
+
+ b.Property("Enabled")
+ .HasColumnType("boolean");
+
+ b.Property("Name")
+ .HasColumnType("text");
+
+ b.Property("NonEditable")
+ .HasColumnType("boolean");
+
+ b.Property("Required")
+ .HasColumnType("boolean");
+
+ b.Property("ShowInDiscoveryDocument")
+ .HasColumnType("boolean");
+
+ b.Property("Updated")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("IdentityResources");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("IdentityResourceId")
+ .HasColumnType("integer");
+
+ b.Property("Type")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("IdentityResourceId");
+
+ b.ToTable("IdentityResourceClaims");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceProperty", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("IdentityResourceId")
+ .HasColumnType("integer");
+
+ b.Property("Key")
+ .HasColumnType("text");
+
+ b.Property("Value")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("IdentityResourceId");
+
+ b.ToTable("IdentityResourceProperties");
+ });
+
+ modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.PersistedGrant", b =>
+ {
+ b.Property("Key")
+ .HasColumnType("text");
+
+ b.Property("ClientId")
+ .HasColumnType("text");
+
+ b.Property("ConsumedTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Data")
+ .HasColumnType("text");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("Expiration")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("SessionId")
+ .HasColumnType("text");
+
+ b.Property("SubjectId")
+ .HasColumnType("text");
+
+ b.Property("Type")
+ .HasColumnType("text");
+
+ b.HasKey("Key");
+
+ b.ToTable("PersistedGrants");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("text");
+
+ b.Property("Name")
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("NormalizedName")
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedName")
+ .IsUnique()
+ .HasDatabaseName("RoleNameIndex");
+
+ b.ToTable("AspNetRoles", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ClaimType")
+ .HasColumnType("text");
+
+ b.Property("ClaimValue")
+ .HasColumnType("text");
+
+ b.Property("RoleId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetRoleClaims", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ClaimType")
+ .HasColumnType("text");
+
+ b.Property("ClaimValue")
+ .HasColumnType("text");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserClaims", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.Property("LoginProvider")
+ .HasColumnType("text");
+
+ b.Property("ProviderKey")
+ .HasColumnType("text");
+
+ b.Property("ProviderDisplayName")
+ .HasColumnType("text");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("LoginProvider", "ProviderKey");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserLogins", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("text");
+
+ b.Property("RoleId")
+ .HasColumnType("text");
+
+ b.HasKey("UserId", "RoleId");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetUserRoles", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("text");
+
+ b.Property("LoginProvider")
+ .HasColumnType("text");
+
+ b.Property("Name")
+ .HasColumnType("text");
+
+ b.Property("Value")
+ .HasColumnType("text");
+
+ b.HasKey("UserId", "LoginProvider", "Name");
+
+ b.ToTable("AspNetUserTokens", (string)null);
+ });
+
+ modelBuilder.Entity("Yavsc.Abstract.Identity.ClientProviderInfo", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("text");
+
+ b.Property("Avatar")
+ .HasColumnType("text");
+
+ b.Property("BillingAddressId")
+ .HasColumnType("bigint");
+
+ b.Property("EMail")
+ .HasColumnType("text");
+
+ b.Property("Phone")
+ .HasColumnType("text");
+
+ b.Property("UserName")
+ .HasColumnType("text");
+
+ b.HasKey("UserId");
+
+ b.ToTable("ClientProviderInfo");
+ });
+
+ modelBuilder.Entity("Yavsc.Abstract.Models.Messaging.Notification", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Target")
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("body")
+ .IsRequired()
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("click_action")
+ .IsRequired()
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("color")
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("icon")
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)")
+ .HasDefaultValue("exclam");
+
+ b.Property("sound")
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("tag")
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("title")
+ .IsRequired()
+ .HasMaxLength(1024)
+ .HasColumnType("character varying(1024)");
+
+ b.HasKey("Id");
+
+ b.ToTable("Notification");
+ });
+
+ modelBuilder.Entity("Yavsc.Models.Access.Ban", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateModified")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Reason")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("TargetId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("UserCreated")
+ .HasColumnType("text");
+
+ b.Property("UserModified")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TargetId");
+
+ b.ToTable("Ban");
+ });
+
+ modelBuilder.Entity("Yavsc.Models.Access.BlackListed", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("OwnerId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("OwnerId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("BlackListed");
+ });
+
+ modelBuilder.Entity("Yavsc.Models.Access.CircleAuthorizationToBlogPost", b =>
+ {
+ b.Property("CircleId")
+ .HasColumnType("bigint");
+
+ b.Property("BlogPostId")
+ .HasColumnType("bigint");
+
+ b.HasKey("CircleId", "BlogPostId");
+
+ b.HasIndex("BlogPostId");
+
+ b.ToTable("CircleAuthorizationToBlogPost");
+ });
+
+ modelBuilder.Entity("Yavsc.Models.AccountBalance", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("text");
+
+ b.Property("ContactCredits")
+ .HasColumnType("bigint");
+
+ b.Property("Credits")
+ .HasColumnType("numeric");
+
+ b.HasKey("UserId");
+
+ b.ToTable("BankStatus");
+ });
+
+ modelBuilder.Entity("Yavsc.Models.ApplicationUser", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("AccessFailedCount")
+ .HasColumnType("integer");
+
+ b.Property("AllowMonthlyEmail")
+ .HasColumnType("boolean");
+
+ b.Property